Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

imx_nfc.c

/*
 * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>

#define DRIVER_VERSION "1.0"

/* Define this macro to enable event reporting. */

#define EVENT_REPORTING

/*
 * For detailed information that will be helpful in understanding this driver,
 * see:
 *
 *     Documentation/imx_nfc.txt
 */

/*
 * Macros that describe NFC hardware have names of the form:
 *
 *     NFC_*
 *
 * Macros that apply only to specific versions of the NFC have names of the
 * following form:
 *
 *     NFC_<M>_<N>_*
 *
 * where:
 *
 *     <M> is the major version of the NFC hardware.
 *     <N> is the minor version of the NFC hardware.
 *
 * The minor version can be 'X', which means that the macro applies to *all*
 * NFCs of the same major version.
 *
 * For NFC versions with only one set of registers, macros that give offsets
 * against the base address have names of the form:
 *
 *     *<RegisterName>_REG_OFF
 *
 * Macros that give the position of a field's LSB within a given register have
 * names of the form:
 *
 *     *<RegisterName>_<FieldName>_POS
 *
 * Macros that mask a field within a given register have names of the form:
 *
 *     *<RegisterName>_<FieldName>_MSK
 */

/*
 * Macro definitions for ALL NFC versions.
 */

#define NFC_MAIN_BUF_SIZE  (512)

/*
 * Macro definitions for version 1.0 NFCs.
 */

#define NFC_1_0_BUF_SIZE_REG_OFF             (0x00)
#define NFC_1_0_BUF_ADDR_REG_OFF             (0x04)
#define NFC_1_0_FLASH_ADDR_REG_OFF           (0x06)
#define NFC_1_0_FLASH_CMD_REG_OFF            (0x08)
#define NFC_1_0_CONFIG_REG_OFF               (0x0A)
#define NFC_1_0_ECC_STATUS_RESULT_REG_OFF    (0x0C)
#define NFC_1_0_RSLTMAIN_AREA_REG_OFF        (0x0E)
#define NFC_1_0_RSLTSPARE_AREA_REG_OFF       (0x10)
#define NFC_1_0_WRPROT_REG_OFF               (0x12)
#define NFC_1_0_UNLOCKSTART_BLKADDR_REG_OFF  (0x14)
#define NFC_1_0_UNLOCKEND_BLKADDR_REG_OFF    (0x16)
#define NFC_1_0_NF_WRPRST_REG_OFF            (0x18)

#define NFC_1_0_CONFIG1_REG_OFF              (0x1A)
#define NFC_1_0_CONFIG1_NF_CE_POS            (7)
#define NFC_1_0_CONFIG1_NF_CE_MSK            (0x1 << 7)

#define NFC_1_0_CONFIG2_REG_OFF              (0x1C)

/*
* Macro definitions for version 2.X NFCs.
*/

#define NFC_2_X_FLASH_ADDR_REG_OFF   (0x06)
#define NFC_2_X_FLASH_CMD_REG_OFF    (0x08)
#define NFC_2_X_CONFIG_REG_OFF       (0x0A)

#define NFC_2_X_WR_PROT_REG_OFF      (0x12)

#define NFC_2_X_NF_WR_PR_ST_REG_OFF  (0x18)

#define NFC_2_X_CONFIG2_REG_OFF      (0x1C)
#define NFC_2_X_CONFIG2_FCMD_POS     (0)
#define NFC_2_X_CONFIG2_FCMD_MSK     (0x1 << 0)
#define NFC_2_X_CONFIG2_FADD_POS     (1)
#define NFC_2_X_CONFIG2_FADD_MSK     (0x1 << 1)
#define NFC_2_X_CONFIG2_FDI_POS      (2)
#define NFC_2_X_CONFIG2_FDI_MSK      (0x1 << 2)
#define NFC_2_X_CONFIG2_FDO_POS      (3)
#define NFC_2_X_CONFIG2_FDO_MSK      (0x7 << 3)
#define NFC_2_X_CONFIG2_INT_POS      (15)
#define NFC_2_X_CONFIG2_INT_MSK      (0x1 << 15)

/*
* Macro definitions for version 2.0 NFCs.
*/

#define NFC_2_0_BUF_ADDR_REG_OFF       (0x04)
#define NFC_2_0_BUF_ADDR_RBA_POS       (0)
#define NFC_2_0_BUF_ADDR_RBA_MSK       (0xf << 0)

#define NFC_2_0_ECC_STATUS_REG_OFF     (0x0C)
#define NFC_2_0_ECC_STATUS_NOSER1_POS  (0)
#define NFC_2_0_ECC_STATUS_NOSER1_MSK  (0xF << 0)
#define NFC_2_0_ECC_STATUS_NOSER2_POS  (4)
#define NFC_2_0_ECC_STATUS_NOSER2_MSK  (0xF << 4)
#define NFC_2_0_ECC_STATUS_NOSER3_POS  (8)
#define NFC_2_0_ECC_STATUS_NOSER3_MSK  (0xF << 8)
#define NFC_2_0_ECC_STATUS_NOSER4_POS  (12)
#define NFC_2_0_ECC_STATUS_NOSER4_MSK  (0xF << 12)

#define NFC_2_0_CONFIG1_REG_OFF        (0x1A)
#define NFC_2_0_CONFIG1_SP_EN_POS      (2)
#define NFC_2_0_CONFIG1_SP_EN_MSK      (0x1 << 2)
#define NFC_2_0_CONFIG1_ECC_EN_POS     (3)
#define NFC_2_0_CONFIG1_ECC_EN_MSK     (0x1 << 3)
#define NFC_2_0_CONFIG1_INT_MSK_POS    (4)
#define NFC_2_0_CONFIG1_INT_MSK_MSK    (0x1 << 4)
#define NFC_2_0_CONFIG1_NF_BIG_POS     (5)
#define NFC_2_0_CONFIG1_NF_BIG_MSK     (0x1 << 5)
#define NFC_2_0_CONFIG1_NFC_RST_POS    (6)
#define NFC_2_0_CONFIG1_NFC_RST_MSK    (0x1 << 6)
#define NFC_2_0_CONFIG1_NF_CE_POS      (7)
#define NFC_2_0_CONFIG1_NF_CE_MSK      (0x1 << 7)
#define NFC_2_0_CONFIG1_ONE_CYLE_POS   (8)
#define NFC_2_0_CONFIG1_ONE_CYLE_MSK   (0x1 << 8)
#define NFC_2_0_CONFIG1_MLC_POS        (9)
#define NFC_2_0_CONFIG1_MLC_MSK        (0x1 << 9)

#define NFC_2_0_UNLOCK_START_REG_OFF   (0x14)
#define NFC_2_0_UNLOCK_END_REG_OFF     (0x16)

/*
* Macro definitions for version 2.1 NFCs.
*/

#define NFC_2_1_BUF_ADDR_REG_OFF        (0x04)
#define NFC_2_1_BUF_ADDR_RBA_POS        (0)
#define NFC_2_1_BUF_ADDR_RBA_MSK        (0x7 << 0)
#define NFC_2_1_BUF_ADDR_CS_POS         (4)
#define NFC_2_1_BUF_ADDR_CS_MSK         (0x3 << 4)

#define NFC_2_1_ECC_STATUS_REG_OFF      (0x0C)
#define NFC_2_1_ECC_STATUS_NOSER1_POS   (0)
#define NFC_2_1_ECC_STATUS_NOSER1_MSK   (0xF << 0)
#define NFC_2_1_ECC_STATUS_NOSER2_POS   (4)
#define NFC_2_1_ECC_STATUS_NOSER2_MSK   (0xF << 4)
#define NFC_2_1_ECC_STATUS_NOSER3_POS   (8)
#define NFC_2_1_ECC_STATUS_NOSER3_MSK   (0xF << 8)
#define NFC_2_1_ECC_STATUS_NOSER4_POS   (12)
#define NFC_2_1_ECC_STATUS_NOSER4_MSK   (0xF << 12)

#define NFC_2_1_CONFIG1_REG_OFF         (0x1A)
#define NFC_2_1_CONFIG1_ECC_MODE_POS    (0)
#define NFC_2_1_CONFIG1_ECC_MODE_MSK    (0x1 << 0)
#define NFC_2_1_CONFIG1_DMA_MODE_POS    (1)
#define NFC_2_1_CONFIG1_DMA_MODE_MSK    (0x1 << 1)
#define NFC_2_1_CONFIG1_SP_EN_POS       (2)
#define NFC_2_1_CONFIG1_SP_EN_MSK       (0x1 << 2)
#define NFC_2_1_CONFIG1_ECC_EN_POS      (3)
#define NFC_2_1_CONFIG1_ECC_EN_MSK      (0x1 << 3)
#define NFC_2_1_CONFIG1_INT_MSK_POS     (4)
#define NFC_2_1_CONFIG1_INT_MSK_MSK     (0x1 << 4)
#define NFC_2_1_CONFIG1_NF_BIG_POS      (5)
#define NFC_2_1_CONFIG1_NF_BIG_MSK      (0x1 << 5)
#define NFC_2_1_CONFIG1_NFC_RST_POS     (6)
#define NFC_2_1_CONFIG1_NFC_RST_MSK     (0x1 << 6)
#define NFC_2_1_CONFIG1_NF_CE_POS       (7)
#define NFC_2_1_CONFIG1_NF_CE_MSK       (0x1 << 7)
#define NFC_2_1_CONFIG1_SYM_POS         (8)
#define NFC_2_1_CONFIG1_SYM_MSK         (0x1 << 8)
#define NFC_2_1_CONFIG1_PPB_POS         (9)
#define NFC_2_1_CONFIG1_PPB_MSK         (0x3 << 9)
#define NFC_2_1_CONFIG1_FP_INT_POS      (11)
#define NFC_2_1_CONFIG1_FP_INT_MSK      (0x1 << 11)

#define NFC_2_1_UNLOCK_START_0_REG_OFF  (0x20)
#define NFC_2_1_UNLOCK_END_0_REG_OFF    (0x22)
#define NFC_2_1_UNLOCK_START_1_REG_OFF  (0x24)
#define NFC_2_1_UNLOCK_END_1_REG_OFF    (0x26)
#define NFC_2_1_UNLOCK_START_2_REG_OFF  (0x28)
#define NFC_2_1_UNLOCK_END_2_REG_OFF    (0x2A)
#define NFC_2_1_UNLOCK_START_3_REG_OFF  (0x2C)
#define NFC_2_1_UNLOCK_END_3_REG_OFF    (0x2E)

/*
* Macro definitions for version 3.X NFCs.
*/

/*
* Macro definitions for version 3.1 NFCs.
*/

#define NFC_3_1_FLASH_ADDR_CMD_REG_OFF          (0x00)
#define NFC_3_1_CONFIG1_REG_OFF                 (0x04)
#define NFC_3_1_ECC_STATUS_RESULT_REG_OFF       (0x08)
#define NFC_3_1_LAUNCH_NFC_REG_OFF              (0x0C)

#define NFC_3_1_WRPROT_REG_OFF                  (0x00)
#define NFC_3_1_WRPROT_UNLOCK_BLK_ADD0_REG_OFF  (0x04)
#define NFC_3_1_CONFIG2_REG_OFF                 (0x14)
#define NFC_3_1_IPC_REG_OFF                     (0x18)

/*
* Macro definitions for version 3.2 NFCs.
*/

#define NFC_3_2_CMD_REG_OFF                (0x00)

#define NFC_3_2_ADD0_REG_OFF               (0x04)
#define NFC_3_2_ADD1_REG_OFF               (0x08)
#define NFC_3_2_ADD2_REG_OFF               (0x0C)
#define NFC_3_2_ADD3_REG_OFF               (0x10)
#define NFC_3_2_ADD4_REG_OFF               (0x14)
#define NFC_3_2_ADD5_REG_OFF               (0x18)
#define NFC_3_2_ADD6_REG_OFF               (0x1C)
#define NFC_3_2_ADD7_REG_OFF               (0x20)
#define NFC_3_2_ADD8_REG_OFF               (0x24)
#define NFC_3_2_ADD9_REG_OFF               (0x28)
#define NFC_3_2_ADD10_REG_OFF              (0x2C)
#define NFC_3_2_ADD11_REG_OFF              (0x30)

#define NFC_3_2_CONFIG1_REG_OFF            (0x34)
#define NFC_3_2_CONFIG1_SP_EN_POS          (0)
#define NFC_3_2_CONFIG1_SP_EN_MSK          (0x1 << 0)
#define NFC_3_2_CONFIG1_NF_CE_POS          (1)
#define NFC_3_2_CONFIG1_NF_CE_MSK          (0x1 << 1)
#define NFC_3_2_CONFIG1_RST_POS            (2)
#define NFC_3_2_CONFIG1_RST_MSK            (0x1 << 2)
#define NFC_3_2_CONFIG1_RBA_POS            (4)
#define NFC_3_2_CONFIG1_RBA_MSK            (0x7 << 4)
#define NFC_3_2_CONFIG1_ITER_POS           (8)
#define NFC_3_2_CONFIG1_ITER_MSK           (0xf << 8)
#define NFC_3_2_CONFIG1_CS_POS             (12)
#define NFC_3_2_CONFIG1_CS_MSK             (0x7 << 12)
#define NFC_3_2_CONFIG1_STATUS_POS         (16)
#define NFC_3_2_CONFIG1_STATUS_MSK         (0xFFFF<<16)

#define NFC_3_2_ECC_STATUS_REG_OFF         (0x38)
#define NFC_3_2_ECC_STATUS_NOBER1_POS      (0)
#define NFC_3_2_ECC_STATUS_NOBER1_MSK      (0xF << 0)
#define NFC_3_2_ECC_STATUS_NOBER2_POS      (4)
#define NFC_3_2_ECC_STATUS_NOBER2_MSK      (0xF << 4)
#define NFC_3_2_ECC_STATUS_NOBER3_POS      (8)
#define NFC_3_2_ECC_STATUS_NOBER3_MSK      (0xF << 8)
#define NFC_3_2_ECC_STATUS_NOBER4_POS      (12)
#define NFC_3_2_ECC_STATUS_NOBER4_MSK      (0xF << 12)
#define NFC_3_2_ECC_STATUS_NOBER5_POS      (16)
#define NFC_3_2_ECC_STATUS_NOBER5_MSK      (0xF << 16)
#define NFC_3_2_ECC_STATUS_NOBER6_POS      (20)
#define NFC_3_2_ECC_STATUS_NOBER6_MSK      (0xF << 20)
#define NFC_3_2_ECC_STATUS_NOBER7_POS      (24)
#define NFC_3_2_ECC_STATUS_NOBER7_MSK      (0xF << 24)
#define NFC_3_2_ECC_STATUS_NOBER8_POS      (28)
#define NFC_3_2_ECC_STATUS_NOBER8_MSK      (0xF << 28)


#define NFC_3_2_STATUS_SUM_REG_OFF         (0x3C)
#define NFC_3_2_STATUS_SUM_NAND_SUM_POS    (0x0)
#define NFC_3_2_STATUS_SUM_NAND_SUM_MSK    (0xFF << 0)
#define NFC_3_2_STATUS_SUM_ECC_SUM_POS     (8)
#define NFC_3_2_STATUS_SUM_ECC_SUM_MSK     (0xFF << 8)

#define NFC_3_2_LAUNCH_REG_OFF             (0x40)
#define NFC_3_2_LAUNCH_FCMD_POS            (0)
#define NFC_3_2_LAUNCH_FCMD_MSK            (0x1 << 0)
#define NFC_3_2_LAUNCH_FADD_POS            (1)
#define NFC_3_2_LAUNCH_FADD_MSK            (0x1 << 1)
#define NFC_3_2_LAUNCH_FDI_POS             (2)
#define NFC_3_2_LAUNCH_FDI_MSK             (0x1 << 2)
#define NFC_3_2_LAUNCH_FDO_POS             (3)
#define NFC_3_2_LAUNCH_FDO_MSK             (0x7 << 3)
#define NFC_3_2_LAUNCH_AUTO_PROG_POS       (6)
#define NFC_3_2_LAUNCH_AUTO_PROG_MSK       (0x1 << 6)
#define NFC_3_2_LAUNCH_AUTO_READ_POS       (7)
#define NFC_3_2_LAUNCH_AUTO_READ_MSK       (0x1 << 7)
#define NFC_3_2_LAUNCH_AUTO_ERASE_POS      (9)
#define NFC_3_2_LAUNCH_AUTO_ERASE_MSK      (0x1 << 9)
#define NFC_3_2_LAUNCH_COPY_BACK0_POS      (10)
#define NFC_3_2_LAUNCH_COPY_BACK0_MSK      (0x1 << 10)
#define NFC_3_2_LAUNCH_COPY_BACK1_POS      (11)
#define NFC_3_2_LAUNCH_COPY_BACK1_MSK      (0x1 << 11)
#define NFC_3_2_LAUNCH_AUTO_STATUS_POS     (12)
#define NFC_3_2_LAUNCH_AUTO_STATUS_MSK     (0x1 << 12)

#define NFC_3_2_WRPROT_REG_OFF             (0x00)
#define NFC_3_2_WRPROT_WPC_POS             (0)
#define NFC_3_2_WRPROT_WPC_MSK             (0x7 << 0)
#define NFC_3_2_WRPROT_CS2L_POS            (3)
#define NFC_3_2_WRPROT_CS2L_MSK            (0x7 << 3)
#define NFC_3_2_WRPROT_BLS_POS             (6)
#define NFC_3_2_WRPROT_BLS_MSK             (0x3 << 6)
#define NFC_3_2_WRPROT_LTS0_POS            (8)
#define NFC_3_2_WRPROT_LTS0_MSK            (0x1 << 8)
#define NFC_3_2_WRPROT_LS0_POS             (9)
#define NFC_3_2_WRPROT_LS0_MSK             (0x1 << 9)
#define NFC_3_2_WRPROT_US0_POS             (10)
#define NFC_3_2_WRPROT_US0_MSK             (0x1 << 10)
#define NFC_3_2_WRPROT_LTS1_POS            (11)
#define NFC_3_2_WRPROT_LTS1_MSK            (0x1 << 11)
#define NFC_3_2_WRPROT_LS1_POS             (12)
#define NFC_3_2_WRPROT_LS1_MSK             (0x1 << 12)
#define NFC_3_2_WRPROT_US1_POS             (13)
#define NFC_3_2_WRPROT_US1_MSK             (0x1 << 13)
#define NFC_3_2_WRPROT_LTS2_POS            (14)
#define NFC_3_2_WRPROT_LTS2_MSK            (0x1 << 14)
#define NFC_3_2_WRPROT_LS2_POS             (15)
#define NFC_3_2_WRPROT_LS2_MSK             (0x1 << 15)
#define NFC_3_2_WRPROT_US2_POS             (16)
#define NFC_3_2_WRPROT_US2_MSK             (0x1 << 16)
#define NFC_3_2_WRPROT_LTS3_POS            (17)
#define NFC_3_2_WRPROT_LTS3_MSK            (0x1 << 17)
#define NFC_3_2_WRPROT_LS3_POS             (18)
#define NFC_3_2_WRPROT_LS3_MSK             (0x1 << 18)
#define NFC_3_2_WRPROT_US3_POS             (19)
#define NFC_3_2_WRPROT_US3_MSK             (0x1 << 19)
#define NFC_3_2_WRPROT_LTS4_POS            (20)
#define NFC_3_2_WRPROT_LTS4_MSK            (0x1 << 20)
#define NFC_3_2_WRPROT_LS4_POS             (21)
#define NFC_3_2_WRPROT_LS4_MSK             (0x1 << 21)
#define NFC_3_2_WRPROT_US4_POS             (22)
#define NFC_3_2_WRPROT_US4_MSK             (0x1 << 22)
#define NFC_3_2_WRPROT_LTS5_POS            (23)
#define NFC_3_2_WRPROT_LTS5_MSK            (0x1 << 23)
#define NFC_3_2_WRPROT_LS5_POS             (24)
#define NFC_3_2_WRPROT_LS5_MSK             (0x1 << 24)
#define NFC_3_2_WRPROT_US5_POS             (25)
#define NFC_3_2_WRPROT_US5_MSK             (0x1 << 25)
#define NFC_3_2_WRPROT_LTS6_POS            (26)
#define NFC_3_2_WRPROT_LTS6_MSK            (0x1 << 26)
#define NFC_3_2_WRPROT_LS6_POS             (27)
#define NFC_3_2_WRPROT_LS6_MSK             (0x1 << 27)
#define NFC_3_2_WRPROT_US6_POS             (28)
#define NFC_3_2_WRPROT_US6_MSK             (0x1 << 28)
#define NFC_3_2_WRPROT_LTS7_POS            (29)
#define NFC_3_2_WRPROT_LTS7_MSK            (0x1 << 29)
#define NFC_3_2_WRPROT_LS7_POS             (30)
#define NFC_3_2_WRPROT_LS7_MSK             (0x1 << 30)
#define NFC_3_2_WRPROT_US7_POS             (31)
#define NFC_3_2_WRPROT_US7_MSK             (0x1 << 31)

#define NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF    (0x04)
#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD1_REG_OFF    (0x08)
#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD2_REG_OFF    (0x0C)
#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD3_REG_OFF    (0x10)
#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD4_REG_OFF    (0x14)
#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD5_REG_OFF    (0x18)
#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD6_REG_OFF    (0x1C)
#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_MSK  (0xFFFF<<16)

#define NFC_3_2_UNLOCK_BLK_ADD7_REG_OFF    (0x20)
#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_POS  (0)
#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_MSK  (0xFFFF << 0)
#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_POS  (16)
#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_MSK  (0xFFFF<<16)

#define NFC_3_2_CONFIG2_REG_OFF            (0x24)
#define NFC_3_2_CONFIG2_PS_POS             (0)
#define NFC_3_2_CONFIG2_PS_MSK             (0x3 << 0)
#define NFC_3_2_CONFIG2_SYM_POS            (2)
#define NFC_3_2_CONFIG2_SYM_MSK            (0x1 << 2)
#define NFC_3_2_CONFIG2_ECC_EN_POS         (3)
#define NFC_3_2_CONFIG2_ECC_EN_MSK         (0x1 << 3)
#define NFC_3_2_CONFIG2_CMD_PHASES_POS     (4)
#define NFC_3_2_CONFIG2_CMD_PHASES_MSK     (0x1 << 4)
#define NFC_3_2_CONFIG2_ADDR_PHASES0_POS   (5)
#define NFC_3_2_CONFIG2_ADDR_PHASES0_MSK   (0x1 << 5)
#define NFC_3_2_CONFIG2_ECC_MODE_POS       (6)
#define NFC_3_2_CONFIG2_ECC_MODE_MSK       (0x1 << 6)
#define NFC_3_2_CONFIG2_PPB_POS            (7)
#define NFC_3_2_CONFIG2_PPB_MSK            (0x3 << 7)
#define NFC_3_2_CONFIG2_EDC_POS            (9)
#define NFC_3_2_CONFIG2_EDC_MSK            (0x7 << 9)
#define NFC_3_2_CONFIG2_ADDR_PHASES1_POS   (12)
#define NFC_3_2_CONFIG2_ADDR_PHASES1_MSK   (0x3 << 12)
#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_POS  (14)
#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_MSK  (0x1 << 14)
#define NFC_3_2_CONFIG2_INT_MSK_POS        (15)
#define NFC_3_2_CONFIG2_INT_MSK_MSK        (0x1 << 15)
#define NFC_3_2_CONFIG2_SPAS_POS           (16)
#define NFC_3_2_CONFIG2_SPAS_MSK           (0xFF << 16)
#define NFC_3_2_CONFIG2_ST_CMD_POS         (24)
#define NFC_3_2_CONFIG2_ST_CMD_MSK         (0xFF << 24)

#define NFC_3_2_CONFIG3_REG_OFF            (0x28)
#define NFC_3_2_CONFIG3_ADD_OP_POS         (0)
#define NFC_3_2_CONFIG3_ADD_OP_MSK         (0x3 << 0)
#define NFC_3_2_CONFIG3_TOO_POS            (2)
#define NFC_3_2_CONFIG3_TOO_MSK            (0x1 << 2)
#define NFC_3_2_CONFIG3_FW_POS             (3)
#define NFC_3_2_CONFIG3_FW_MSK             (0x1 << 3)
#define NFC_3_2_CONFIG3_SB2R_POS           (4)
#define NFC_3_2_CONFIG3_SB2R_MSK           (0x7 << 4)
#define NFC_3_2_CONFIG3_NF_BIG_POS         (7)
#define NFC_3_2_CONFIG3_NF_BIG_MSK         (0x1 << 7)
#define NFC_3_2_CONFIG3_SBB_POS            (8)
#define NFC_3_2_CONFIG3_SBB_MSK            (0x7 << 8)
#define NFC_3_2_CONFIG3_DMA_MODE_POS       (11)
#define NFC_3_2_CONFIG3_DMA_MODE_MSK       (0x1 << 11)
#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS (12)
#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK (0x7 << 12)
#define NFC_3_2_CONFIG3_RBB_MODE_POS       (15)
#define NFC_3_2_CONFIG3_RBB_MODE_MSK       (0x1 << 15)
#define NFC_3_2_CONFIG3_FMP_POS            (16)
#define NFC_3_2_CONFIG3_FMP_MSK            (0xF << 16)
#define NFC_3_2_CONFIG3_NO_SDMA_POS        (20)
#define NFC_3_2_CONFIG3_NO_SDMA_MSK        (0x1 << 20)

#define NFC_3_2_IPC_REG_OFF                (0x2C)
#define NFC_3_2_IPC_CREQ_POS               (0)
#define NFC_3_2_IPC_CREQ_MSK               (0x1 << 0)
#define NFC_3_2_IPC_CACK_POS               (1)
#define NFC_3_2_IPC_CACK_MSK               (0x1 << 1)
#define NFC_3_2_IPC_DMA_STATUS_POS         (26)
#define NFC_3_2_IPC_DMA_STATUS_MSK         (0x3 << 26)
#define NFC_3_2_IPC_RB_B_POS               (28)
#define NFC_3_2_IPC_RB_B_MSK               (0x1 << 28)
#define NFC_3_2_IPC_LPS_POS                (29)
#define NFC_3_2_IPC_LPS_MSK                (0x1 << 29)
#define NFC_3_2_IPC_AUTO_PROG_DONE_POS     (30)
#define NFC_3_2_IPC_AUTO_PROG_DONE_MSK     (0x1 << 30)
#define NFC_3_2_IPC_INT_POS                (31)
#define NFC_3_2_IPC_INT_MSK                (0x1 << 31)

#define NFC_3_2_AXI_ERR_ADD_REG_OFF        (0x30)

/**
 * enum override - Choices for overrides.
 *
 * Some functions of this driver can be overriden at run time. This is a
 * convenient enumerated type for all such options.
 */

enum override {
      NEVER         = -1,
      DRIVER_CHOICE =  0,
      ALWAYS        =  1,
};

/**
 * struct physical_geometry - Physical geometry description.
 *
 * This structure describes the physical geometry of the medium.
 *
 * @chip_count:      The number of chips in the medium.
 * @chip_size:       The size, in bytes, of a single chip (excluding the
 *                   out-of-band bytes).
 * @block_size:      The size, in bytes, of a single block (excluding the
 *                   out-of-band bytes).
 * @page_data_size:  The size, in bytes, of the data area in a page (excluding
 *                   the out-of-band bytes).
 * @page_oob_size:   The size, in bytes, of the out-of-band area in a page.
 */

00521 struct physical_geometry {
      unsigned int  chip_count;
      uint64_t      chip_size;
      unsigned int  block_size;
      unsigned int  page_data_size;
      unsigned int  page_oob_size;
};

/**
 * struct nfc_geometry - NFC geometry description.
 *
 * This structure describes the NFC's view of the medium geometry, which may be
 * different from the physical geometry (for example, we treat pages that are
 * physically 2K+112 as if they are 2K+64).
 *
 * @page_data_size:  The size of the data area in a page (excluding the
 *                   out-of-band bytes). This is almost certain to be the same
 *                   as the physical data size.
 * @page_oob_size:   The size of the out-of-band area in a page. This may be
 *                   different from the physical OOB size.
 * @ecc_algorithm:   The name of the ECC algorithm (e.g., "Reed-Solomon" or
 *                   "BCH").
 * @ecc_strength:    A number that describes the strength of the ECC algorithm.
 *                   For example, various i.MX SoC's support ECC-1, ECC-4 or
 *                   ECC-8 of the Reed-Solomon ECC algorithm.
 * @buffer_count:    The number of main/spare buffers used with this geometry.
 * @spare_buf_size:  The number of bytes held in each spare buffer.
 * @spare_buf_spill: The number of extra bytes held in the last spare buffer.
 * @mtd_layout:      The MTD layout that best matches the geometry described by
 *                   the rest of this structure. The logical layer might not use
 *                   this structure, especially when interleaving.
 */

00554 struct nfc_geometry {
      const unsigned int     page_data_size;
      const unsigned int     page_oob_size;
      const char             *ecc_algorithm;
      const int              ecc_strength;
      const unsigned int     buffer_count;
      const unsigned int     spare_buf_size;
      const unsigned int     spare_buf_spill;
      struct nand_ecclayout  mtd_layout;
};

/**
 * struct logical_geometry - Logical geometry description.
 *
 * This structure describes the logical geometry we expose to MTD. This geometry
 * may be different from the physical or NFC geometries, especially when
 * interleaving.
 *
 * @chip_count:      The number of chips in the medium.
 * @chip_size:       The size, in bytes, of a single chip (excluding the
 *                   out-of-band bytes).
 * @usable_size:     The size, in bytes, of the medium that MTD can actually
 *                   use. This may be less than the chip size multiplied by the
 *                   number of chips.
 * @block_size:      The size, in bytes, of a single block (excluding the
 *                   out-of-band bytes).
 * @page_data_size:  The size of the data area in a page (excluding the
 *                   out-of-band bytes).
 * @page_oob_size:   The size of the out-of-band area in a page.
 */

00585 struct logical_geometry {
      unsigned int           chip_count;
      uint32_t               chip_size;
      uint32_t               usable_size;
      unsigned int           block_size;
      unsigned int           page_data_size;
      unsigned int           page_oob_size;
      struct nand_ecclayout  *mtd_layout;
};

/**
 * struct imx_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.
 * @buffers:             A pointer to the NFC buffers.
 * @primary_regs:        A pointer to the NFC primary registers.
 * @secondary_regs:      A pointer to the NFC secondary registers.
 * @clock:               A pointer to the NFC struct clk.
 * @interrupt:           The NFC interrupt number.
 * @physical_geometry:   A description of the medium's physical geometry.
 * @nfc:                 A pointer to the NFC HAL.
 * @nfc_geometry:        A description of the medium geometry as viewed by the
 *                       NFC.
 * @done:                The struct completion we use to handle interrupts.
 * @logical_geometry:    A description of the logical geometry exposed to MTD.
 * @interrupt_override:  Can override how the driver uses interrupts when
 *                       waiting for the NFC.
 * @auto_op_override:    Can override whether the driver uses automatic NFC
 *                       operations.
 * @inject_ecc_error:    Indicates that the driver should inject an ECC error in
 *                       the next read operation that uses ECC. User space
 *                       programs can set this value through the sysfs node of
 *                       the same name. If this value is less than zero, the
 *                       driver will inject an uncorrectable ECC error. If this
 *                       value is greater than zero, the driver will inject that
 *                       number of correctable errors, capped at whatever
 *                       possible maximum currently applies.
 * @current_chip:        The chip currently selected by the NAND Fash MTD HIL.
 *                       A negative value indicates that no chip is selected.
 *                       We use this field to detect when the HIL begins and
 *                       ends essential transactions. This helps us to know when
 *                       we should turn the NFC clock on or off.
 * @command:             The last command the HIL tried to send by calling
 *                       cmdfunc(). Later functions use this information to
 *                       adjust their behavior. The sentinel value ~0 indicates
 *                       no command.
 * @command_is_new:      Indicates the command has just come down to cmdfunc()
 *                       from the HIL and hasn't yet been handled. Other
 *                       functions use this to adjust their behavior.
 * @page_address:        The current page address of interest. For reads, this
 *                       information arrives in calls to cmdfunc(), but we don't
 *                       actually use it until later.
 * @nand:                The data structure that represents this NAND Flash
 *                       medium to the MTD NAND Flash system.
 * @mtd:                 The data structure that represents this NAND Flash
 *                       medium to MTD.
 * @partitions:          A pointer to a set of partitions collected from one of
 *                       several possible sources (e.g., the boot loader, the
 *                       kernel command line, etc.). See the global variable
 *                       partition_source_types for the list of partition
 *                       sources we examine. If this member is NULL, then no
 *                       partitions were discovered.
 * @partition_count:     The number of discovered partitions.
 */

00657 struct imx_nfc_data {

      /* System Interface */
      struct device                 *dev;
      struct platform_device        *pdev;

      /* Platform Configuration */
      struct imx_nfc_platform_data  *pdata;
      void                          *buffers;
      void                          *primary_regs;
      void                          *secondary_regs;
      struct clk                    *clock;
      unsigned int                  interrupt;

      /* Flash Hardware */
      struct physical_geometry      physical_geometry;

      /* NFC HAL and NFC Utilities */
      struct nfc_hal                *nfc;
      struct nfc_geometry           *nfc_geometry;
      struct completion             done;

      /* Medium Abstraction Layer */
      struct logical_geometry       logical_geometry;
      enum override                 interrupt_override;
      enum override                 auto_op_override;
      int                           inject_ecc_error;

      /* MTD Interface Layer */
      int                           current_chip;
      unsigned int                  command;
      int                           command_is_new;
      int                           page_address;

      /* NAND Flash MTD */
      struct nand_chip              nand;

      /* MTD */
      struct mtd_info               mtd;
      struct mtd_partition          *partitions;
      unsigned int                  partition_count;

};

/**
 * struct nfc_hal - i.MX NFC HAL
 *
 * This structure embodies an abstract interface to the underlying NFC hardware.
 *
 * @major_version:       The major version number of the NFC to which this
 *                       structure applies.
 * @minor_version:       The minor version number of the NFC to which this
 *                       structure applies.
 * @max_chip_count:      The maximum number of chips the NFC can possibly
 *                       support. This may *not* be the actual number of chips
 *                       currently connected. This value is constant for NFC's
 *                       of a given version.
 * @max_buffer_count:    The number of main/spare buffers available in the NFC's
 *                       memory. This value is constant for NFC's of a given
 *                       version.
 * @spare_buf_stride:    The stride, in bytes, from the beginning of one spare
 *                       buffer to the beginning of the next one. This value is
 *                       constant for NFC's of a given version.
 * @has_secondary_regs:  Indicates if the NFC has a secondary register set that
 *                       must be mapped in.
 * @can_be_symmetric:    Indicates if the NFC supports a "symmetric" clock. When
 *                       the clock is "symmetric," the hardware waits one NFC
 *                       clock for every read/write cycle. When the clock is
 *                       "asymmetric," the hardware waits two NFC clocks for
 *                       every read/write cycle.
 * @init:                Initializes the NFC and any version-specific 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. A non-zero
 *                       return value indicates failure.
 * @set_geometry:        Based on the physical geometry, this function selects
 *                       an NFC geometry structure and configures the NFC
 *                       hardware to match. A  non-zero return value indicates
 *                       failure.
 * @exit:                Shuts down the NFC and cleans up version-specific data
 *                       structures. This function will be called after the
 *                       platform has shut down off-chip communication but while
 *                       communication with the NFC still works.
 * @set_closest_cycle:   Configures the hardware to make the NAND Flash bus
 *                       cycle period as close as possible to the given cycle
 *                       period. This function is called during boot up and may
 *                       assume that, at the time it's called, the parent clock
 *                       is running at the highest rate it will ever run. Thus,
 *                       this function need never worry that the NAND Flash bus
 *                       will run faster and potentially make it impossible to
 *                       communicate with the NAND Flash device -- it will only
 *                       run slower.
 * @mask_interrupt:      Masks the NFC's interrupt.
 * @unmask_interrupt:    Unmasks the NFC's interrupt.
 * @clear_interrupt:     Clears the NFC's interrupt.
 * @is_interrupting:     Returns true if the NFC is interrupting.
 * @is_ready:            Returns true if all the chips in the medium are ready.
 *                       This member may be set to NULL, which indicates that
 *                       the underlying NFC hardware doesn't expose  ready/busy
 *                       signals.
 * @set_force_ce:        If passed true, forces the hardware chip enable signal
 *                       for the current chip to be asserted always. If passed
 *                       false, causes the chip enable signal to be asserted
 *                       only during I/O.
 * @set_ecc:             Sets ECC on or off.
 * @get_ecc_status:      Examines the hardware ECC status and returns:
 *                           == 0  =>  No errors.
 *                           >  0  =>  The number of corrected errors.
 *                           <  0  =>  There were uncorrectable errors.
 * @get_symmetric:       Gets the current symmetric clock setting. For versions
 *                       that don't support symmetric clocks, this function
 *                       always returns false.
 * @set_symmetric:       For versions that support symmetric clocks, sets
 *                       whether or not the clock is symmetric.
 * @select_chip:         Selects the current chip.
 * @command_cycle:       Sends a command code and then returns immediately
 *                       *without* waiting for the NFC to finish.
 * @write_cycle:         Applies a single write cycle to the current chip,
 *                 sending the given byte, and waiting for the NFC to
 *                       finish.
 * @read_cycle:          Applies a single read cycle to the current chip and
 *                       returns the result, necessarily waiting for the NFC to
 *                       finish. The width of the result is the same as the
 *                       width of the Flash bus.
 * @read_page:           Applies read cycles to the current chip to read an
 *                       entire page into the NFC. Note that ECC is enabled or
 *                       disabled with the set_ecc function pointer (see above).
 *                       This function waits for the NFC to finish before
 *                       returning.
 * @send_page:           Applies write cycles to send an entire page from the
 *                       NFC to the current chip. Note that ECC is enabled or
 *                       disabled with the set_ecc function pointer (see above).
 *                       This function waits for the NFC to finish before
 *                       returning.
 * @start_auto_read:     Starts an automatic read operation. A NULL pointer
 *                       indicates automatic read operations aren't available
 *                       with this NFC version.
 * @wait_for_auto_read:  Blocks until an automatic read operation is ready for
 *                       the CPU to copy a page out of the NFC.
 * @resume_auto_read:    Resumes an automatic read operation after the CPU has
 *                       copied a page out.
 * @start_auto_write:    Starts an automatic write operation. A NULL pointer
 *                       indicates automatic write operations aren't available
 *                       with this NFC version.
 * @wait_for_auto_write: Blocks until an automatic write operation is ready for
 *                       the CPU to copy a page into the NFC.
 * @start_auto_erase:    Starts an automatic erase operation. A NULL pointer
 *                       indicates automatic erase operations aren't available
 *                       with this NFC version.
 */

00810 struct nfc_hal {
      const unsigned int  major_version;
      const unsigned int  minor_version;
      const unsigned int  max_chip_count;
      const unsigned int  max_buffer_count;
      const unsigned int  spare_buf_stride;
      const int           has_secondary_regs;
      const int           can_be_symmetric;
      int       (*init)               (struct imx_nfc_data *);
      int       (*set_geometry)       (struct imx_nfc_data *);
      void      (*exit)               (struct imx_nfc_data *);
      int       (*set_closest_cycle)  (struct imx_nfc_data *, unsigned ns);
      void      (*mask_interrupt)     (struct imx_nfc_data *);
      void      (*unmask_interrupt)   (struct imx_nfc_data *);
      void      (*clear_interrupt)    (struct imx_nfc_data *);
      int       (*is_interrupting)    (struct imx_nfc_data *);
      int       (*is_ready)           (struct imx_nfc_data *);
      void      (*set_force_ce)       (struct imx_nfc_data *, int on);
      void      (*set_ecc)            (struct imx_nfc_data *, int on);
      int       (*get_ecc_status)     (struct imx_nfc_data *);
      int       (*get_symmetric)      (struct imx_nfc_data *);
      void      (*set_symmetric)      (struct imx_nfc_data *, int on);
      void      (*select_chip)        (struct imx_nfc_data *, int chip);
      void      (*command_cycle)      (struct imx_nfc_data *, unsigned cmd);
      void      (*write_cycle)        (struct imx_nfc_data *, unsigned byte);
      unsigned  (*read_cycle)         (struct imx_nfc_data *);
      void      (*read_page)          (struct imx_nfc_data *);
      void      (*send_page)          (struct imx_nfc_data *);
      int       (*start_auto_read)    (struct imx_nfc_data *,
            unsigned start, unsigned count, unsigned column, unsigned page);
      int       (*wait_for_auto_read) (struct imx_nfc_data *);
      int       (*resume_auto_read)   (struct imx_nfc_data *);
      int       (*start_auto_write)   (struct imx_nfc_data *,
            unsigned start, unsigned count, unsigned column, unsigned page);
      int       (*wait_for_auto_write)(struct imx_nfc_data *);
      int       (*start_auto_erase)   (struct imx_nfc_data *,
            unsigned start, unsigned count, unsigned page);
};

/*
 * This variable controls whether or not probing is enabled. If false, then
 * the driver will refuse to probe. The "enable" module parameter controls the
 * value of this variable.
 */

static int imx_nfc_module_enable = true;

#ifdef EVENT_REPORTING

/*
 * This variable controls whether the driver reports event information by
 * printing to the console. The "report_events" module parameter controls the
 * value of this variable.
 */

static int imx_nfc_module_report_events; /* implicitly initialized false*/

#endif

/*
 * This variable potentially overrides the driver's choice to interleave. The
 * "interleave_override" module parameter controls the value of this variable.
 */

static enum override imx_nfc_module_interleave_override = DRIVER_CHOICE;

/*
 * When set, this variable forces the driver to use the bytewise copy functions
 * to get data in and out of the NFC. This is intended for testing, not typical
 * use.
 */

static int imx_nfc_module_force_bytewise_copy; /* implicitly initialized false*/

/*
 * The list of algorithms we use to get partition information from the
 * environment.
 */

#ifdef CONFIG_MTD_PARTITIONS
static const char *partition_source_types[] = { "cmdlinepart", NULL };
#endif

/*
 * The following structures describe the NFC geometries we support.
 *
 * Notice that pieces of some structure definitions are commented out and edited
 * because various parts of the MTD system can't handle the way our hardware
 * formats the out-of-band area.
 *
 * Here are the problems:
 *
 * - struct nand_ecclayout expects no more than 64 ECC bytes.
 *
 *     The eccpos member of struct nand_ecclayout can't hold more than 64 ECC
 *     byte positions. Some of our formats have more so, unedited, they won't
 *     even compile. We comment out all ECC byte positions after the 64th one.
 *
 * - struct nand_ecclayout expects no more than 8 free spans.
 *
 *     The oobfree member of struct nand_ecclayout can't hold more than 8 free
 *     spans. Some of our formats have more so, unedited, they won't even
 *     compile. We comment out all free spans after the eighth one.
 *
 * - The MEMGETOOBSEL command in the mtdchar driver.
 *
 *     The mtdchar ioctl command MEMGETOOBSEL checks the number of ECC bytes
 *     against the length of the eccpos array in struct nand_oobinfo
 *     (see include/mtd/mtd-abi.h). This array can handle up to 32 ECC bytes,
 *     but some of our formats have more.
 *
 *     To make this work, we cap the value assigned to eccbytes at 32.
 *
 *     Notice that struct nand_oobinfo, used by mtdchar, is *different* from the
 *     struct nand_ecclayout that MTD uses internally. The latter structure
 *     can accomodate up to 64 ECC byte positions. Thus, we declare up to 64
 *     ECC byte positions here, even if the advertised number of ECC bytes is
 *     less.
 *
 *     This command is obsolete and, if no one used it, we wouldn't care about
 *     this problem. Unfortunately The nandwrite program uses it, and everyone
 *     expects nandwrite to work (it's how everyone usually lays down their
 *     JFFS2 file systems).
 */

static struct nfc_geometry  nfc_geometry_512_16_RS_ECC1 = {
      .page_data_size  = 512,
      .page_oob_size   = 16,
      .ecc_algorithm   = "Reed-Solomon",
      .ecc_strength    = 1,
      .buffer_count    = 1,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 5,
            .eccpos   = {6, 7, 8, 9, 10},
            .oobavail = 11,
            .oobfree  = { {0, 6}, {11, 5} },
      }
};

static struct nfc_geometry  nfc_geometry_512_16_RS_ECC4 = {
      .page_data_size  = 512,
      .page_oob_size   = 16,
      .ecc_algorithm   = "Reed-Solomon",
      .ecc_strength    = 4,
      .buffer_count    = 1,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 9,
            .eccpos   = {7, 8, 9, 10, 11, 12, 13, 14, 15},
            .oobavail = 7,
            .oobfree  = { {0, 7} },
      }
};

static struct nfc_geometry  nfc_geometry_512_16_BCH_ECC4 = {
      .page_data_size  = 512,
      .page_oob_size   = 16,
      .ecc_algorithm   = "BCH",
      .ecc_strength    = 4,
      .buffer_count    = 1,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 8,
            .eccpos   = {8, 9, 10, 11, 12, 13, 14, 15},
            .oobavail = 8,
            .oobfree  = { {0, 8} },
      }
};

static struct nfc_geometry  nfc_geometry_2K_64_RS_ECC4 = {
      .page_data_size  = 2048,
      .page_oob_size   = 64,
      .ecc_algorithm   = "Reed-Solomon",
      .ecc_strength    = 4,
      .buffer_count    = 4,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 32 /*9 * 4*/, /* See notes above. */
            .eccpos   = {

                  (0*16)+7 , (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
                  (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,

                  (1*16)+7 , (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
                  (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,

                  (2*16)+7 , (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
                  (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,

                  (3*16)+7 , (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
                  (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,

            },
            .oobavail = 7 * 4,
            .oobfree  = {
                  {(0*16)+0, 7},
                  {(1*16)+0, 7},
                  {(2*16)+0, 7},
                  {(3*16)+0, 7},
            },
      }
};

static struct nfc_geometry  nfc_geometry_2K_64_BCH_ECC4 = {
      .page_data_size  = 2048,
      .page_oob_size   = 64,
      .ecc_algorithm   = "BCH",
      .ecc_strength    = 4,
      .buffer_count    = 4,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 8 * 4,
            .eccpos   = {

                  (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
                  (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,

                  (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
                  (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,

                  (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
                  (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,

                  (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
                  (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,

            },
            .oobavail = 8 * 4,
            .oobfree  = {
                  {(0*16)+0, 8},
                  {(1*16)+0, 8},
                  {(2*16)+0, 8},
                  {(3*16)+0, 8},
            },
      }
};

static struct nfc_geometry  nfc_geometry_4K_128_BCH_ECC4 = {
      .page_data_size  = 4096,
      .page_oob_size   = 128,
      .ecc_algorithm   = "BCH",
      .ecc_strength    = 4,
      .buffer_count    = 8,
      .spare_buf_size  = 16,
      .spare_buf_spill = 0,
      .mtd_layout = {
            .eccbytes = 8 * 8,
            .eccpos   = {

                  (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
                  (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,

                  (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
                  (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,

                  (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
                  (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,

                  (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
                  (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,

                  (4*16)+8 , (4*16)+9 , (4*16)+10, (4*16)+11,
                  (4*16)+12, (4*16)+13, (4*16)+14, (4*16)+15,

                  (5*16)+8 , (5*16)+9 , (5*16)+10, (5*16)+11,
                  (5*16)+12, (5*16)+13, (5*16)+14, (5*16)+15,

                  (6*16)+8 , (6*16)+9 , (6*16)+10, (6*16)+11,
                  (6*16)+12, (6*16)+13, (6*16)+14, (6*16)+15,

                  (7*16)+8 , (7*16)+9 , (7*16)+10, (7*16)+11,
                  (7*16)+12, (7*16)+13, (7*16)+14, (7*16)+15,

            },
            .oobavail = 8 * 8,
            .oobfree  = {
                  {(0*16)+0, 8},
                  {(1*16)+0, 8},
                  {(2*16)+0, 8},
                  {(3*16)+0, 8},
                  {(4*16)+0, 8},
                  {(5*16)+0, 8},
                  {(6*16)+0, 8},
                  {(7*16)+0, 8},
            },
      }
};

static struct nfc_geometry  nfc_geometry_4K_218_BCH_ECC8 = {
      .page_data_size  = 4096,
      .page_oob_size   = 218,
      .ecc_algorithm   = "BCH",
      .ecc_strength    = 8,
      .buffer_count    = 8,
      .spare_buf_size  = 26,
      .spare_buf_spill = 10,
      .mtd_layout = {
            .eccbytes = 32 /*10 * 8*/, /* See notes above. */
            .eccpos   = {

                  (0*26)+12, (0*26)+13, (0*26)+14, (0*26)+15, (0*26)+16,
                  (0*26)+17, (0*26)+18, (0*26)+19, (0*26)+20, (0*26)+21,

                  (1*26)+12, (1*26)+13, (1*26)+14, (1*26)+15, (1*26)+16,
                  (1*26)+17, (1*26)+18, (1*26)+19, (1*26)+20, (1*26)+21,

                  (2*26)+12, (2*26)+13, (2*26)+14, (2*26)+15, (2*26)+16,
                  (2*26)+17, (2*26)+18, (2*26)+19, (2*26)+20, (2*26)+21,

                  (3*26)+12, (3*26)+13, (3*26)+14, (3*26)+15, (3*26)+16,
                  (3*26)+17, (3*26)+18, (3*26)+19, (3*26)+20, (3*26)+21,

                  (4*26)+12, (4*26)+13, (4*26)+14, (4*26)+15, (4*26)+16,
                  (4*26)+17, (4*26)+18, (4*26)+19, (4*26)+20, (4*26)+21,

                  (5*26)+12, (5*26)+13, (5*26)+14, (5*26)+15, (5*26)+16,
                  (5*26)+17, (5*26)+18, (5*26)+19, (5*26)+20, (5*26)+21,

                  (6*26)+12, (6*26)+13, (6*26)+14, (6*26)+15,
                  /*  See notes above.
                  (6*26)+16,
                  (6*26)+17, (6*26)+18, (6*26)+19, (6*26)+20, (6*26)+21,

                  (7*26)+12, (7*26)+13, (7*26)+14, (7*26)+15, (7*26)+16,
                  (7*26)+17, (7*26)+18, (7*26)+19, (7*26)+20, (7*26)+21,
                  */

            },
            .oobavail = 96 /*(16 * 8) + 10*/, /* See notes above. */
            .oobfree  = {
                  {(0*26)+0, 12}, {(0*26)+22, 4},
                  {(1*26)+0, 12}, {(1*26)+22, 4},
                  {(2*26)+0, 12}, {(2*26)+22, 4},
                  {(3*26)+0, 48}, /* See notes above. */
                  /* See notes above.
                  {(3*26)+0, 12}, {(3*26)+22, 4},
                  {(4*26)+0, 12}, {(4*26)+22, 4},
                  {(5*26)+0, 12}, {(5*26)+22, 4},
                  {(6*26)+0, 12}, {(6*26)+22, 4},
                  {(7*26)+0, 12}, {(7*26)+22, 4 + 10},
                  */
            },
      }
};

/*
 * When the logical geometry differs from the NFC geometry (e.g.,
 * interleaving), we synthesize a layout rather than use the one that comes with
 * the NFC geometry. See mal_set_logical_geometry().
 */

static struct nand_ecclayout  synthetic_layout;

/*
 * These structures describe how the BBT code will find block marks in the OOB
 * area of a page. Don't be confused by the fact that this is the same type used
 * to describe bad block tables. Some of the same information is needed, so the
 * designers use the same structure for two conceptually distinct functions.
 */

static uint8_t block_mark_pattern[] = { 0xff, 0xff };

static struct nand_bbt_descr small_page_block_mark_descriptor = {
      .options = NAND_BBT_SCAN2NDPAGE,
      .offs    = 5,
      .len     = 1,
      .pattern = block_mark_pattern,
};

static struct nand_bbt_descr large_page_block_mark_descriptor = {
      .options = NAND_BBT_SCAN2NDPAGE,
      .offs    = 0,
      .len     = 2,
      .pattern = block_mark_pattern,
};

/*
 * Bad block table descriptors for the main and mirror tables.
 */

static uint8_t bbt_main_pattern[]   = { 'B', 'b', 't', '0' };
static uint8_t bbt_mirror_pattern[] = { '1', 't', 'b', 'B' };

static struct nand_bbt_descr bbt_main_descriptor = {
      .options =
            NAND_BBT_LASTBLOCK |
            NAND_BBT_CREATE    |
            NAND_BBT_WRITE     |
            NAND_BBT_2BIT      |
            NAND_BBT_VERSION   |
            NAND_BBT_PERCHIP,
      .offs      = 0,
      .len       = 4,
      .veroffs   = 4,
      .maxblocks = 4,
      .pattern   = bbt_main_pattern,
};

static struct nand_bbt_descr bbt_mirror_descriptor = {
      .options =
            NAND_BBT_LASTBLOCK |
            NAND_BBT_CREATE    |
            NAND_BBT_WRITE     |
            NAND_BBT_2BIT      |
            NAND_BBT_VERSION   |
            NAND_BBT_PERCHIP,
      .offs      = 0,
      .len       = 4,
      .veroffs   = 4,
      .maxblocks = 4,
      .pattern   = bbt_mirror_pattern,
};

#ifdef EVENT_REPORTING

/**
 * struct event - A single record in the event trace.
 *
 * @time:         The time at which the event occurred.
 * @nesting:      Indicates function call nesting.
 * @description:  A description of the event.
 */

01239 struct event {
      ktime_t       time;
      unsigned int  nesting;
      char          *description;
};

/**
 * The event trace.
 *
 * @overhead:  The delay to take a time stamp and nothing else.
 * @nesting:   The current nesting level.
 * @overflow:  Indicates the trace overflowed.
 * @next:      Index of the next event to write.
 * @events:    The array of events.
 */

#define MAX_EVENT_COUNT  (200)

static struct {
      ktime_t       overhead;
      int           nesting;
      int           overflow;
      unsigned int  next;
      struct event  events[MAX_EVENT_COUNT];
} event_trace;

/**
 * reset_event_trace() - Resets the event trace.
 */
static void reset_event_trace(void)
{
      event_trace.nesting  = 0;
      event_trace.overflow = false;
      event_trace.next     = 0;
}

/**
 * add_event() - Adds an event to the event trace.
 *
 * @description:  A description of the event.
 * @delta:        A delta to the nesting level for this event [-1, 0, 1].
 */
static inline void add_event(char *description, int delta)
{
      struct event  *event;

      if (!imx_nfc_module_report_events)
            return;

      if (event_trace.overflow)
            return;

      if (event_trace.next >= MAX_EVENT_COUNT) {
            event_trace.overflow = true;
            return;
      }

      event = event_trace.events + event_trace.next;

      event->time = ktime_get();

      event->description = description;

      if (!delta)
            event->nesting = event_trace.nesting;
      else if (delta < 0) {
            event->nesting = event_trace.nesting - 1;
            event_trace.nesting -= 2;
      } else {
            event->nesting = event_trace.nesting + 1;
            event_trace.nesting += 2;
      }

      if (event_trace.nesting < 0)
            event_trace.nesting = 0;

      event_trace.next++;

}

/**
 * add_state_event_l() - Adds an event to display some state.
 *
 * @address:   The address to examine.
 * @mask:      A mask to apply to the contents of the given address.
 * @clear:     An event message to add if the result is zero.
 * @not_zero:  An event message to add if the result is not zero.
 */
static void add_state_event_l(void *address, uint32_t mask,
                                    char *zero, char *not_zero)
{
      int  state;
      state = !!(__raw_readl(address) & mask);
      if (state)
            add_event(not_zero, 0);
      else
            add_event(zero, 0);
}

/**
 * start_event_trace() - Starts an event trace and adds the first event.
 *
 * @description:  A description of the first event.
 */
static void start_event_trace(char *description)
{

      ktime_t  t0;
      ktime_t  t1;

      if (!imx_nfc_module_report_events)
            return;

      reset_event_trace();

      t0 = ktime_get();
      t1 = ktime_get();

      event_trace.overhead = ktime_sub(t1, t0);

      add_event(description, 1);

}

/**
 * dump_event_trace() - Dumps the event trace.
 */
static void dump_event_trace(void)
{
      unsigned int  i;
      time_t        seconds;
      long          nanoseconds;
      char          line[100];
      int           o;
      struct event  *first_event;
      struct event  *last_event;
      struct event  *matching_event;
      struct event  *event;
      ktime_t       delta;

      /* Check if event reporting is turned off. */

      if (!imx_nfc_module_report_events)
            return;

      /* Print important facts about this event trace. */

      printk(KERN_DEBUG "\n+--------------\n");

      printk(KERN_DEBUG "|  Overhead    : [%d:%d]\n",
                        event_trace.overhead.tv.sec,
                        event_trace.overhead.tv.nsec);

      if (!event_trace.next) {
            printk(KERN_DEBUG "|  No Events\n");
            return;
      }

      first_event = event_trace.events;
      last_event  = event_trace.events + (event_trace.next - 1);

      delta = ktime_sub(last_event->time, first_event->time);
      printk(KERN_DEBUG "|  Elapsed Time: [%d:%d]\n",
                                    delta.tv.sec, delta.tv.nsec);

      if (event_trace.overflow)
            printk(KERN_DEBUG "|  Overflow!\n");

      /* Print the events in this history. */

      for (i = 0, event = event_trace.events;
                              i < event_trace.next; i++, event++) {

            /* Get the delta between this event and the previous event. */

            if (!i) {
                  seconds     = 0;
                  nanoseconds = 0;
            } else {
                  delta = ktime_sub(event[0].time, event[-1].time);
                  seconds     = delta.tv.sec;
                  nanoseconds = delta.tv.nsec;
            }

            /* Print the current event. */

            o = 0;

            o = snprintf(line, sizeof(line) - o, "|  [%ld:% 10ld]%*s %s",
                                          seconds, nanoseconds,
                                          event->nesting, "",
                                          event->description);
            /* Check if this is the last event in a nested series. */

            if (i && (event[0].nesting < event[-1].nesting)) {

                  for (matching_event = event - 1;; matching_event--) {

                        if (matching_event < event_trace.events) {
                              matching_event = 0;
                              break;
                        }

                        if (matching_event->nesting == event->nesting)
                              break;

                  }

                  if (matching_event) {
                        delta = ktime_sub(event->time,
                                          matching_event->time);
                        o += snprintf(line + o, sizeof(line) - o,
                                    " <%d:%d]", delta.tv.sec,
                                                delta.tv.nsec);
                  }

            }

            /* Check if this is the first event in a nested series. */

            if ((i < event_trace.next - 1) &&
                        (event[0].nesting < event[1].nesting)) {

                  for (matching_event = event + 1;; matching_event++) {

                        if (matching_event >=
                              (event_trace.events+event_trace.next)) {
                              matching_event = 0;
                              break;
                        }

                        if (matching_event->nesting == event->nesting)
                              break;

                  }

                  if (matching_event) {
                        delta = ktime_sub(matching_event->time,
                                                event->time);
                        o += snprintf(line + o, sizeof(line) - o,
                                    " [%d:%d>", delta.tv.sec,
                                                delta.tv.nsec);
                  }

            }

            printk(KERN_DEBUG "%s\n", line);

      }

      printk(KERN_DEBUG "+--------------\n");

}

/**
 * stop_event_trace() - Stops an event trace.
 *
 * @description:  A description of the last event.
 */
static void stop_event_trace(char *description)
{
      struct event  *event;

      if (!imx_nfc_module_report_events)
            return;

      /*
       * We want the end of the trace, no matter what happens. If the trace
       * has already overflowed, or is about to, just jam this event into the
       * last spot. Otherwise, add this event like any other.
       */

      if (event_trace.overflow || (event_trace.next >= MAX_EVENT_COUNT)) {
            event = event_trace.events + (MAX_EVENT_COUNT - 1);
            event->time = ktime_get();
            event->description = description;
            event->nesting     = 0;
      } else {
            add_event(description, -1);
      }

      dump_event_trace();
      reset_event_trace();

}

#else /* EVENT_REPORTING */

#define start_event_trace(description)                   do {} while (0)
#define add_event(description, delta)                    do {} while (0)
#define add_state_event_l(address, mask, zero, not_zero) do {} while (0)
#define stop_event_trace(description)                    do {} while (0)
#define dump_event_trace()                               do {} while (0)

#endif /* EVENT_REPORTING */

/**
 * unimplemented() - Announces intentionally unimplemented features.
 *
 * @this:  Per-device data.
 * @msg:   A message about the unimplemented feature.
 */
static inline void unimplemented(struct imx_nfc_data *this, const char * msg)
{
      dev_err(this->dev, "Intentionally unimplemented: %s", msg);
}

/**
 * raw_read_mask_w() - Reads masked bits in a 16-bit hardware register.
 */
static inline uint16_t raw_read_mask_w(uint16_t mask, void *address)
{
      return __raw_readw(address) & mask;
}

/**
 * raw_set_mask_w() - Sets bits in a 16-bit hardware register.
 */
static inline void raw_set_mask_w(uint16_t mask, void *address)
{
      __raw_writew(__raw_readw(address) | mask, address);
}

/**
 * raw_clr_mask_w() - Clears bits in a 16-bit hardware register.
 */
static inline void raw_clr_mask_w(uint16_t mask, void *address)
{
      __raw_writew(__raw_readw(address) & (~mask), address);
}

/**
 * raw_read_mask_l() - Reads masked bits in a 32-bit hardware register.
 */
static inline uint32_t raw_read_mask_l(uint32_t mask, void *address)
{
      return __raw_readl(address) & mask;
}

/**
 * raw_set_mask_l() - Sets bits in a 32-bit hardware register.
 */
static inline void raw_set_mask_l(uint32_t mask, void *address)
{
      __raw_writel(__raw_readl(address) | mask, address);
}

/**
 * raw_clr_mask_l() - Clears bits in a 32-bit hardware register.
 */
static inline void raw_clr_mask_l(uint32_t mask, void *address)
{
      __raw_writel(__raw_readl(address) & (~mask), address);
}

/**
 * is_large_page_chip() - Returns true for large page media.
 *
 * @this:  Per-device data.
 */
static inline int is_large_page_chip(struct imx_nfc_data *this)
{
      return (this->physical_geometry.page_data_size > 512);
}

/**
 * is_small_page_chip() - Returns true for small page media.
 *
 * @this:  Per-device data.
 */
static inline int is_small_page_chip(struct imx_nfc_data *this)
{
      return !is_large_page_chip(this);
}

/**
 * get_cycle_in_ns() - Returns the given device's cycle period, in ns.
 *
 * @this:  Per-device data.
 */
static inline unsigned get_cycle_in_ns(struct imx_nfc_data *this)
{
      unsigned long  cycle_in_ns;

      cycle_in_ns = 1000000000 / clk_get_rate(this->clock);

      if (!this->nfc->get_symmetric(this))
            cycle_in_ns *= 2;

      return cycle_in_ns;

}

/**
 * nfc_util_set_best_cycle() - Sets the closest possible NAND Flash bus cycle.
 *
 * This function computes the clock setup that will best approximate the given
 * target Flash bus cycle period.
 *
 * For some NFC versions, we can make the clock "symmetric." When the clock
 * is "symmetric," the hardware waits one NFC clock for every read/write cycle.
 * When the clock is "asymmetric," the hardware waits two NFC clocks for every
 * read/write cycle. Thus, making the clock asymmetric essentially divides the
 * NFC clock by two.
 *
 * We compute the target frequency that matches the given target period. We then
 * discover the closest available match with that frequency and the closest
 * available match with double that frequency (for use with an asymmetric
 * clock). We implement the best choice of original clock and symmetric or
 * asymmetric setting, preferring symmetric clocks.
 *
 * @this:      Per-device data.
 * @ns:        The target cycle period, in nanoseconds.
 * @no_asym:   Disallow making the clock asymmetric.
 * @no_sym:    Disallow making the clock  symmetric.
 */
static int nfc_util_set_best_cycle(struct imx_nfc_data *this,
                        unsigned int ns, int no_asym, int no_sym)
{
      unsigned long  target_hz;
      long           symmetric_hz;
      long           symmetric_delta_hz;
      long           asymmetric_hz;
      long           asymmetric_delta_hz;
      unsigned long  best_hz;
      int            best_symmetry_setting;
      struct device  *dev = this->dev;

      /* The target cycle period must be greater than zero. */

      if (!ns)
            return -EINVAL;

      /* Compute the target frequency. */

      target_hz = 1000000000 / ns;

      /* Find out how close we can get with a symmetric clock. */

      if (!no_sym && this->nfc->can_be_symmetric)
            symmetric_hz = clk_round_rate(this->clock, target_hz);
      else
            symmetric_hz = -EINVAL;

      /* Find out how close we can get with an asymmetric clock. */

      if (!no_asym)
            asymmetric_hz = clk_round_rate(this->clock, target_hz * 2);
      else
            asymmetric_hz = -EINVAL;

      /* Does anything work at all? */

      if ((symmetric_hz == -EINVAL) && (asymmetric_hz == -EINVAL)) {
            dev_err(dev, "Can't support Flash bus cycle of %uns\n", ns);
            return -EINVAL;
      }

      /* Discover the best match. */

      if ((symmetric_hz != -EINVAL) && (asymmetric_hz != -EINVAL)) {

            symmetric_delta_hz  = target_hz - symmetric_hz;
            asymmetric_delta_hz = target_hz - (asymmetric_hz / 2);

            if (symmetric_delta_hz <= asymmetric_delta_hz)
                  best_symmetry_setting = true;
            else
                  best_symmetry_setting = false;

      } else if (symmetric_hz != -EINVAL) {
            best_symmetry_setting = true;
      } else {
            best_symmetry_setting = false;
      }

      best_hz = best_symmetry_setting ? symmetric_hz : asymmetric_hz;

      /* Implement the best match. */

      this->nfc->set_symmetric(this, best_symmetry_setting);

      return clk_set_rate(this->clock, best_hz);

}

/**
 * nfc_util_wait_for_the_nfc() - Waits for the NFC to finish an operation.
 *
 * @this:     Per-device data.
 * @use_irq:  Indicates that we should wait for an interrupt rather than polling
 *            and delaying.
 */
static void nfc_util_wait_for_the_nfc(struct imx_nfc_data *this, int use_irq)
{
      unsigned       spin_count;
      struct device  *dev = this->dev;

      /* Apply the override, if any. */

      switch (this->interrupt_override) {

      case NEVER:
            use_irq = false;
            break;

      case DRIVER_CHOICE:
            break;

      case ALWAYS:
            use_irq = true;
            break;

      }

      /* Check if we're using interrupts. */

      if (use_irq) {

            /*
             * If control arrives here, the caller wants to use interrupts.
             * Presumably, this operation is known to take a very long time.
             */

            if (this->nfc->is_interrupting(this)) {
                  add_event("Waiting for the NFC (early interrupt)", 1);
                  this->nfc->clear_interrupt(this);
            } else {
                  add_event("Waiting for the NFC (interrupt)", 1);
                  this->nfc->unmask_interrupt(this);
                  wait_for_completion(&this->done);
            }

            add_event("NFC done", -1);

      } else {

            /*
             * If control arrives here, the caller doesn't want to use
             * interrupts. Presumably, this operation is too quick to
             * justify the overhead. Leave the interrupt masked, and loop
             * until the interrupt bit lights up, or we time out.
             *
             * We spin for a maximum of about 2ms before declaring a time
             * out. No operation we could possibly spin on should take that
             * long.
             */

            spin_count = 2000;

            add_event("Waiting for the NFC (polling)", 1);

            for (; spin_count > 0; spin_count--) {

                  if (this->nfc->is_interrupting(this)) {
                        this->nfc->clear_interrupt(this);
                        add_event("NFC done", -1);
                        return;
                  }

                  udelay(1);

            }

            /* Timed out. */

            add_event("Timed out", -1);

            dev_err(dev, "[wait_for_the_nfc] ===== Time Out =====\n");
            dump_event_trace();

      }

}

/**
 *  nfc_util_bytewise_copy_from_nfc_mem() - Copies bytes from the NFC memory.
 *
 * @from:  A pointer to the source memory.
 * @to:    A pointer to the destination memory.
 * @size:  The number of bytes to copy.
 */
static void nfc_util_bytewise_copy_from_nfc_mem(const void *from,
                                          void *to, size_t n)
{
      unsigned int   i;
      const uint8_t  *f = from;
      uint8_t        *t = to;
      uint16_t       *p;
      uint16_t       x;

      for (i = 0; i < n; i++, f++, t++) {

            p = (uint16_t *) (((unsigned long) f) & ~((unsigned long) 1));

            x = __raw_readw(p);

            if (((unsigned long) f) & 0x1)
                  *t = (x >> 8) & 0xff;
            else
                  *t = (x >> 0) & 0xff;

      }

}

/**
 * nfc_util_bytewise_copy_to_nfc_mem() - Copies bytes to the NFC memory.
 *
 * @from:  A pointer to the source memory.
 * @to:    A pointer to the destination memory.
 * @size:  The number of bytes to copy.
 */
static void nfc_util_bytewise_copy_to_nfc_mem(const void *from,
                                          void *to, size_t n)
{
      unsigned int   i;
      const uint8_t  *f = from;
      uint8_t        *t = to;
      uint16_t       *p;
      uint16_t       x;

      for (i = 0; i < n; i++, f++, t++) {

            p = (uint16_t *) (((unsigned long) t) & ~((unsigned long) 1));

            x = __raw_readw(p);

            if (((unsigned long) t) & 0x1)
                  ((uint8_t *)(&x))[1] = *f;
            else
                  ((uint8_t *)(&x))[0] = *f;

            __raw_writew(x, p);

      }

}

/**
 * nfc_util_copy_from_nfc_mem() - Copies from the NFC memory to main memory.
 *
 * @from:  A pointer to the source memory.
 * @to:    A pointer to the destination memory.
 * @size:  The number of bytes to copy.
 */
static void nfc_util_copy_from_nfc_mem(const void *from, void *to, size_t n)
{
      unsigned int  chunk_count;

      /*
       * Check if we're testing bytewise copies.
       */

      if (imx_nfc_module_force_bytewise_copy)
            goto force_bytewise_copy;

      /*
       * We'd like to use memcpy to get data out of the NFC but, because that
       * memory responds only to 16- and 32-byte reads, we can only do so
       * safely if both the start and end of both the source and destination
       * are perfectly aligned on 4-byte boundaries.
       */

      if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {

            /*
             * If control arrives here, both the source and destination are
             * aligned on 4-byte boundaries. Compute the number of whole,
             * 4-byte chunks we can move.
             */

            chunk_count = n / 4;

            /*
             * Move all the chunks we can, and then update the pointers and
             * byte count to show what's left.
             */

            if (chunk_count) {
                  memcpy(to, from, chunk_count * 4);
                  from += chunk_count * 4;
                  to   += chunk_count * 4;
                  n    -= chunk_count * 4;
            }

      }

      /*
       * Move what's left.
       */

force_bytewise_copy:

      nfc_util_bytewise_copy_from_nfc_mem(from, to, n);

}

/**
 * nfc_util_copy_to_nfc_mem() - Copies from main memory to the NFC memory.
 *
 * @from:  A pointer to the source memory.
 * @to:    A pointer to the destination memory.
 * @size:  The number of bytes to copy.
 */
static void nfc_util_copy_to_nfc_mem(const void *from, void *to, size_t n)
{
      unsigned int  chunk_count;

      /*
       * Check if we're testing bytewise copies.
       */

      if (imx_nfc_module_force_bytewise_copy)
            goto force_bytewise_copy;

      /*
       * We'd like to use memcpy to get data into the NFC but, because that
       * memory responds only to 16- and 32-byte writes, we can only do so
       * safely if both the start and end of both the source and destination
       * are perfectly aligned on 4-byte boundaries.
       */

      if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {

            /*
             * If control arrives here, both the source and destination are
             * aligned on 4-byte boundaries. Compute the number of whole,
             * 4-byte chunks we can move.
             */

            chunk_count = n / 4;

            /*
             * Move all the chunks we can, and then update the pointers and
             * byte count to show what's left.
             */

            if (chunk_count) {
                  memcpy(to, from, chunk_count * 4);
                  from += chunk_count * 4;
                  to   += chunk_count * 4;
                  n    -= chunk_count * 4;
            }

      }

      /*
       * Move what's left.
       */

force_bytewise_copy:

      nfc_util_bytewise_copy_to_nfc_mem(from, to, n);

}

/**
 * nfc_util_copy_from_the_nfc() - Copies bytes out of the NFC.
 *
 * This function makes the data in the NFC look like a contiguous, model page.
 *
 * @this:   Per-device data.
 * @start:  The index of the starting byte in the NFC.
 * @buf:    A pointer to the destination buffer.
 * @len:    The number of bytes to copy out.
 */
static void nfc_util_copy_from_the_nfc(struct imx_nfc_data *this,
                  unsigned int start, uint8_t *buf, unsigned int len)
{
      unsigned int         i;
      unsigned int         count;
      unsigned int         offset;
      unsigned int         data_size;
      unsigned int         oob_size;
      unsigned int         total_size;
      void                 *spare_base;
      unsigned int         first_spare;
      void                 *from;
      struct nfc_geometry  *geometry = this->nfc_geometry;

      /*
       * During initialization, the HIL will attempt to read ID bytes. For
       * some NFC hardware versions, the ID bytes are deposited in the NFC
       * memory, so this function will be called to deliver them. At that
       * point, we won't know the NFC geometry. That's OK because we're only
       * going to be reading a byte at a time.
       *
       * If we don't yet know the NFC geometry, just plug in some values that
       * make things work for now.
       */

      if (unlikely(!geometry)) {
            data_size = NFC_MAIN_BUF_SIZE;
            oob_size  = 0;
      } else {
            data_size = geometry->page_data_size;
            oob_size  = geometry->page_oob_size;
      }

      total_size = data_size + oob_size;

      /* Validate. */

      if ((start >= total_size) || ((start + len) > total_size)) {
            dev_err(this->dev, "Bad copy from NFC memory: [%u, %u]\n",
                                                start, len);
            return;
      }

      /* Check if we're copying anything at all. */

      if (!len)
            return;

      /* Check if anything comes from the main area. */

      if (start < data_size) {

            /* Compute the bytes to copy from the main area. */

            count = min(len, data_size - start);

            /* Copy. */

            nfc_util_copy_from_nfc_mem(this->buffers + start, buf, count);

            buf   += count;
            start += count;
            len   -= count;

      }

      /* Check if we're done. */

      if (!len)
            return;

      /* Compute the base address of the spare buffers. */

      spare_base = this->buffers +
                  (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);

      /* Discover in which spare buffer the copying begins. */

      first_spare = (start - data_size) / geometry->spare_buf_size;

      /* Check if anything comes from the regular spare buffer area. */

      if (first_spare < geometry->buffer_count) {

            /* Start copying from spare buffers. */

            for (i = first_spare; i < geometry->buffer_count; i++) {

                  /* Compute the offset into this spare area. */

                  offset = start -
                        (data_size + (geometry->spare_buf_size * i));

                  /* Compute the address of that offset. */

                  from = spare_base + offset +
                              (this->nfc->spare_buf_stride * i);

                  /* Compute the bytes to copy from this spare area. */

                  count = min(len, geometry->spare_buf_size - offset);

                  /* Copy. */

                  nfc_util_copy_from_nfc_mem(from, buf, count);

                  buf   += count;
                  start += count;
                  len   -= count;

            }

      }

      /* Check if we're done. */

      if (!len)
            return;

      /* Compute the offset into the extra spare area. */

      offset = start -
            (data_size + (geometry->spare_buf_size*geometry->buffer_count));

      /* Compute the address of that offset. */

      from = spare_base + offset +
                  (this->nfc->spare_buf_stride * geometry->buffer_count);

      /* Compute the bytes to copy from the extra spare area. */

      count = min(len, geometry->spare_buf_spill - offset);

      /* Copy. */

      nfc_util_copy_from_nfc_mem(from, buf, count);

}

/**
 * nfc_util_copy_to_the_nfc() - Copies bytes into the NFC memory.
 *
 * This function makes the data in the NFC look like a contiguous, model page.
 *
 * @this:   Per-device data.
 * @buf:   A pointer to the source buffer.
 * @start: The index of the starting byte in the NFC memory.
 * @len:   The number of bytes to copy in.
 */
static void nfc_util_copy_to_the_nfc(struct imx_nfc_data *this,
            const uint8_t *buf, unsigned int start, unsigned int len)
{
      unsigned int         i;
      unsigned int         count;
      unsigned int         offset;
      unsigned int         data_size;
      unsigned int         oob_size;
      unsigned int         total_size;
      void                 *spare_base;
      unsigned int         first_spare;
      void                 *to;
      struct nfc_geometry  *geometry = this->nfc_geometry;

      /* Establish some important facts. */

      data_size  = geometry->page_data_size;
      oob_size   = geometry->page_oob_size;
      total_size = data_size + oob_size;

      /* Validate. */

      if ((start >= total_size) || ((start + len) > total_size)) {
            dev_err(this->dev, "Bad copy to NFC memory: [%u, %u]\n",
                                                start, len);
            return;
      }

      /* Check if we're copying anything at all. */

      if (!len)
            return;

      /* Check if anything goes to the main area. */

      if (start < data_size) {

            /* Compute the bytes to copy to the main area. */

            count = min(len, data_size - start);

            /* Copy. */

            nfc_util_copy_to_nfc_mem(buf, this->buffers + start, count);

            buf   += count;
            start += count;
            len   -= count;

      }

      /* Check if we're done. */

      if (!len)
            return;

      /* Compute the base address of the spare buffers. */

      spare_base = this->buffers +
                  (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);

      /* Discover in which spare buffer the copying begins. */

      first_spare = (start - data_size) / geometry->spare_buf_size;

      /* Check if anything goes to the regular spare buffer area. */

      if (first_spare < geometry->buffer_count) {

            /* Start copying to spare buffers. */

            for (i = first_spare; i < geometry->buffer_count; i++) {

                  /* Compute the offset into this spare area. */

                  offset = start -
                        (data_size + (geometry->spare_buf_size * i));

                  /* Compute the address of that offset. */

                  to = spare_base + offset +
                              (this->nfc->spare_buf_stride * i);

                  /* Compute the bytes to copy to this spare area. */

                  count = min(len, geometry->spare_buf_size - offset);

                  /* Copy. */

                  nfc_util_copy_to_nfc_mem(buf, to, count);

                  buf   += count;
                  start += count;
                  len   -= count;

            }

      }

      /* Check if we're done. */

      if (!len)
            return;

      /* Compute the offset into the extra spare area. */

      offset = start -
            (data_size + (geometry->spare_buf_size*geometry->buffer_count));

      /* Compute the address of that offset. */

      to = spare_base + offset +
                  (this->nfc->spare_buf_stride * geometry->buffer_count);

      /* Compute the bytes to copy to the extra spare area. */

      count = min(len, geometry->spare_buf_spill - offset);

      /* Copy. */

      nfc_util_copy_to_nfc_mem(buf, to, count);

}

/**
 * nfc_util_isr() - i.MX NFC ISR.
 *
 * @irq:      The arriving interrupt number.
 * @context:  A cookie for this ISR.
 */
static irqreturn_t nfc_util_isr(int irq, void *cookie)
{
      struct imx_nfc_data  *this = cookie;
      this->nfc->mask_interrupt(this);
      this->nfc->clear_interrupt(this);
      complete(&this->done);
      return IRQ_HANDLED;
}

/**
 * nfc_util_send_cmd() - Sends a command to the current chip, without waiting.
 *
 * @this:     Per-device data.
 * @command:  The command code.
 */

static void nfc_util_send_cmd(struct imx_nfc_data *this, unsigned int command)
{

      add_event("Entering nfc_util_send_cmd", 1);

      this->nfc->command_cycle(this, command);

      add_event("Exiting nfc_util_send_cmd", -1);

}

/**
 * nfc_util_send_cmd_and_addrs() - Sends a cmd and addrs to the current chip.
 *
 * This function conveniently combines sending a command, and then sending
 * optional addresses, waiting for the NFC to finish will all steps.
 *
 * @this:     Per-device data.
 * @command:  The command code.
 * @column:   The column address to send, or -1 if no column address applies.
 * @page:     The page address to send, or -1 if no page address applies.
 */

static void nfc_util_send_cmd_and_addrs(struct imx_nfc_data *this,
                              unsigned command, int column, int page)
{
      uint32_t  page_mask;

      add_event("Entering nfc_util_send_cmd_and_addrs", 1);

      /* Send the command.*/

      add_event("Sending the command...", 0);

      nfc_util_send_cmd(this, command);

      nfc_util_wait_for_the_nfc(this, false);

      /* Send the addresses. */

      add_event("Sending the addresses...", 0);

      if (column != -1) {

            this->nfc->write_cycle(this, (column >> 0) & 0xff);

            if (is_large_page_chip(this))
                  this->nfc->write_cycle(this, (column >> 8) & 0xff);

      }

      if (page != -1) {

            page_mask = this->nand.pagemask;

            do {
                  this->nfc->write_cycle(this, page & 0xff);
                  page_mask >>= 8;
                  page      >>= 8;
            } while (page_mask != 0);

      }

      add_event("Exiting nfc_util_send_cmd_and_addrs", -1);

}

/**
 * nfc_2_x_exit() - Version-specific shut down.
 *
 * @this:  Per-device data.
 */
static void nfc_2_x_exit(struct imx_nfc_data *this)
{
}

/**
 * nfc_2_x_clear_interrupt() - Clears an interrupt.
 *
 * @this:  Per-device data.
 */
static void nfc_2_x_clear_interrupt(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;
      raw_clr_mask_w(NFC_2_X_CONFIG2_INT_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
}

/**
 * nfc_2_x_is_interrupting() - Returns the interrupt bit status.
 *
 * @this:  Per-device data.
 */
static int nfc_2_x_is_interrupting(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;
      return raw_read_mask_w(NFC_2_X_CONFIG2_INT_MSK,
                                    base + NFC_2_X_CONFIG2_REG_OFF);
}

/**
 * nfc_2_x_command_cycle() - Sends a command.
 *
 * @this:     Per-device data.
 * @command:  The command code.
 */
static void nfc_2_x_command_cycle(struct imx_nfc_data *this, unsigned command)
{
      void  *base = this->primary_regs;

      /* Write the command we want to send. */

      __raw_writew(command, base + NFC_2_X_FLASH_CMD_REG_OFF);

      /* Launch a command cycle. */

      __raw_writew(NFC_2_X_CONFIG2_FCMD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);

}

/**
 * nfc_2_x_write_cycle() - Writes a single byte.
 *
 * @this:  Per-device data.
 * @byte:  The byte.
 */
static void nfc_2_x_write_cycle(struct imx_nfc_data *this, unsigned int byte)
{
      void  *base = this->primary_regs;

      /* Give the NFC the byte we want to write. */

      __raw_writew(byte, base + NFC_2_X_FLASH_ADDR_REG_OFF);

      /* Launch an address cycle.
       *
       * This is *sort* of a hack, but not really. The intent of the NFC
       * design is for this operation to send an address byte. In fact, the
       * NFC neither knows nor cares what we're sending. It justs runs a write
       * cycle.
       */

      __raw_writew(NFC_2_X_CONFIG2_FADD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, false);

}

/**
 * nfc_2_0_init() - Version-specific hardware initialization.
 *
 * @this:  Per-device data.
 */
static int nfc_2_0_init(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;

      /* Initialize the interrupt machinery. */

      this->nfc->mask_interrupt(this);
      this->nfc->clear_interrupt(this);

      /* Unlock the NFC memory. */

      __raw_writew(0x2, base + NFC_2_X_CONFIG_REG_OFF);

      /* Set the unlocked block range to cover the entire medium. */

      __raw_writew(0 , base + NFC_2_0_UNLOCK_START_REG_OFF);
      __raw_writew(~0, base + NFC_2_0_UNLOCK_END_REG_OFF);

      /* Unlock all blocks. */

      __raw_writew(0x4, base + NFC_2_X_WR_PROT_REG_OFF);

      /* Return success. */

      return 0;

}

/**
 * nfc_2_0_mask_interrupt() - Masks interrupts.
 *
 * @this:  Per-device data.
 */
static void nfc_2_0_mask_interrupt(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;
      raw_set_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);
}

/**
 * nfc_2_0_unmask_interrupt() - Unmasks interrupts.
 *
 * @this:  Per-device data.
 */
static void nfc_2_0_unmask_interrupt(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;
      raw_clr_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);
}

/**
 * nfc_2_0_set_ecc() - Turns ECC on or off.
 *
 * @this:  Per-device data.
 * @on:    Indicates if ECC should be on or off.
 */
static void nfc_2_0_set_ecc(struct imx_nfc_data *this, int on)
{
      void  *base = this->primary_regs;

      if (on)
            raw_set_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);
      else
            raw_clr_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);

}

/**
 * nfc_2_0_get_ecc_status() - Reports ECC errors.
 *
 * @this:  Per-device data.
 */
static int nfc_2_0_get_ecc_status(struct imx_nfc_data *this)
{
      unsigned int  i;
      void          *base = this->primary_regs;
      uint16_t      status_reg;
      unsigned int  buffer_status[4];
      int           status;

      /* Get the entire status register. */

      status_reg = __raw_readw(base + NFC_2_0_ECC_STATUS_REG_OFF);

      /* Pick out the status for each buffer. */

      buffer_status[0] = (status_reg & NFC_2_0_ECC_STATUS_NOSER1_MSK)
                              >> NFC_2_0_ECC_STATUS_NOSER1_POS;

      buffer_status[1] = (status_reg & NFC_2_0_ECC_STATUS_NOSER2_MSK)
                              >> NFC_2_0_ECC_STATUS_NOSER2_POS;

      buffer_status[2] = (status_reg & NFC_2_0_ECC_STATUS_NOSER3_MSK)
                              >> NFC_2_0_ECC_STATUS_NOSER3_POS;

      buffer_status[3] = (status_reg & NFC_2_0_ECC_STATUS_NOSER4_MSK)
                              >> NFC_2_0_ECC_STATUS_NOSER4_POS;

      /* Loop through the buffers we're actually using. */

      status = 0;

      for (i = 0; i < this->nfc_geometry->buffer_count; i++) {

            if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
                  status = -1;
                  break;
            }

            status += buffer_status[i];

      }

      /* Return the final result. */

      return status;

}

/**
 * nfc_2_0_get_symmetric() - Indicates if the clock is symmetric.
 *
 * @this:  Per-device data.
 */
static int nfc_2_0_get_symmetric(struct imx_nfc_data *this)
{
      void  *base = this->primary_regs;

      return !!raw_read_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);

}

/**
 * nfc_2_0_set_symmetric() - Turns symmetric clock mode on or off.
 *
 * @this:  Per-device data.
 */
static void nfc_2_0_set_symmetric(struct imx_nfc_data *this, int on)
{
      void  *base = this->primary_regs;

      if (on)
            raw_set_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);
      else
            raw_clr_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
                                    base + NFC_2_0_CONFIG1_REG_OFF);

}

/**
 * nfc_2_0_set_geometry() - Configures for the medium geometry.
 *
 * @this:  Per-device data.
 */
static int nfc_2_0_set_geometry(struct imx_nfc_data *this)
{
      struct physical_geometry  *physical = &this->physical_geometry;

      /* Select an NFC geometry. */

      switch (physical->page_data_size) {

      case 512:
            this->nfc_geometry = &nfc_geometry_512_16_RS_ECC4;
            break;

      case 2048:
            this->nfc_geometry = &nfc_geometry_2K_64_RS_ECC4;
            break;

      default:
            dev_err(this->dev, "NFC can't handle page size: %u",
                                    physical->page_data_size);
            return !0;
            break;

      }

      /*
       * This NFC version receives page size information from a register
       * that's external to the NFC. We must rely on platform-specific code
       * to set this register for us.
       */

      return this->pdata->set_page_size(physical->page_data_size);

}

/**
 * nfc_2_0_select_chip() - Selects the current chip.
 *
 * @this:  Per-device data.
 * @chip:  The chip number to select, or -1 to select no chip.
 */
static void nfc_2_0_select_chip(struct imx_nfc_data *this, int chip)
{
}

/**
 * nfc_2_0_read_cycle() - Applies a single read cycle to the current chip.
 *
 * @this:  Per-device data.
 */
static unsigned int nfc_2_0_read_cycle(struct imx_nfc_data *this)
{
      uint8_t       byte;
      unsigned int  result;
      void          *base = this->primary_regs;

      /* Read into main buffer 0. */

      __raw_writew(0x0, base + NFC_2_0_BUF_ADDR_REG_OFF);

      /* Launch a "Data Out" operation. */

      __raw_writew(0x4 << NFC_2_X_CONFIG2_FDO_POS,
                                    base + NFC_2_X_CONFIG2_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, false);

      /* Get the result from the NFC memory. */

      nfc_util_copy_from_the_nfc(this, 0, &byte, 1);
      result = byte;

      /* Return the results. */

      return result;

}

/**
 * nfc_2_0_read_page() - Reads a page from the current chip into the NFC.
 *
 * @this:  Per-device data.
 */
static void nfc_2_0_read_page(struct imx_nfc_data *this)
{
      unsigned int  i;
      void          *base = this->primary_regs;

      /* Loop over the number of buffers in use. */

      for (i = 0; i < this->nfc_geometry->buffer_count; i++) {

            /* Make the NFC read into the current buffer. */

            __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
                              base + NFC_2_0_BUF_ADDR_REG_OFF);

            /* Launch a page data out operation. */

            __raw_writew(0x1 << NFC_2_X_CONFIG2_FDO_POS,
                              base + NFC_2_X_CONFIG2_REG_OFF);

            /* Wait for the NFC to finish. */

            nfc_util_wait_for_the_nfc(this, true);

      }

}

/**
 * nfc_2_0_send_page() - Sends a page from the NFC to the current chip.
 *
 * @this:  Per-device data.
 */
static void nfc_2_0_send_page(struct imx_nfc_data *this)
{
      unsigned int  i;
      void          *base = this->primary_regs;

      /* Loop over the number of buffers in use. */

      for (i = 0; i < this->nfc_geometry->buffer_count; i++) {

            /* Make the NFC send from the current buffer. */

            __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
                              base + NFC_2_0_BUF_ADDR_REG_OFF);

            /* Launch a page data in operation. */

            __raw_writew(0x1 << NFC_2_X_CONFIG2_FDI_POS,
                              base + NFC_2_X_CONFIG2_REG_OFF);

            /* Wait for the NFC to finish. */

            nfc_util_wait_for_the_nfc(this, true);

      }

}

/**
 * nfc_3_2_init() - Hardware initialization.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_init(struct imx_nfc_data *this)
{
      int           error;
      unsigned int  no_sdma;
      unsigned int  fmp;
      unsigned int  rbb_mode;
      unsigned int  num_of_devices;
      unsigned int  dma_mode;
      unsigned int  sbb;
      unsigned int  nf_big;
      unsigned int  sb2r;
      unsigned int  fw;
      unsigned int  too;
      unsigned int  add_op;
      uint32_t      config3;
      void          *primary_base   = this->primary_regs;
      void          *secondary_base = this->secondary_regs;

      /* Initialize the interrupt machinery. */

      this->nfc->mask_interrupt(this);
      this->nfc->clear_interrupt(this);

      /* Set up the clock. */

      error = this->nfc->set_closest_cycle(this,
                              this->pdata->target_cycle_in_ns);

      if (error)
            return !0;

      /* We never read the spare area alone. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_SP_EN_MSK,
                        primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Tell the NFC the "Read Status" command code. */

      raw_clr_mask_l(NFC_3_2_CONFIG2_ST_CMD_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

      raw_set_mask_l(NAND_CMD_STATUS << NFC_3_2_CONFIG2_ST_CMD_POS,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

      /*
       * According to erratum ENGcm09051, the CONFIG3 register doesn't reset
       * correctly, so we need to re-build the entire register just in case.
       */

      /*
       * Set the NO_SDMA bit to tell the NFC that we are NOT using SDMA. If
       * you clear this bit (to indicates you *are* using SDMA), but you
       * don't actually set up SDMA, the NFC has been observed to crash the
       * hardware when it asserts its DMA request signals. In the future, we
       * *may* use SDMA, but it's not worth the effort at this writing.
       */

      no_sdma = 0x1;

      /*
       * Set the default FIFO Mode Protection (128 bytes). FMP doesn't work if
       * the NO_SDMA bit is set.
       */

      fmp = 0x2;

      /*
       * The rbb_mode bit determines how the NFC figures out whether chips are
       * ready during automatic operations only (this has no effect on atomic
       * operations). The two choices are either to monitor the ready/busy
       * signals, or to read the status register. We monitor the ready/busy
       * signals.
       */

      rbb_mode = 0x1;

      /*
       * We don't yet know how many devices are connected. We'll find out in
       * out in nfc_3_2_set_geometry().
       */

      num_of_devices = 0;

      /* Set the default DMA mode. */

      dma_mode = 0x0;

      /* Set the default status busy bit. */

      sbb = 0x6;

      /* Little-endian (the default). */

      nf_big = 0x0;

      /* Set the default (standard) status bit to record. */

      sb2r = 0x0;

      /* We support only 8-bit Flash bus width. */

      fw = 0x1;

      /* We don't support "two-on-one." */

      too = 0x0;

      /* Set the addressing option. */

      add_op = 0x3;

      /* Set the CONFIG3 register. */

      config3 = 0;

      config3 |= no_sdma        << NFC_3_2_CONFIG3_NO_SDMA_POS;
      config3 |= fmp            << NFC_3_2_CONFIG3_FMP_POS;
      config3 |= rbb_mode       << NFC_3_2_CONFIG3_RBB_MODE_POS;
      config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
      config3 |= dma_mode       << NFC_3_2_CONFIG3_DMA_MODE_POS;
      config3 |= sbb            << NFC_3_2_CONFIG3_SBB_POS;
      config3 |= nf_big         << NFC_3_2_CONFIG3_NF_BIG_POS;
      config3 |= sb2r           << NFC_3_2_CONFIG3_SB2R_POS;
      config3 |= fw             << NFC_3_2_CONFIG3_FW_POS;
      config3 |= too            << NFC_3_2_CONFIG3_TOO_POS;
      config3 |= add_op         << NFC_3_2_CONFIG3_ADD_OP_POS;

      __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);

      /* Return success. */

      return 0;

}

/**
 * nfc_3_2_set_geometry() - Configures for the medium geometry.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_set_geometry(struct imx_nfc_data *this)
{
      unsigned int              ps;
      unsigned int              cmd_phases;
      unsigned int              pages_per_chip;
      unsigned int              addr_phases0;
      unsigned int              addr_phases1;
      unsigned int              pages_per_block;
      unsigned int              ecc_mode;
      unsigned int              ppb;
      unsigned int              spas;
      unsigned int              mask;
      uint32_t                  config2;
      unsigned int              num_of_devices;
      uint32_t                  config3;
      unsigned int              x;
      unsigned int              chip;
      struct physical_geometry  *physical = &this->physical_geometry;
      void                      *secondary_base = this->secondary_regs;

      /*
       * Select an NFC geometry based on the physical geometry and the
       * capabilities of this NFC.
       */

      switch (physical->page_data_size) {

      case 512:
            this->nfc_geometry = &nfc_geometry_512_16_BCH_ECC4;
            ps = 0;
            break;

      case 2048:
            this->nfc_geometry = &nfc_geometry_2K_64_BCH_ECC4;
            ps = 1;
            break;

      case 4096:

            switch (this->physical_geometry.page_oob_size) {

            case 128:
                  this->nfc_geometry = &nfc_geometry_4K_128_BCH_ECC4;
                  break;

            case 218:
                  this->nfc_geometry = &nfc_geometry_4K_218_BCH_ECC8;
                  break;

            default:
                  dev_err(this->dev,
                        "NFC can't handle page geometry: %u+%u",
                        physical->page_data_size,
                        physical->page_oob_size);
                  return !0;
                  break;

            }

            ps = 2;

            break;

      default:
            dev_err(this->dev, "NFC can't handle page size: %u",
                                    physical->page_data_size);
            return !0;
            break;

      }

      /* Compute the ECC mode. */

      switch (this->nfc_geometry->ecc_strength) {

      case 4:
            ecc_mode = 0;
            break;

      case 8:
            ecc_mode = 1;
            break;

      default:
            dev_err(this->dev, "NFC can't handle ECC strength: %u",
                              this->nfc_geometry->ecc_strength);
            return !0;
            break;

      }

      /* Compute the pages per block. */

      pages_per_block = physical->block_size / physical->page_data_size;

      switch (pages_per_block) {
      case 32:
            ppb = 0;
            break;
      case 64:
            ppb = 1;
            break;
      case 128:
            ppb = 2;
            break;
      case 256:
            ppb = 3;
            break;
      default:
            dev_err(this->dev, "NFC can't handle pages per block: %d",
                                          pages_per_block);
            return !0;
            break;
      }

      /*
       * The hardware needs to know the physical size of the spare area, in
       * units of half-words (16 bits). This may be different from the amount
       * of the spare area we actually expose to MTD. For example, for for
       * 2K+112, we only expose 64 spare bytes, but the hardware needs to know
       * the real facts.
       */

      spas = this->physical_geometry.page_oob_size >> 1;

      /*
       * The number of command phases needed to read a page is directly
       * dependent on whether this is a small page or large page device. Large
       * page devices need more address phases, terminated by a second command
       * phase.
       */

      cmd_phases = is_large_page_chip(this) ? 1 : 0;

      /*
       * The num_adr_phases1 field contains the number of phases needed to
       * transmit addresses for read and program operations. This is the sum
       * of the number of phases for a page address and the number of phases
       * for a column address.
       *
       * The number of phases for a page address is the number of bytes needed
       * to contain a page address.
       *
       * The number of phases for a column address is the number of bytes
       * needed to contain a column address.
       *
       * After computing the sum, we subtract three because a value of zero in
       * this field indicates three address phases, and this is the minimum
       * number of phases the hardware can comprehend.
       *
       * We compute the number of phases based on the *physical* geometry, not
       * the NFC geometry. For example, even if we are treating a very large
       * device as if it contains fewer pages than it actually does, the
       * hardware still needs the additional address phases.
       */

      pages_per_chip =
            physical->chip_size >> (fls(physical->page_data_size) - 1);

      addr_phases1 = (fls(pages_per_chip) >> 3) + 1;

      addr_phases1 += (fls(physical->page_data_size) >> 3) + 1;

      addr_phases1 -= 3;

      /*
       * The num_adr_phases0 field contains the number of phases needed to
       * transmit a page address for an erase operation. That is, this is
       * the value of addr_phases1, less the number of phases for the column
       * address.
       *
       * The hardware expresses this phase count as one or two cycles less
       * than the count indicated by add_phases1 (see the reference manual).
       */

      addr_phases0 = is_large_page_chip(this) ? 1 : 0;

      /* Set the CONFIG2 register. */

      mask =
            NFC_3_2_CONFIG2_PS_MSK           |
            NFC_3_2_CONFIG2_CMD_PHASES_MSK   |
            NFC_3_2_CONFIG2_ADDR_PHASES0_MSK |
            NFC_3_2_CONFIG2_ECC_MODE_MSK     |
            NFC_3_2_CONFIG2_PPB_MSK          |
            NFC_3_2_CONFIG2_ADDR_PHASES1_MSK |
            NFC_3_2_CONFIG2_SPAS_MSK         ;

      config2 = __raw_readl(secondary_base + NFC_3_2_CONFIG2_REG_OFF);

      config2 &= ~mask;

      config2 |= ps           << NFC_3_2_CONFIG2_PS_POS;
      config2 |= cmd_phases   << NFC_3_2_CONFIG2_CMD_PHASES_POS;
      config2 |= addr_phases0 << NFC_3_2_CONFIG2_ADDR_PHASES0_POS;
      config2 |= ecc_mode     << NFC_3_2_CONFIG2_ECC_MODE_POS;
      config2 |= ppb          << NFC_3_2_CONFIG2_PPB_POS;
      config2 |= addr_phases1 << NFC_3_2_CONFIG2_ADDR_PHASES1_POS;
      config2 |= spas         << NFC_3_2_CONFIG2_SPAS_POS;

      config2 = __raw_writel(config2,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

      /*
       * Compute the num_of_devices field.
       *
       * It's very important to set this field correctly. This controls the
       * set of ready/busy lines to which the NFC listens with automatic
       * transactions. If this number is too large, the NFC will listen to
       * ready/busy signals that are electrically floating, or it will try to
       * read the status registers of chips that don't exist. Conversely, if
       * the number is too small, the NFC could believe an operation is
       * finished when some chips are still busy.
       */

      num_of_devices = physical->chip_count - 1;

      /* Set the CONFIG3 register. */

      mask = NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK;

      config3 = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);

      config3 &= ~mask;

      config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;

      __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);

      /*
       * Check if the physical chip count is a power of 2. If not, then
       * automatic operations aren't available. This is because we use an
       * addressing option (see the ADD_OP field of CONFIG3) that requires
       * a number of chips that is a power of 2.
       */

      if (ffs(physical->chip_count) != fls(physical->chip_count)) {
            this->nfc->start_auto_read  = 0;
            this->nfc->start_auto_write = 0;
            this->nfc->start_auto_erase = 0;
      }

      /* Unlock the NFC RAM. */

      x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
      x &= ~NFC_3_2_WRPROT_BLS_MSK;
      x |= 0x2  << NFC_3_2_WRPROT_BLS_POS;
      __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);

      /* Loop over chip selects, setting the unlocked ranges. */

      for (chip = 0; chip < this->nfc->max_chip_count; chip++) {

            /* Set the unlocked range to cover the entire chip.*/

            __raw_writel(0xffff0000, secondary_base +
                        NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF + (chip * 4));

            /* Unlock. */

            x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
            x &= ~(NFC_3_2_WRPROT_CS2L_MSK | NFC_3_2_WRPROT_WPC_MSK);
            x |= chip << NFC_3_2_WRPROT_CS2L_POS;
            x |= 0x4  << NFC_3_2_WRPROT_WPC_POS ;
            __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);

      }

      /* Return success. */

      return 0;

}

/**
 * nfc_3_2_exit() - Hardware cleanup.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_exit(struct imx_nfc_data *this)
{
}

/**
 * nfc_3_2_set_closest_cycle() - Version-specific hardware cleanup.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_set_closest_cycle(struct imx_nfc_data *this, unsigned ns)
{
      struct clk     *parent_clock;
      unsigned long  parent_clock_rate_in_hz;
      unsigned long  sym_low_clock_rate_in_hz;
      unsigned long  asym_low_clock_rate_in_hz;
      unsigned int   sym_high_cycle_in_ns;
      unsigned int   asym_high_cycle_in_ns;

      /*
       * According to ENGcm09121:
       *
       *  - If the NFC is set to SYMMETRIC mode, the NFC clock divider must
       *    divide the EMI Slow Clock by NO MORE THAN 4.
       *
       *  - If the NFC is set for ASYMMETRIC mode, the NFC clock divider must
       *    divide the EMI Slow Clock by NO MORE THAN 3.
       *
       * We need to compute the corresponding cycle time constraints. Start
       * by getting information about the parent clock.
       */

      parent_clock            = clk_get_parent(this->clock);
      parent_clock_rate_in_hz = clk_get_rate(parent_clock);

      /* Compute the limit frequencies. */

      sym_low_clock_rate_in_hz  = parent_clock_rate_in_hz / 4;
      asym_low_clock_rate_in_hz = parent_clock_rate_in_hz / 3;

      /* Compute the corresponding limit cycle periods. */

      sym_high_cycle_in_ns  = 1000000000 / sym_low_clock_rate_in_hz;
      asym_high_cycle_in_ns = (1000000000 / asym_low_clock_rate_in_hz) * 2;

      /* Attempt to implement the given cycle. */

      return nfc_util_set_best_cycle(this, ns,
                  ns > asym_high_cycle_in_ns, ns > sym_high_cycle_in_ns);

}

/**
 * nfc_3_2_mask_interrupt() - Masks interrupts.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_mask_interrupt(struct imx_nfc_data *this)
{
      void  *secondary_base = this->secondary_regs;
      raw_set_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);
}

/**
 * nfc_3_2_unmask_interrupt() - Unmasks interrupts.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_unmask_interrupt(struct imx_nfc_data *this)
{
      void  *secondary_base = this->secondary_regs;
      raw_clr_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);
}

/**
 * nfc_3_2_clear_interrupt() - Clears an interrupt.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_clear_interrupt(struct imx_nfc_data *this)
{
      int   done;
      void  *secondary_base = this->secondary_regs;

      /* Request IP bus interface access. */

      raw_set_mask_l(NFC_3_2_IPC_CREQ_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

      /* Wait for access. */

      do
            done = !!raw_read_mask_l(NFC_3_2_IPC_CACK_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);
      while (!done);

      /* Clear the interrupt. */

      raw_clr_mask_l(NFC_3_2_IPC_INT_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

      /* Release the IP bus interface. */

      raw_clr_mask_l(NFC_3_2_IPC_CREQ_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

}

/**
 * nfc_3_2_is_interrupting() - Returns the interrupt bit status.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_is_interrupting(struct imx_nfc_data *this)
{
      void  *secondary_base = this->secondary_regs;
      return !!raw_read_mask_l(NFC_3_2_IPC_INT_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);
}

/**
 * nfc_3_2_is_ready() - Returns the ready/busy status.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_is_ready(struct imx_nfc_data *this)
{
      void  *secondary_base = this->secondary_regs;
      return !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);
}

/**
 * nfc_3_2_set_force_ce() - Can force CE to be asserted always.
 *
 * @this:  Per-device data.
 * @on:    Indicates if the hardware CE signal should be asserted always.
 */
static void nfc_3_2_set_force_ce(struct imx_nfc_data *this, int on)
{
      void  *primary_base = this->primary_regs;

      if (on)
            raw_set_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);
      else
            raw_clr_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

}

/**
 * nfc_3_2_set_ecc() - Turns ECC on or off.
 *
 * @this:  Per-device data.
 * @on:    Indicates if ECC should be on or off.
 */
static void nfc_3_2_set_ecc(struct imx_nfc_data *this, int on)
{
      void  *secondary_base = this->secondary_regs;

      if (on)
            raw_set_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);
      else
            raw_clr_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

}

/**
 * nfc_3_2_get_ecc_status() - Reports ECC errors.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_get_ecc_status(struct imx_nfc_data *this)
{
      unsigned int  i;
      void          *base = this->primary_regs;
      uint16_t      status_reg;
      unsigned int  buffer_status[8];
      int           status;

      /* Get the entire status register. */

      status_reg = __raw_readw(base + NFC_3_2_ECC_STATUS_REG_OFF);

      /* Pick out the status for each buffer. */

      buffer_status[0] = (status_reg & NFC_3_2_ECC_STATUS_NOBER1_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER1_POS;

      buffer_status[1] = (status_reg & NFC_3_2_ECC_STATUS_NOBER2_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER2_POS;

      buffer_status[2] = (status_reg & NFC_3_2_ECC_STATUS_NOBER3_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER3_POS;

      buffer_status[3] = (status_reg & NFC_3_2_ECC_STATUS_NOBER4_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER4_POS;

      buffer_status[4] = (status_reg & NFC_3_2_ECC_STATUS_NOBER5_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER5_POS;

      buffer_status[5] = (status_reg & NFC_3_2_ECC_STATUS_NOBER6_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER6_POS;

      buffer_status[6] = (status_reg & NFC_3_2_ECC_STATUS_NOBER7_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER7_POS;

      buffer_status[7] = (status_reg & NFC_3_2_ECC_STATUS_NOBER8_MSK)
                              >> NFC_3_2_ECC_STATUS_NOBER8_POS;

      /* Loop through the buffers we're actually using. */

      status = 0;

      for (i = 0; i < this->nfc_geometry->buffer_count; i++) {

            if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
                  status = -1;
                  break;
            }

            status += buffer_status[i];

      }

      /* Return the final result. */

      return status;

}

/**
 * nfc_3_2_get_symmetric() - Indicates if the clock is symmetric.
 *
 * @this:  Per-device data.
 */
static int nfc_3_2_get_symmetric(struct imx_nfc_data *this)
{
      void  *secondary_base = this->secondary_regs;

      return !!raw_read_mask_w(NFC_3_2_CONFIG2_SYM_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

}

/**
 * nfc_3_2_set_symmetric() - Turns symmetric clock mode on or off.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_set_symmetric(struct imx_nfc_data *this, int on)
{
      void  *secondary_base = this->secondary_regs;

      if (on)
            raw_set_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);
      else
            raw_clr_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
                        secondary_base + NFC_3_2_CONFIG2_REG_OFF);

}

/**
 * nfc_3_2_select_chip() - Selects the current chip.
 *
 * @this:  Per-device data.
 * @chip:  The chip number to select, or -1 to select no chip.
 */
static void nfc_3_2_select_chip(struct imx_nfc_data *this, int chip)
{
      unsigned long  x;
      void           *primary_base = this->primary_regs;

      if (chip < 0)
            return;

      x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);

      x &= ~NFC_3_2_CONFIG1_CS_MSK;

      x |= (chip << NFC_3_2_CONFIG1_CS_POS) & NFC_3_2_CONFIG1_CS_MSK;

      __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);

}

/**
 * nfc_3_2_command_cycle() - Sends a command.
 *
 * @this:     Per-device data.
 * @command:  The command code.
 */
static void nfc_3_2_command_cycle(struct imx_nfc_data *this, unsigned command)
{
      void  *primary_base = this->primary_regs;

      /* Write the command we want to send. */

      __raw_writel(command, primary_base + NFC_3_2_CMD_REG_OFF);

      /* Launch a command cycle. */

      __raw_writel(NFC_3_2_LAUNCH_FCMD_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

}

/**
 * nfc_3_2_write_cycle() - writes a single byte.
 *
 * @this:  Per-device data.
 * @byte:  The byte.
 */
static void nfc_3_2_write_cycle(struct imx_nfc_data *this, unsigned int byte)
{
      void  *primary_base = this->primary_regs;

      /* Give the NFC the byte we want to write. */

      __raw_writel(byte, primary_base + NFC_3_2_ADD0_REG_OFF);

      /* Launch an address cycle.
       *
       * This is *sort* of a hack, but not really. The intent of the NFC
       * design is for this operation to send an address byte. In fact, the
       * NFC neither knows nor cares what we're sending. It justs runs a write
       * cycle.
       */

      __raw_writel(NFC_3_2_LAUNCH_FADD_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, false);

}

/**
 * nfc_3_2_read_cycle() - Applies a single read cycle to the current chip.
 *
 * @this:  Per-device data.
 */
static unsigned int nfc_3_2_read_cycle(struct imx_nfc_data *this)
{
      unsigned int  result;
      void          *primary_base = this->primary_regs;

      /* Launch a "Data Out" operation. */

      __raw_writel(0x4 << NFC_3_2_LAUNCH_FDO_POS,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, false);

      /* Get the result. */

      result = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF)
                                    >> NFC_3_2_CONFIG1_STATUS_POS;
      result &= 0xff;

      /* Return the result. */

      return result;

}

/**
 * nfc_3_2_read_page() - Reads a page into the NFC memory.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_read_page(struct imx_nfc_data *this)
{
      void  *primary_base = this->primary_regs;

      /* Start reading into buffer 0. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Launch a page data out operation. */

      __raw_writel(0x1 << NFC_3_2_LAUNCH_FDO_POS,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, true);

}

/**
 * nfc_3_2_send_page() - Sends a page from the NFC to the current chip.
 *
 * @this:  Per-device data.
 */
static void nfc_3_2_send_page(struct imx_nfc_data *this)
{
      void  *primary_base = this->primary_regs;

      /* Start sending from buffer 0. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Launch a page data in operation. */

      __raw_writel(NFC_3_2_LAUNCH_FDI_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, true);

}

/**
 * nfc_3_2_add_state_events() - Adds events to display important state.
 *
 * @this:   Per-device data.
 */
static void nfc_3_2_add_state_events(struct imx_nfc_data *this)
{
#ifdef EVENT_REPORTING
      void  *secondary_base = this->secondary_regs;

      add_state_event_l
            (
            secondary_base + NFC_3_2_IPC_REG_OFF,
            NFC_3_2_IPC_INT_MSK,
            "  Interrupt     : 0",
            "  Interrupt     : X"
            );

      add_state_event_l
            (
            secondary_base + NFC_3_2_IPC_REG_OFF,
            NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
            "  auto_prog_done: 0",
            "  auto_prog_done: X"
            );

      add_state_event_l
            (
            secondary_base + NFC_3_2_IPC_REG_OFF,
            NFC_3_2_IPC_RB_B_MSK,
            "  Medium        : Busy",
            "  Medium        : Ready"
            );
#endif
}

/**
 * nfc_3_2_get_auto_loop_params() - Gets automatic operation loop parameters.
 *
 * This function and the corresponding "setter" enable the automatic operations
 * to keep some state as they iterate over chips.
 *
 * The most "obvious" way to save state would be to allocate a private data
 * structure and hang it off the owning struct nfc_hal. On the other hand,
 * writing the code to allocate the memory and then release it when the NFC
 * shuts down is annoying - and we have some perfectly good memory in the NFC
 * hardware that we can use. Since we only use two commands at a time, we can
 * stash our loop limits and loop index in the top 16 bits of the NAND_CMD
 * register. To paraphrase the reference manual:
 *
 *
 * NAND_CMD
 *
 * |<--  4 bits  -->|<--  4 bits -->|<--          8 bits          -->|
 * +----------------+---------------+--------------------------------+
 * |     First      |     Last      |           Loop Index           |
 * +----------------+---------------+--------------------------------+
 * |         NAND COMMAND1          |         NAND COMMAND0          |
 * +--------------------------------+--------------------------------+
 * |<--         16 bits          -->|<--         16 bits          -->|
 *
 *
 * @this:   Per-device data.
 * @first:  A pointer to a variable that will receive the first chip number.
 * @last:   A pointer to a variable that will receive the last chip number.
 * @index:  A pointer to a variable that will receive the current chip number.
 */
static void nfc_3_2_get_auto_loop_params(struct imx_nfc_data *this,
                  unsigned *first, unsigned *last, unsigned *index)
{
      uint32_t  x;
      void      *primary_base = this->primary_regs;

      x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);

      *first = (x >> 28) & 0x0f;
      *last  = (x >> 24) & 0x0f;
      *index = (x >> 16) & 0xff;

}

/**
 * nfc_3_2_set_auto_loop_params() - Sets automatic operation loop parameters.
 *
 * See nfc_3_2_get_auto_loop_params() for detailed information about these
 * functions.
 *
 * @this:   Per-device data.
 * @first:  The first chip number.
 * @last:   The last chip number.
 * @index:  The current chip number.
 */
static void nfc_3_2_set_auto_loop_params(struct imx_nfc_data *this,
                        unsigned first, unsigned last, unsigned index)
{
      uint32_t  x;
      void      *primary_base = this->primary_regs;

      x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);

      x &= 0x0000ffff;
      x |= (first & 0x0f) << 28;
      x |= (last  & 0x0f) << 24;
      x |= (index & 0xff) << 16;

      __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);

}

/**
 * nfc_3_2_get_auto_addresses() - Gets automatic operation addresses.
 *
 * @this:    Per-device data.
 * @group:   The address group number.
 * @chip:    A pointer to a variable that will receive the chip number.
 * @column:  A pointer to a variable that will receive the column address.
 *           A NULL pointer indicates there is no column address.
 * @page:    A pointer to a variable that will receive the page address.
 */
static void nfc_3_2_get_auto_addresses(struct imx_nfc_data *this,
      unsigned group, unsigned *chip, unsigned *column, unsigned *page)
{
      uint32_t                  x;
      unsigned int              chip_count;
      unsigned int              cs_width;
      unsigned int              cs_mask;
      unsigned int              page_lsbs;
      unsigned int              page_msbs;
      uint32_t                  *low;
      uint16_t                  *high;
      void                      *primary_base   = this->primary_regs;
      void                      *secondary_base = this->secondary_regs;

      /*
       * The width of the chip select field depends on the number of connected
       * chips.
       *
       * Notice that these computations work only if the number of chips is a
       * power of 2. In fact, that is a fundamental limitation for using
       * automatic operations.
       */

      x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);

      chip_count =
            (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
                              NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
      chip_count++;

      cs_width = ffs(chip_count) - 1;
      cs_mask  = chip_count - 1;

      /* Construct pointers to the pieces of the given address group. */

      low = primary_base + NFC_3_2_ADD0_REG_OFF;
      low += group;

      high = primary_base + NFC_3_2_ADD8_REG_OFF;
      high += group;

      /* Check if there's a column address. */

      if (column) {

            /*
             * The low 32 bits of the address group look like this:
             *
             *      16 - n     n
             * | <-  bits  ->|<->|<-  16 bits   ->|
             * +-------------+---+----------------+
             * |  Page LSBs  |CS |     Column     |
             * +-------------+---+----------------+
             */

            x = __raw_readl(low);

            *column   = x & 0xffff;
            *chip     = (x >> 16) & cs_mask;
            page_lsbs = x >> (16 + cs_width);

            /* The high 16 bits contain the MSB's of the page address. */

            page_msbs = __raw_readw(high);

            *page = (page_msbs << (16 - cs_width)) | page_lsbs;

      } else {

            /*
             * The low 32 bits of the address group look like this:
             *
             *                                 n
             * | <-     (32 - n) bits      ->|<->|
             * +-----------------------------+---+
             * |          Page LSBs          |CS |
             * +-----------------------------+---+
             */

            x = __raw_readl(low);

            *chip     = x &  cs_mask;
            page_lsbs = x >> cs_width;

            /* The high 16 bits contain the MSB's of the page address. */

            page_msbs = __raw_readw(high);

            *page = (page_msbs << (32 - cs_width)) | page_lsbs;

      }

}

/**
 * nfc_3_2_set_auto_addresses() - Sets automatic operation addresses.
 *
 * @this:    Per-device data.
 * @group:   The address group number.
 * @chip:    The chip number.
 * @column:  The column address. The sentinel value ~0 indicates that there is
 *           no column address.
 * @page:    The page address.
 */
static void nfc_3_2_set_auto_addresses(struct imx_nfc_data *this,
            unsigned group, unsigned chip, unsigned column, unsigned page)
{
      uint32_t                  x;
      unsigned                  chip_count;
      unsigned int              cs_width;
      unsigned int              cs_mask;
      uint32_t                  *low;
      uint16_t                  *high;
      void                      *primary_base = this->primary_regs;
      void                      *secondary_base = this->secondary_regs;

      /*
       * The width of the chip select field depends on the number of connected
       * chips.
       *
       * Notice that these computations work only if the number of chips is a
       * power of 2. In fact, that is a fundamental limitation for using
       * automatic operations.
       */

      x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);

      chip_count =
            (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
                              NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
      chip_count++;

      cs_width = ffs(chip_count) - 1;
      cs_mask  = chip_count - 1;

      /* Construct pointers to the pieces of the given address group. */

      low = primary_base + NFC_3_2_ADD0_REG_OFF;
      low += group;

      high = primary_base + NFC_3_2_ADD8_REG_OFF;
      high += group;

      /* Check if we have a column address. */

      if (column != ~0) {

            /*
             * The low 32 bits of the address group look like this:
             *
             *      16 - n     n
             * | <-  bits  ->|<->|<-  16 bits   ->|
             * +-------------+---+----------------+
             * |  Page LSBs  |CS |     Column     |
             * +-------------+---+----------------+
             */

            x  = 0;
            x |= column & 0xffff;
            x |= (chip & cs_mask) << 16;
            x |= page << (16 + cs_width);

            __raw_writel(x, low);

            /* The high 16 bits contain the MSB's of the page address. */

            x = (page >> (16 - cs_width)) & 0xffff;

            __raw_writew(x, high);

      } else {

            /*
             * The low 32 bits of the address group look like this:
             *
             *                                 n
             * | <-     (32 - n) bits      ->|<->|
             * +-----------------------------+---+
             * |          Page LSBs          |CS |
             * +-----------------------------+---+
             */

            x  = 0;
            x |= chip & cs_mask;
            x |= page << cs_width;

            __raw_writel(x, low);

            /* The high 16 bits contain the MSB's of the page address. */

            x = (page >> (32 - cs_width)) & 0xffff;

            __raw_writew(x, high);

      }

}

/**
 * nfc_3_2_start_auto_read() - Starts an automatic read.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 * @start:  The first physical chip number on which to operate.
 * @count:  The number of physical chips on which to operate.
 * @column: The column address.
 * @page:   The page address.
 */
static int nfc_3_2_start_auto_read(struct imx_nfc_data *this,
            unsigned start, unsigned count, unsigned column, unsigned page)
{
      uint32_t  x;
      int       return_value = 0;
      void      *primary_base = this->primary_regs;

      add_event("Entering nfc_3_2_start_auto_read", 1);

      /* Check for nonsense. */

      if ((start > 7) || (!count) || (count > 8)) {
            return_value = !0;
            goto exit;
      }

      /* Set state. */

      nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
      nfc_3_2_set_auto_addresses(this, 0, start, column, page);

      /* Set up for ONE iteration at a time. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Reset to buffer 0. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /*
       * Set up the commands. Note that the number of command phases was
       * configured in the set_geometry() function so, even though we're
       * giving both commands here, they won't necessarily both be used.
       */

      x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);

      x &= 0xffff0000;
      x |= NAND_CMD_READ0     << 0;
      x |= NAND_CMD_READSTART << 8;

      __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);

      /* Launch the operation. */

      add_event("Launching", 0);

      __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

exit: /* Return. */

      add_event("Exiting nfc_3_2_start_auto_read", -1);

      return return_value;

}

/**
 * nfc_3_2_wait_for_auto_read() - Waits until auto read is ready for the CPU.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 */
static int nfc_3_2_wait_for_auto_read(struct imx_nfc_data *this)
{
      unsigned int  first;
      unsigned int  last;
      unsigned int  index;
      int           return_value = 0;

      add_event("Entering nfc_3_2_wait_for_auto_read", 1);

      /* Get state. */

      nfc_3_2_get_auto_loop_params(this, &first, &last, &index);

      /* This function should be called for every chip. */

      if ((index < first) || (index > last)) {
            return_value = !0;
            goto exit;
      }

      /* Wait for the NFC to completely finish and interrupt. */

      nfc_util_wait_for_the_nfc(this, true);

exit: /* Return. */

      add_event("Exiting nfc_3_2_wait_for_auto_read", -1);

      return return_value;

}

/**
 * nfc_3_2_resume_auto_read() - Resumes auto read after CPU intervention.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 */
static int nfc_3_2_resume_auto_read(struct imx_nfc_data *this)
{
      unsigned int  first;
      unsigned int  last;
      unsigned int  index;
      unsigned int  chip;
      unsigned int  column;
      unsigned int  page;
      int           return_value = 0;
      void          *primary_base = this->primary_regs;

      add_event("Entering nfc_3_2_resume_auto_read", 1);

      /* Get state. */

      nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
      nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);

      /* This function should be called for every chip, except the last. */

      if ((index < first) || (index >= last)) {
            return_value = !0;
            goto exit;
      }

      /* Move to the next chip. */

      index++;

      /* Update state. */

      nfc_3_2_set_auto_loop_params(this, first, last, index);
      nfc_3_2_set_auto_addresses(this, 0, index, column, page);

      /* Reset to buffer 0. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Launch the operation. */

      add_event("Launching", 0);

      __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

exit: /* Return. */

      add_event("Exiting nfc_3_2_resume_auto_read", -1);

      return return_value;

}

/**
 * nfc_3_2_start_auto_write() - Starts an automatic write.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 * @start:  The first physical chip number on which to operate.
 * @count:  The number of physical chips on which to operate.
 * @column: The column address.
 * @page:   The page address.
 */
static int nfc_3_2_start_auto_write(struct imx_nfc_data *this,
            unsigned start, unsigned count, unsigned column, unsigned page)
{
      uint32_t  x;
      int       return_value = 0;
      void      *primary_base   = this->primary_regs;
      void      *secondary_base = this->secondary_regs;

      add_event("Entering nfc_3_2_start_auto_write", 1);

      /* Check for nonsense. */

      if ((start > 7) || (!count) || (count > 8)) {
            return_value = !0;
            goto exit;
      }

      /* Set state. */

      nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
      nfc_3_2_set_auto_addresses(this, 0, start, column, page);

      /* Set up for ONE iteration at a time. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Set up the commands. */

      x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);

      x &= 0xffff0000;
      x |= NAND_CMD_SEQIN    << 0;
      x |= NAND_CMD_PAGEPROG << 8;

      __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);

      /* Clear the auto_prog_done bit. */

      raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

exit: /* Return. */

      add_event("Exiting nfc_3_2_start_auto_write", -1);

      return return_value;

}

/**
 * nfc_3_2_wait_for_auto_write() - Waits for auto write to be writey for the CPU.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 */
static int nfc_3_2_wait_for_auto_write(struct imx_nfc_data *this)
{
      unsigned int  first;
      unsigned int  last;
      unsigned int  index;
      unsigned int  chip;
      unsigned int  column;
      unsigned int  page;
      uint32_t      x;
      int           interrupt;
      int           transmitted;
      int           ready;
      int           return_value = 0;
      void          *primary_base   = this->primary_regs;
      void          *secondary_base = this->secondary_regs;

      add_event("Entering nfc_3_2_wait_for_auto_write", 1);

      /* Get state. */

      nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
      nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);

      /* This function should be called for every chip. */

      if ((index < first) || (index > last)) {
            return_value = !0;
            goto exit;
      }

      /* Reset to buffer 0. */

      raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
                              primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Launch the operation. */

      nfc_3_2_add_state_events(this);

      add_event("Launching", 0);

      __raw_writel(NFC_3_2_LAUNCH_AUTO_PROG_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

      nfc_3_2_add_state_events(this);

      /* Wait for the NFC to transmit the page. */

      add_event("Spinning while the NFC transmits the page...", 0);

      do
            transmitted = !!raw_read_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);
      while (!transmitted);

      /*
       * When control arrives here, the auto_prog_done bit is set. This
       * indicates the NFC has finished transmitting the current page. The CPU
       * is now free to write the next page into the NFC's memory. The Flash
       * hardware is still busy programming the page into its storage array.
       *
       * Clear the auto_prog_done bit. This is analogous to acknowledging an
       * interrupt.
       */

      nfc_3_2_add_state_events(this);

      add_event("Acknowledging the page...", 0);

      raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

      nfc_3_2_add_state_events(this);

      /*
       * If this is *not* the last iteration, move to the next chip and return
       * to the caller so he can put the next page in the NFC buffer.
       */

      if (index < last) {

            add_event("Moving to the next chip...", 0);

            index++;

            nfc_3_2_set_auto_loop_params(this, first, last, index);
            nfc_3_2_set_auto_addresses(this, 0, index, column, page);

            goto exit;

      }

      /*
       * If control arrives here, this is the last iteration, so it's time to
       * close out the entire operation. We need to wait for the medium to be
       * ready and then acknowledge the final interrupt.
       *
       * Because of the way the NFC hardware works, the code here requires a
       * bit of explanation. The most important rule is:
       *
       *         During automatic operations, the NFC sets its
       *         interrupt bit *whenever* it sees the ready/busy
       *         signal transition from "Busy" to "Ready".
       *
       * Recall that the ready/busy signals from all the chips in the medium
       * are "wire-anded." Thus, the NFC will only see that the medium is
       * ready if *all* chips are ready.
       *
       * Because of variability in NAND Flash timing, the medium *may* have
       * become ready during previous iterations, which means the interrupt
       * bit *may* be set at this moment. This is a "left-over" interrupt, and
       * can complicate our logic.
       *
       * The two bits of state that interest us here are the interrupt bit
       * and the ready/busy bit. It boils down to the following truth table:
       *
       * | Interrupt  | Ready/Busy | Description
       * +------------+------------+---------------
       * |            |            | Busy medium and no left-over interrupt.
       * |     0      |     0      | The final interrupt will arrive in the
       * |            |            | future.
       * +------------+------------+---------------
       * |            |            | Ready medium and no left-over interrupt.
       * |     0      |     1      | There will be no final interrupt. This
       * |            |            | case should be impossible.
       * +------------+------------+---------------
       * |            |            | Busy medium and left-over interrupt.
       * |     1      |     0      | The final interrupt will arrive in the
       * |            |            | future. This is the hard case.
       * +------------+------------+---------------
       * |            |            | Ready medium and left-over interrupt.
       * |     1      |     1      | The final interrupt has already
       * |            |            | arrived. Acknowledge it and exit.
       * +------------+------------+---------------
       *
       * Case #3 is a small problem. If we clear the interrupt, we may or may
       * not have another interrupt following.
       */

      /* Sample the IPC register. */

      x = __raw_readl(secondary_base + NFC_3_2_IPC_REG_OFF);

      interrupt = !!(x & NFC_3_2_IPC_INT_MSK);
      ready     = !!(x & NFC_3_2_IPC_RB_B_MSK);

      /* Check for the easy cases. */

      if (!interrupt && !ready) {
            add_event("Waiting for the final interrupt..." , 0);
            nfc_util_wait_for_the_nfc(this, true);
            goto exit;
      } else if (!interrupt && ready) {
            add_event("Done." , 0);
            goto exit;
      } else if (interrupt && ready) {
            add_event("Acknowledging the final interrupt..." , 0);
            nfc_util_wait_for_the_nfc(this, false);
            goto exit;

      }

      /*
       * If control arrives here, we hit case #3. Begin by acknowledging the
       * interrupt we have right now.
       */

      add_event("Clearing the left-over interrupt..." , 0);
      nfc_util_wait_for_the_nfc(this, false);

      /*
       * Check the ready/busy bit again. If the medium is still busy, then
       * we're going to get one more interrupt.
       */

      ready = !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
                              secondary_base + NFC_3_2_IPC_REG_OFF);

      if (!ready) {
            add_event("Waiting for the final interrupt..." , 0);
            nfc_util_wait_for_the_nfc(this, true);
      }

exit: /* Return. */

      add_event("Exiting nfc_3_2_wait_for_auto_write", -1);

      return return_value;

}

/**
 * nfc_3_2_start_auto_erase() - Starts an automatic erase.
 *
 * This function returns 0 if everything went well.
 *
 * @this:   Per-device data.
 * @start:  The first physical chip number on which to operate.
 * @count:  The number of physical chips on which to operate.
 * @page:   The page address.
 */
static int nfc_3_2_start_auto_erase(struct imx_nfc_data *this,
                        unsigned start, unsigned count, unsigned page)
{
      uint32_t  x;
      unsigned  i;
      int       return_value = 0;
      void      *primary_base = this->primary_regs;

      add_event("Entering nfc_3_2_start_auto_erase", 1);

      /* Check for nonsense. */

      if ((start > 7) || (!count) || (count > 8)) {
            return_value = !0;
            goto exit;
      }

      /* Set up the commands. */

      x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);

      x &= 0xffff0000;
      x |= NAND_CMD_ERASE1 << 0;
      x |= NAND_CMD_ERASE2 << 8;

      __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);

      /* Set the iterations. */

      x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);

      x &= ~NFC_3_2_CONFIG1_ITER_MSK;
      x |= ((count - 1) << NFC_3_2_CONFIG1_ITER_POS) &
                                    NFC_3_2_CONFIG1_ITER_MSK;

      __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);

      /* Loop over chips, setting up the address groups. */

      for (i = 0; i < count; i++)
            nfc_3_2_set_auto_addresses(this, i, start + i, ~0, page);

      /* Launch the operation. */

      add_event("Launching", 0);

      __raw_writel(NFC_3_2_LAUNCH_AUTO_ERASE_MSK,
                              primary_base + NFC_3_2_LAUNCH_REG_OFF);

exit: /* Return. */

      add_event("Exiting nfc_3_2_start_auto_erase", -1);

      return return_value;

}

/*
 * At this point, we've defined all the version-specific primitives. We're now
 * ready to construct the NFC HAL structures for every version.
 */

struct nfc_hal nfc_1_0_hal = {
            .major_version       = 1,
            .minor_version       = 0,
            .max_chip_count      = 1,
            .max_buffer_count    = 4,
            .spare_buf_stride    = 16,
            .has_secondary_regs  = 0,
            .can_be_symmetric    = 0,
      };

struct nfc_hal nfc_2_0_hal = {
            .major_version       = 2,
            .minor_version       = 0,
            .max_chip_count      = 1,
            .max_buffer_count    = 4,
            .spare_buf_stride    = 16,
            .has_secondary_regs  = false,
            .can_be_symmetric    = true,
            .init                = nfc_2_0_init,
            .set_geometry        = nfc_2_0_set_geometry,
            .exit                = nfc_2_x_exit,
            .mask_interrupt      = nfc_2_0_mask_interrupt,
            .unmask_interrupt    = nfc_2_0_unmask_interrupt,
            .clear_interrupt     = nfc_2_x_clear_interrupt,
            .is_interrupting     = nfc_2_x_is_interrupting,
            .is_ready            = 0, /* Ready/Busy not exposed. */
            .set_ecc             = nfc_2_0_set_ecc,
            .get_ecc_status      = nfc_2_0_get_ecc_status,
            .get_symmetric       = nfc_2_0_get_symmetric,
            .set_symmetric       = nfc_2_0_set_symmetric,
            .select_chip         = nfc_2_0_select_chip,
            .command_cycle       = nfc_2_x_command_cycle,
            .write_cycle         = nfc_2_x_write_cycle,
            .read_cycle          = nfc_2_0_read_cycle,
            .read_page           = nfc_2_0_read_page,
            .send_page           = nfc_2_0_send_page,
            .start_auto_read     = 0, /* Not supported. */
            .wait_for_auto_read  = 0, /* Not supported. */
            .resume_auto_read    = 0, /* Not supported. */
            .start_auto_write    = 0, /* Not supported. */
            .wait_for_auto_write = 0, /* Not supported. */
            .start_auto_erase    = 0, /* Not supported. */
      };

struct nfc_hal nfc_2_1_hal = {
            .major_version       = 2,
            .minor_version       = 1,
            .max_chip_count      = 4,
            .max_buffer_count    = 8,
            .spare_buf_stride    = 64,
            .has_secondary_regs  = 0,
            .can_be_symmetric    = !0,
      };

struct nfc_hal nfc_3_1_hal = {
            .major_version       = 3,
            .minor_version       = 1,
            .max_chip_count      = 4,
            .max_buffer_count    = 8,
            .spare_buf_stride    = 64,
            .has_secondary_regs  = !0,
            .can_be_symmetric    = !0,
      };

struct nfc_hal nfc_3_2_hal = {
            .major_version       = 3,
            .minor_version       = 2,
            .max_chip_count      = 8,
            .max_buffer_count    = 8,
            .spare_buf_stride    = 64,
            .has_secondary_regs  = true,
            .can_be_symmetric    = true,
            .init                = nfc_3_2_init,
            .set_geometry        = nfc_3_2_set_geometry,
            .exit                = nfc_3_2_exit,
            .set_closest_cycle   = nfc_3_2_set_closest_cycle,
            .mask_interrupt      = nfc_3_2_mask_interrupt,
            .unmask_interrupt    = nfc_3_2_unmask_interrupt,
            .clear_interrupt     = nfc_3_2_clear_interrupt,
            .is_interrupting     = nfc_3_2_is_interrupting,
            .is_ready            = nfc_3_2_is_ready,
            .set_force_ce        = nfc_3_2_set_force_ce,
            .set_ecc             = nfc_3_2_set_ecc,
            .get_ecc_status      = nfc_3_2_get_ecc_status,
            .get_symmetric       = nfc_3_2_get_symmetric,
            .set_symmetric       = nfc_3_2_set_symmetric,
            .select_chip         = nfc_3_2_select_chip,
            .command_cycle       = nfc_3_2_command_cycle,
            .write_cycle         = nfc_3_2_write_cycle,
            .read_cycle          = nfc_3_2_read_cycle,
            .read_page           = nfc_3_2_read_page,
            .send_page           = nfc_3_2_send_page,
            .start_auto_read     = nfc_3_2_start_auto_read,
            .wait_for_auto_read  = nfc_3_2_wait_for_auto_read,
            .resume_auto_read    = nfc_3_2_resume_auto_read,
            .start_auto_write    = nfc_3_2_start_auto_write,
            .wait_for_auto_write = nfc_3_2_wait_for_auto_write,
            .start_auto_erase    = nfc_3_2_start_auto_erase,
      };

/*
 * This array has a pointer to every NFC HAL structure. The probing process will
 * find the one that matches the version given by the platform.
 */

struct nfc_hal  *(nfc_hals[]) = {
      &nfc_1_0_hal,
      &nfc_2_0_hal,
      &nfc_2_1_hal,
      &nfc_3_1_hal,
      &nfc_3_2_hal,
};

/**
 * mal_init() - Initialize the Medium Abstraction Layer.
 *
 * @this:  Per-device data.
 */
static void mal_init(struct imx_nfc_data  *this)
{
      this->interrupt_override = DRIVER_CHOICE;
      this->auto_op_override   = DRIVER_CHOICE;
      this->inject_ecc_error   = 0;
}

/**
 * mal_set_physical_geometry() - Set up the physical medium geometry.
 *
 * This function retrieves the physical geometry information discovered by
 * nand_scan(), corrects it, and records it in the per-device data structure.
 *
 * @this:  Per-device data.
 */
static int mal_set_physical_geometry(struct imx_nfc_data  *this)
{
      struct mtd_info           *mtd  = &this->mtd;
      struct nand_chip          *nand = &this->nand;
      struct device             *dev  = this->dev;
      uint8_t                   manufacturer_id;
      uint8_t                   device_id;
      unsigned int              block_size_in_pages;
      unsigned int              chip_size_in_blocks;
      unsigned int              chip_size_in_pages;
      uint64_t                  medium_size_in_bytes;
      struct physical_geometry  *physical = &this->physical_geometry;

      /*
       * Begin by transcribing exactly what the MTD code discovered. If there
       * are any mistakes, we'll fix them in a moment.
       */

      physical->chip_count     = nand->numchips;
      physical->chip_size      = nand->chipsize;
      physical->block_size     = mtd->erasesize;
      physical->page_data_size = mtd->writesize;
      physical->page_oob_size  = mtd->oobsize;

      /* Read some of the ID bytes from the first NAND Flash chip. */

      nand->select_chip(mtd, 0);

      nfc_util_send_cmd_and_addrs(this, NAND_CMD_READID, 0x00, -1);

      manufacturer_id = nand->read_byte(mtd);
      device_id       = nand->read_byte(mtd);

      /*
       * Most manufacturers sell 4K page devices with 218 out-of-band bytes
       * per page to accomodate ECC-8.
       *
       * Samsung and Hynix claim their parts have better reliability, so they
       * only need ECC-4 and they have only 128 out-of-band bytes.
       *
       * The MTD code pays no attention to the manufacturer ID (something that
       * eventually will have to change), so it believes that all 4K pages
       * have 218 out-of-band bytes.
       *
       * We correct that mistake here.
       */

      if (physical->page_data_size == 4096) {
            if ((manufacturer_id == NAND_MFR_SAMSUNG) ||
                              (manufacturer_id == NAND_MFR_HYNIX)) {
                  physical->page_oob_size = 128;
            }
      }

      /* Compute some interesting facts. */

      block_size_in_pages  =
            physical->block_size / physical->page_data_size;
      chip_size_in_pages   =
            physical->chip_size >> (fls(physical->page_data_size) - 1);
      chip_size_in_blocks  =
            physical->chip_size >> (fls(physical->block_size) - 1);
      medium_size_in_bytes =
            physical->chip_size * physical->chip_count;

      /* Report. */

      dev_dbg(dev, "-----------------\n");
      dev_dbg(dev, "Physical Geometry\n");
      dev_dbg(dev, "-----------------\n");
      dev_dbg(dev, "Chip Count             : %d\n", physical->chip_count);
      dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
                  physical->page_data_size, physical->page_data_size);
      dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
                  physical->page_oob_size);
      dev_dbg(dev, "Block Size in Bytes    : %u (0x%x)\n",
                  physical->block_size, physical->block_size);
      dev_dbg(dev, "Block Size in Pages    : %u (0x%x)\n",
                  block_size_in_pages, block_size_in_pages);
      dev_dbg(dev, "Chip Size in Bytes     : %llu (0x%llx)\n",
                  physical->chip_size, physical->chip_size);
      dev_dbg(dev, "Chip Size in Pages     : %u (0x%x)\n",
                  chip_size_in_pages, chip_size_in_pages);
      dev_dbg(dev, "Chip Size in Blocks    : %u (0x%x)\n",
                  chip_size_in_blocks, chip_size_in_blocks);
      dev_dbg(dev, "Medium Size in Bytes   : %llu (0x%llx)\n",
                  medium_size_in_bytes, medium_size_in_bytes);

      /* Return success. */

      return 0;

}

/**
 * mal_set_nfc_geometry() - Set up the NFC geometry.
 *
 * This function calls the NFC HAL to select an NFC geometry that is compatible
 * with the medium's physical geometry.
 *
 * @this:  Per-device data.
 */
static int mal_set_nfc_geometry(struct imx_nfc_data  *this)
{
      struct device        *dev = this->dev;
      struct nfc_geometry  *nfc;

      /* Set the NFC geometry. */

      if (this->nfc->set_geometry(this))
            return !0;

      /* Get a pointer to the new NFC geometry information. */

      nfc = this->nfc_geometry;

      /* Report. */

      dev_dbg(dev, "------------\n");
      dev_dbg(dev, "NFC Geometry\n");
      dev_dbg(dev, "------------\n");
      dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
                        nfc->page_data_size, nfc->page_data_size);
      dev_dbg(dev, "Page OOB Size in Bytes : %u\n", nfc->page_oob_size);
      dev_dbg(dev, "ECC Algorithm          : %s\n", nfc->ecc_algorithm);
      dev_dbg(dev, "ECC Strength           : %d\n", nfc->ecc_strength);
      dev_dbg(dev, "Buffer Count           : %u\n", nfc->buffer_count);
      dev_dbg(dev, "Spare Buffer Size      : %u\n", nfc->spare_buf_size);
      dev_dbg(dev, "Spare Buffer Spillover : %u\n", nfc->spare_buf_spill);
      dev_dbg(dev, "Auto Read Available    : %s\n",
                        this->nfc->start_auto_read  ? "Yes" : "No");
      dev_dbg(dev, "Auto Write Available   : %s\n",
                        this->nfc->start_auto_write ? "Yes" : "No");
      dev_dbg(dev, "Auto Erase Available   : %s\n",
                        this->nfc->start_auto_erase ? "Yes" : "No");

      /* Return success. */

      return 0;

}

/**
 * mal_set_logical_geometry() - Set up the logical medium geometry.
 *
 * This function constructs the logical geometry that we will expose to MTD,
 * based on the physical and NFC geometries, and whether or not interleaving is
 * on.
 *
 * @this:  Per-device data.
 */
static int mal_set_logical_geometry(struct imx_nfc_data  *this)
{
      const uint32_t            max_medium_size_in_bytes = ~0;
      int                       we_are_interleaving;
      uint64_t                  physical_medium_size_in_bytes;
      unsigned int              usable_blocks;
      unsigned int              block_size_in_pages;
      unsigned int              chip_size_in_blocks;
      unsigned int              chip_size_in_pages;
      unsigned int              usable_medium_size_in_pages;
      unsigned int              usable_medium_size_in_blocks;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct nfc_geometry       *nfc      =  this->nfc_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;
      struct device             *dev      =  this->dev;

      /* Figure out if we're interleaving. */

      we_are_interleaving = this->pdata->interleave;

      switch (imx_nfc_module_interleave_override) {

      case NEVER:
            we_are_interleaving = false;
            break;

      case DRIVER_CHOICE:
            break;

      case ALWAYS:
            we_are_interleaving = true;
            break;

      }

      /* Compute the physical size of the medium. */

      physical_medium_size_in_bytes =
                        physical->chip_count * physical->chip_size;

      /* Compute the logical geometry. */

      if (!we_are_interleaving) {

            /*
             * At this writing, MTD uses unsigned 32-bit variables to
             * represent the size of the medium. If the physical medium is
             * larger than that, the logical medium must be smaller. Here,
             * we compute the total number of physical blocks in the medium
             * that we can actually use.
             */

            if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
                  usable_blocks =
                        physical_medium_size_in_bytes >>
                                    (ffs(physical->block_size) - 1);
            } else {
                  usable_blocks =
                        max_medium_size_in_bytes / physical->block_size;
            }

            /* Set up the logical geometry.
             *
             * Notice that the usable medium size is not necessarily the
             * same as the chip size multiplied by the number of physical
             * chips. We can't afford to touch the physical chip size
             * because the NAND Flash MTD code *requires* it to be a power
             * of 2.
             */

            logical->chip_count     = physical->chip_count;
            logical->chip_size      = physical->chip_size;
            logical->usable_size    = usable_blocks * physical->block_size;
            logical->block_size     = physical->block_size;
            logical->page_data_size = nfc->page_data_size;

            /* Use the MTD layout that best matches the NFC geometry. */

            logical->mtd_layout    = &nfc->mtd_layout;
            logical->page_oob_size = nfc->mtd_layout.eccbytes +
                                    nfc->mtd_layout.oobavail;

      } else {

            /*
             * If control arrives here, we are interleaving. Specifically,
             * we are "horizontally concatenating" the pages in all the
             * physical chips.
             *
             * - A logical page will be the size of a physical page
             *   multiplied by the number of physical chips.
             *
             * - A logical block will have the same number of pages as a
             *   physical block but, since the logical page size is larger,
             *   the logical block size is larger.
             *
             * - The entire medium will appear to be a single chip.
             *
             * At this writing, MTD uses unsigned 32-bit variables to
             * represent the size of the medium. If the physical medium is
             * larger than that, the logical medium must be smaller.
             *
             * The NAND Flash MTD code represents the size of a single chip
             * as an unsigned 32-bit value. It also *requires* that the size
             * of a chip be a power of two. Thus, the largest possible chip
             * size is 2GiB.
             *
             * When interleaving, the entire medium appears to be one chip.
             * Thus, when interleaving, the largest possible medium size is
             * 2GiB.
             */

            if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
                  logical->chip_size =
                        0x1 << (fls(physical_medium_size_in_bytes) - 1);
            } else {
                  logical->chip_size =
                        0x1 << (fls(max_medium_size_in_bytes) - 1);
            }

            /*
             * If control arrives here, we're interleaving. The logical
             * geometry is very different from the physical geometry.
             */

            logical->chip_count     = 1;
            logical->usable_size    = logical->chip_size;
            logical->block_size     =
                        physical->block_size * physical->chip_count;
            logical->page_data_size =
                        nfc->page_data_size * physical->chip_count;

            /*
             * Since the logical geometry doesn't match the physical
             * geometry, we can't use the MTD layout that matches the
             * NFC geometry. We synthesize one here.
             *
             * Our "logical" OOB will be the concatenation of the first 5
             * bytes of the "physical" OOB of every chip. This has some
             * important properties:
             *
             * - This will make the block mark of every physical chip
             *   visible (even for small page chips, which put their block
             *   mark in the 5th OOB byte).
             *
             * - None of the NFC controllers put ECC in the first 5 OOB
             *   bytes, so this layout exposes no ECC.
             */

            logical->page_oob_size = 5 * physical->chip_count;

            synthetic_layout.eccbytes = 0;
            synthetic_layout.oobavail = 5 * physical->chip_count;
            synthetic_layout.oobfree[0].offset = 0;
            synthetic_layout.oobfree[0].length = synthetic_layout.oobavail;

            /* Install the synthetic layout. */

            logical->mtd_layout = &synthetic_layout;

      }

      /* Compute some interesting facts. */

      block_size_in_pages = logical->block_size / logical->page_data_size;
      chip_size_in_pages  = logical->chip_size / logical->page_data_size;
      chip_size_in_blocks = logical->chip_size / logical->block_size;
      usable_medium_size_in_pages =
                        logical->usable_size / logical->page_data_size;
      usable_medium_size_in_blocks =
                        logical->usable_size / logical->block_size;

      /* Report. */

      dev_dbg(dev, "----------------\n");
      dev_dbg(dev, "Logical Geometry\n");
      dev_dbg(dev, "----------------\n");
      dev_dbg(dev, "Chip Count             : %d\n", logical->chip_count);
      dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
            logical->page_data_size, logical->page_data_size);
      dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
            logical->page_oob_size);
      dev_dbg(dev, "Block Size in Bytes    : %u (0x%x)\n",
            logical->block_size, logical->block_size);
      dev_dbg(dev, "Block Size in Pages    : %u (0x%x)\n",
            block_size_in_pages, block_size_in_pages);
      dev_dbg(dev, "Chip Size in Bytes     : %u (0x%x)\n",
            logical->chip_size, logical->chip_size);
      dev_dbg(dev, "Chip Size in Pages     : %u (0x%x)\n",
            chip_size_in_pages, chip_size_in_pages);
      dev_dbg(dev, "Chip Size in Blocks    : %u (0x%x)\n",
            chip_size_in_blocks, chip_size_in_blocks);
      dev_dbg(dev, "Physical Size in Bytes : %llu (0x%llx)\n",
            physical_medium_size_in_bytes, physical_medium_size_in_bytes);
      dev_dbg(dev, "Usable Size in Bytes   : %u (0x%x)\n",
            logical->usable_size, logical->usable_size);
      dev_dbg(dev, "Usable Size in Pages   : %u (0x%x)\n",
            usable_medium_size_in_pages, usable_medium_size_in_pages);
      dev_dbg(dev, "Usable Size in Blocks  : %u (0x%x)\n",
            usable_medium_size_in_blocks, usable_medium_size_in_blocks);

      /* Return success. */

      return 0;

}

/**
 * mal_set_mtd_geometry() - Set up the MTD geometry.
 *
 * This function adjusts the owning MTD data structures to match the logical
 * geometry we've chosen.
 *
 * @this:  Per-device data.
 */
static int mal_set_mtd_geometry(struct imx_nfc_data *this)
{
      struct logical_geometry  *logical = &this->logical_geometry;
      struct mtd_info          *mtd     = &this->mtd;
      struct nand_chip         *nand    = &this->nand;

      /* Configure the struct mtd_info. */

      mtd->size        = logical->usable_size;
      mtd->erasesize   = logical->block_size;
      mtd->writesize   = logical->page_data_size;
      mtd->ecclayout   = logical->mtd_layout;
      mtd->oobavail    = mtd->ecclayout->oobavail;
      mtd->oobsize     = mtd->ecclayout->oobavail + mtd->ecclayout->eccbytes;
      mtd->subpage_sft = 0; /* We don't support sub-page writing. */

      /* Configure the struct nand_chip. */

      nand->numchips         = logical->chip_count;
      nand->chipsize         = logical->chip_size;
      nand->page_shift       = ffs(logical->page_data_size) - 1;
      nand->pagemask         = (nand->chipsize >> nand->page_shift) - 1;
      nand->subpagesize      = mtd->writesize >> mtd->subpage_sft;
      nand->phys_erase_shift = ffs(logical->block_size) - 1;
      nand->bbt_erase_shift  = nand->phys_erase_shift;
      nand->chip_shift       = ffs(logical->chip_size) - 1;
      nand->oob_poi          = nand->buffers->databuf+logical->page_data_size;
      nand->ecc.layout       = logical->mtd_layout;

      /* Set up the pattern that describes block marks. */

      if (is_small_page_chip(this))
            nand->badblock_pattern = &small_page_block_mark_descriptor;
      else
            nand->badblock_pattern = &large_page_block_mark_descriptor;

      /* Return success. */

      return 0;
}

/**
 * mal_set_geometry() - Set up the medium geometry.
 *
 * @this:  Per-device data.
 */
static int mal_set_geometry(struct imx_nfc_data  *this)
{

      /* Set up the various layers of geometry, in this specific order. */

      if (mal_set_physical_geometry(this))
            return !0;

      if (mal_set_nfc_geometry(this))
            return !0;

      if (mal_set_logical_geometry(this))
            return !0;

      if (mal_set_mtd_geometry(this))
            return !0;

      /* Return success. */

      return 0;

}

/**
 * mal_reset() - Resets the given chip.
 *
 * This is the fully-generalized reset operation, including support for
 * interleaving. All reset operations funnel through here.
 *
 * @this:  Per-device data.
 * @chip:  The logical chip of interest.
 */
static void mal_reset(struct imx_nfc_data *this, unsigned chip)
{
      int                       we_are_interleaving;
      unsigned int              start;
      unsigned int              end;
      unsigned int              i;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      add_event("Entering mal_get_status", 1);

      /* Establish some important facts. */

      we_are_interleaving = logical->chip_count != physical->chip_count;

      /* Choose the loop bounds. */

      if (we_are_interleaving) {
            start = 0;
            end   = physical->chip_count;
      } else {
            start = chip;
            end   = start + 1;
      }

      /* Loop over physical chips. */

      add_event("Looping over physical chips...", 0);

      for (i = start; i < end; i++) {

            /* Select the current chip. */

            this->nfc->select_chip(this, i);

            /* Reset the current chip. */

            add_event("Resetting...", 0);

            nfc_util_send_cmd(this, NAND_CMD_RESET);
            nfc_util_wait_for_the_nfc(this, false);

      }

      add_event("Exiting mal_get_status", -1);

}

/**
 * mal_get_status() - Abstracted status retrieval.
 *
 * For media with a single chip, or concatenated chips, the HIL explicitly
 * addresses a single chip at a time and wants the status from that chip only.
 *
 * For interleaved media, we must combine the individual chip states. At this
 * writing, the NAND MTD system knows about the following bits in status
 * registers:
 *
 *    +------------------------+-------+---------+
 *    |                        |       | Combine |
 *    |         Macro          | Value |  With   |
 *    +------------------------+-------+---------+
 *    | NAND_STATUS_FAIL       |  0x01 |    OR   |
 *    | NAND_STATUS_FAIL_N1    |  0x02 |    OR   |
 *    | NAND_STATUS_TRUE_READY |  0x20 |   AND   |
 *    | NAND_STATUS_READY      |  0x40 |   AND   |
 *    | NAND_STATUS_WP         |  0x80 |   AND   |
 *    +------------------------+-------+---------+
 *
 * @this:  Per-device data.
 * @chip:  The logical chip of interest.
 */
static uint8_t mal_get_status(struct imx_nfc_data *this, unsigned chip)
{
      int                       we_are_interleaving;
      unsigned int              start;
      unsigned int              end;
      unsigned int              i;
      unsigned int              x;
      unsigned int              or_mask;
      unsigned int              and_mask;
      uint8_t                   status;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      add_event("Entering mal_get_status", 1);

      /* Establish some important facts. */

      we_are_interleaving = logical->chip_count != physical->chip_count;

      /* Compute the masks we need. */

      or_mask  = NAND_STATUS_FAIL | NAND_STATUS_FAIL_N1;
      and_mask = NAND_STATUS_TRUE_READY | NAND_STATUS_READY | NAND_STATUS_WP;

      /* Assume the chip is successful, ready and writeable. */

      status = and_mask & ~or_mask;

      /* Choose the loop bounds. */

      if (we_are_interleaving) {
            start = 0;
            end   = physical->chip_count;
      } else {
            start = chip;
            end   = start + 1;
      }

      /* Loop over physical chips. */

      add_event("Looping over physical chips...", 0);

      for (i = start; i < end; i++) {

            /* Select the current chip. */

            this->nfc->select_chip(this, i);

            /* Get the current chip's status. */

            add_event("Sending the command...", 0);

            nfc_util_send_cmd(this, NAND_CMD_STATUS);
            nfc_util_wait_for_the_nfc(this, false);

            add_event("Reading the status...", 0);

            x = this->nfc->read_cycle(this);

            /* Fold this chip's status into the combined status. */

            status |= (x & or_mask);
            status &= (x & and_mask) | or_mask;

      }

      add_event("Exiting mal_get_status", -1);

      return status;

}

/**
 * mal_read_a_page() - Abstracted page read.
 *
 * This function returns the ECC status for the entire read operation. A
 * positive return value indicates the number of errors that were corrected
 * (symbol errors for Reed-Solomon hardware engines, bit errors for BCH hardware
 * engines). A negative return value indicates that the ECC engine failed to
 * correct all errors and the data is corrupted. A zero return value indicates
 * there were no errors at all.
 *
 * @this:    Per-device data.
 * @use_ecc: Indicates if we're to use ECC.
 * @chip:    The logical chip of interest.
 * @page:    The logical page number to read.
 * @data:    A pointer to the destination data buffer. If this pointer is null,
 *           that indicates the caller doesn't want the data.
 * @oob:     A pointer to the destination OOB buffer. If this pointer is null,
 *           that indicates the caller doesn't want the OOB.
 */
static int mal_read_a_page(struct imx_nfc_data *this, int use_ecc,
            unsigned chip, unsigned page, uint8_t *data, uint8_t *oob)
{
      int                       we_are_interleaving;
      int                       use_automatic_op;
      unsigned int              start;
      unsigned int              end;
      unsigned int              current_chip;
      unsigned int              oob_bytes_to_copy;
      unsigned int              data_bytes_to_copy;
      int                       status;
      unsigned int              worst_case_ecc_status;
      int                       return_value = 0;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct nfc_geometry       *nfc      = this->nfc_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      add_event("Entering mal_read_a_page", 1);

      /* Establish some important facts. */

      we_are_interleaving = logical->chip_count != physical->chip_count;
      use_automatic_op    = !!this->nfc->start_auto_read;

      /* Apply the automatic operation override, if any. */

      switch (this->auto_op_override) {

      case NEVER:
            use_automatic_op = false;
            break;

      case DRIVER_CHOICE:
            break;

      case ALWAYS:
            if (this->nfc->start_auto_read)
                  use_automatic_op = true;
            break;

      }

      /* Set up ECC. */

      this->nfc->set_ecc(this, use_ecc);

      /* Check if we're interleaving and set up the loop iterations. */

      if (we_are_interleaving) {

            start = 0;
            end   = physical->chip_count;

            data_bytes_to_copy =
                  this->logical_geometry.page_data_size /
                              this->physical_geometry.chip_count;
            oob_bytes_to_copy =
                  this->logical_geometry.page_oob_size /
                              this->physical_geometry.chip_count;

      } else {

            start = chip;
            end   = start + 1;

            data_bytes_to_copy = this->logical_geometry.page_data_size;
            oob_bytes_to_copy  = this->logical_geometry.page_oob_size;

      }

      /* If we're using the automatic operation, start it now. */

      if (use_automatic_op) {
            add_event("Starting the automatic operation...", 0);
            this->nfc->start_auto_read(this, start, end - start, 0, page);
      }

      /* Loop over physical chips. */

      add_event("Looping over physical chips...", 0);

      for (current_chip = start; current_chip < end; current_chip++) {

            /* Check if we're using the automatic operation. */

            if (use_automatic_op) {

                  add_event("Waiting...", 0);
                  this->nfc->wait_for_auto_read(this);

            } else {

                  /* Select the current chip. */

                  this->nfc->select_chip(this, current_chip);

                  /* Set up the chip. */

                  add_event("Sending the command and addresses...", 0);

                  nfc_util_send_cmd_and_addrs(this,
                                    NAND_CMD_READ0, 0, page);

                  if (is_large_page_chip(this)) {
                        add_event("Sending the final command...", 0);
                        nfc_util_send_cmd(this, NAND_CMD_READSTART);
                  }

                  /* Wait till the page is ready. */

                  add_event("Waiting for the page to arrive...", 0);

                  nfc_util_wait_for_the_nfc(this, true);

                  /* Read the page. */

                  add_event("Reading the page...", 0);

                  this->nfc->read_page(this);

            }

            /* Copy a page out of the NFC. */

            add_event("Copying from the NFC...", 0);

            if (oob) {
                  nfc_util_copy_from_the_nfc(this,
                        nfc->page_data_size, oob, oob_bytes_to_copy);
                  oob += oob_bytes_to_copy;
            }

            if (data) {
                  nfc_util_copy_from_the_nfc(this,
                                    0, data, data_bytes_to_copy);
                  data += data_bytes_to_copy;
            }

            /*
             * If we're using ECC, and we haven't already seen an ECC
             * failure, continue to gather ECC status. Note that, if we
             * *do* see an ECC failure, we continue to read because the
             * client might want the data for forensic purposes.
             */

            if (use_ecc && (return_value >= 0)) {

                  add_event("Getting ECC status...", 0);

                  status = this->nfc->get_ecc_status(this);

                  if (status >= 0)
                        return_value += status;
                  else
                        return_value = -1;

            }

            /* Check if we're using the automatic operation. */

            if (use_automatic_op) {

                  /*
                   * If this is not the last iteration, resume the
                   * automatic operation.
                   */

                  if (current_chip < (end - 1)) {
                        add_event("Resuming...", 0);
                        this->nfc->resume_auto_read(this);
                  }

            }

      }

      /* Check if we're supposed to inject an ECC error. */

      if (use_ecc && this->inject_ecc_error) {

            /* Inject the appropriate error. */

            if (this->inject_ecc_error < 0) {

                  add_event("Injecting an uncorrectable error...", 0);

                  return_value = -1;

            } else {

                  add_event("Injecting correctable errors...", 0);

                  worst_case_ecc_status =
                        physical->chip_count *
                        nfc->buffer_count *
                        nfc->ecc_strength;

                  if (this->inject_ecc_error > worst_case_ecc_status)
                        return_value = worst_case_ecc_status;
                  else
                        return_value = this->inject_ecc_error;

            }

            /* Stop injecting further errors. */

            this->inject_ecc_error = 0;

      }

      /* Return. */

      add_event("Exiting mal_read_a_page", -1);

      return return_value;

}

/**
 * mal_write_a_page() - Abstracted page write.
 *
 * This function returns zero if the operation succeeded, or -EIO if the
 * operation failed.
 *
 * @this:    Per-device data.
 * @use_ecc: Indicates if we're to use ECC.
 * @chip:    The logical chip of interest.
 * @page:    The logical page number to write.
 * @data:    A pointer to the source data buffer.
 * @oob:     A pointer to the source OOB buffer.
 */
static int mal_write_a_page(struct imx_nfc_data *this, int use_ecc,
      unsigned chip, unsigned page, const uint8_t *data, const uint8_t *oob)
{
      int                       we_are_interleaving;
      int                       use_automatic_op;
      unsigned int              start;
      unsigned int              end;
      unsigned int              current_chip;
      unsigned int              oob_bytes_to_copy;
      unsigned int              data_bytes_to_copy;
      int                       return_value = 0;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct nfc_geometry       *nfc      = this->nfc_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      add_event("Entering mal_write_a_page", 1);

      /* Establish some important facts. */

      we_are_interleaving = logical->chip_count != physical->chip_count;
      use_automatic_op    = !!this->nfc->start_auto_write;

      /* Apply the automatic operation override, if any. */

      switch (this->auto_op_override) {

      case NEVER:
            use_automatic_op = false;
            break;

      case DRIVER_CHOICE:
            break;

      case ALWAYS:
            if (this->nfc->start_auto_write)
                  use_automatic_op = true;
            break;

      }

      /* Set up ECC. */

      this->nfc->set_ecc(this, use_ecc);

      /* Check if we're interleaving and set up the loop iterations. */

      if (we_are_interleaving) {

            start = 0;
            end   = physical->chip_count;

            data_bytes_to_copy =
                  this->logical_geometry.page_data_size /
                              this->physical_geometry.chip_count;
            oob_bytes_to_copy =
                  this->logical_geometry.page_oob_size /
                              this->physical_geometry.chip_count;

      } else {

            start = chip;
            end   = start + 1;

            data_bytes_to_copy = this->logical_geometry.page_data_size;
            oob_bytes_to_copy  = this->logical_geometry.page_oob_size;

      }

      /* If we're using the automatic operation, start the hardware now. */

      if (use_automatic_op) {
            add_event("Starting the automatic operation...", 0);
            this->nfc->start_auto_write(this, start, end - start, 0, page);
      }

      /* Loop over physical chips. */

      add_event("Looping over physical chips...", 0);

      for (current_chip = start; current_chip < end; current_chip++) {

            /* Copy a page into the NFC. */

            add_event("Copying to the NFC...", 0);

            nfc_util_copy_to_the_nfc(this, oob, nfc->page_data_size,
                                          oob_bytes_to_copy);
            oob += oob_bytes_to_copy;

            nfc_util_copy_to_the_nfc(this, data, 0, data_bytes_to_copy);

            data += data_bytes_to_copy;

            /* Check if we're using the automatic operation. */

            if (use_automatic_op) {

                  /* Wait for the write operation to finish. */

                  add_event("Waiting...", 0);

                  this->nfc->wait_for_auto_write(this);

            } else {

                  /* Select the current chip. */

                  this->nfc->select_chip(this, current_chip);

                  /* Set up the chip. */

                  add_event("Sending the command and addresses...", 0);

                  nfc_util_send_cmd_and_addrs(this,
                                    NAND_CMD_SEQIN, 0, page);

                  /* Send the page. */

                  add_event("Sending the page...", 0);

                  this->nfc->send_page(this);

                  /* Start programming the page. */

                  add_event("Programming the page...", 0);

                  nfc_util_send_cmd(this, NAND_CMD_PAGEPROG);

                  /* Wait until the page is finished. */

                  add_event("Waiting...", 0);

                  nfc_util_wait_for_the_nfc(this, true);

            }

      }

      /* Get status. */

      add_event("Gathering status...", 0);

      if (mal_get_status(this, chip) & NAND_STATUS_FAIL) {
            add_event("Bad status", 0);
            return_value = -EIO;
      } else {
            add_event("Good status", 0);
      }

      /* Return. */

      add_event("Exiting mal_write_a_page", -1);

      return return_value;

}

/**
 * mal_erase_a_block() - Abstract block erase operation.
 *
 * Note that this function does *not* wait for the operation to finish. The
 * caller is expected to call waitfunc() at some later time.
 *
 * @this:  Per-device data.
 * @chip:  The logical chip of interest.
 * @page:  A logical page address that identifies the block to erase.
 */
static void mal_erase_a_block(struct imx_nfc_data *this,
                                    unsigned chip, unsigned page)
{
      int                       we_are_interleaving;
      int                       use_automatic_op;
      unsigned int              start;
      unsigned int              end;
      unsigned int              i;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      add_event("Entering mal_erase_a_block", 1);

      /* Establish some important facts. */

      we_are_interleaving = logical->chip_count != physical->chip_count;
      use_automatic_op    = !!this->nfc->start_auto_erase;

      /* Apply the automatic operation override, if any. */

      switch (this->auto_op_override) {

      case NEVER:
            use_automatic_op = false;
            break;

      case DRIVER_CHOICE:
            break;

      case ALWAYS:
            if (this->nfc->start_auto_erase)
                  use_automatic_op = true;
            break;

      }

      /* Choose the loop bounds. */

      if (we_are_interleaving) {
            start = 0;
            end   = physical->chip_count;
      } else {
            start = chip;
            end   = start + 1;
      }

      /* Check if we're using the automatic operation. */

      if (use_automatic_op) {

            /*
             * Start the operation. Note that we don't wait for it to
             * finish because the HIL will call our waitfunc().
             */

            add_event("Starting the automatic operation...", 0);

            this->nfc->start_auto_erase(this, start, end - start, page);

      } else {

            /* Loop over physical chips. */

            add_event("Looping over physical chips...", 0);

            for (i = start; i < end; i++) {

                  /* Select the current chip. */

                  this->nfc->select_chip(this, i);

                  /* Set up the chip. */

                  nfc_util_send_cmd_and_addrs(this,
                                    NAND_CMD_ERASE1, -1, page);

                  /* Start the erase. */

                  nfc_util_send_cmd(this, NAND_CMD_ERASE2);

                  /*
                   * If this is the last time through the loop, break out
                   * now so we don't try to wait (the HIL will call our
                   * waitfunc() for the final wait).
                   */

                  if (i >= (end - 1))
                        break;

                  /* Wait for the erase on the current chip to finish. */

                  nfc_util_wait_for_the_nfc(this, true);

            }

      }

      add_event("Exiting mal_erase_a_block", -1);

}

/**
 * mal_is_block_bad() - Abstract bad block check.
 *
 * @this:    Per-device data.
 * @chip:    The logical chip of interest.
 * @page:    The logical page number to read.
 */
 #if 0

/* TODO: Finish this function and plug it in. */

static int mal_is_block_bad(struct imx_nfc_data *this,
                                    unsigned chip, unsigned page)
{
      int                       we_are_interleaving;
      unsigned int              start;
      unsigned int              end;
      unsigned int              i;
      uint8_t                   *p;
      int                       return_value = 0;
      struct nand_chip          *nand = &this->nand;
      struct physical_geometry  *physical = &this->physical_geometry;
      struct logical_geometry   *logical  = &this->logical_geometry;

      /* Figure out if we're interleaving. */

      we_are_interleaving = logical->chip_count != physical->chip_count;

      /*
       * We're about to use the NAND Flash MTD layer's buffer, so invalidate
       * the page cache.
       */

      this->nand.pagebuf = -1;

      /*
       * Read the OOB of the given page, using the NAND Flash MTD's buffer.
       *
       * Notice that ECC is off, which it *must* be when scanning block marks.
       */

      mal_read_a_page(this, false,
            this->current_chip, this->page_address, 0, nand->oob_poi);

      /* Choose the loop bounds. */

      if (we_are_interleaving) {
            start = 0;
            end   = physical->chip_count;
      } else {
            start = chip;
            end   = start + 1;
      }

      /* Start scanning at the beginning of the OOB data. */

      p = nand->oob_poi;

      /* Loop over physical chips. */

      add_event("Looping over physical chips...", 0);

      for (i = start; i < end; i++, p += 5) {

            /* Examine the OOB for this chip. */

            if (p[nand->badblockpos] != 0xff) {
                  return_value = !0;
                  break;
            }

      }

      /* Return. */

      return return_value;

}
#endif

/**
 * mil_init() - Initializes the MTD Interface Layer.
 *
 * @this:    Per-device data.
 */
static void mil_init(struct imx_nfc_data *this)
{
      this->current_chip   = -1;    /* No chip is selected yet. */
      this->command_is_new = false; /* No command yet.          */
}

/**
 * mil_cmd_ctrl() - MTD Interface cmd_ctrl()
 *
 * @mtd:  A pointer to the owning MTD.
 * @dat:  The data signals to present to the chip.
 * @ctrl: The control signals to present to the chip.
 */
static void mil_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_dev_ready() - MTD Interface dev_ready()
 *
 * @mtd:   A pointer to the owning MTD.
 */
static int mil_dev_ready(struct mtd_info *mtd)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc dev_ready]\n");

      add_event("Entering mil_dev_ready", 1);

      if (this->nfc->is_ready(this)) {
            add_event("Exiting mil_dev_ready - Returning ready", -1);
            return !0;
      } else {
            add_event("Exiting mil_dev_ready - Returning busy", -1);
            return 0;
      }

}

/**
 * mil_select_chip() - MTD Interface select_chip()
 *
 * @mtd:   A pointer to the owning MTD.
 * @chip:  The chip number to select, or -1 to select no chip.
 */
static void mil_select_chip(struct mtd_info *mtd, int chip)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc select_chip] chip: %d\n", chip);

      /* Figure out what kind of transition this is. */

      if ((this->current_chip < 0) && (chip >= 0)) {
            start_event_trace("Entering mil_select_chip");
            if (this->pdata->force_ce)
                  this->nfc->set_force_ce(this, true);
            clk_enable(this->clock);
            add_event("Exiting mil_select_chip", -1);
      } else if ((this->current_chip >= 0) && (chip < 0)) {
            add_event("Entering mil_select_chip", 1);
            if (this->pdata->force_ce)
                  this->nfc->set_force_ce(this, false);
            clk_disable(this->clock);
            stop_event_trace("Exiting mil_select_chip");
      } else {
            add_event("Entering mil_select_chip", 1);
            add_event("Exiting mil_select_chip", -1);
      }

      this->current_chip = chip;

}

/**
 * mil_cmdfunc() - MTD Interface cmdfunc()
 *
 * This function handles NAND Flash command codes from the HIL. Since only the
 * HIL calls this function (none of the reference implementations we use do), it
 * needs to handle very few command codes.
 *
 * @mtd:      A pointer to the owning MTD.
 * @command:  The command code.
 * @column:   The column address associated with this command code, or -1 if no
 *            column address applies.
 * @page:     The page address associated with this command code, or -1 if no
 *            page address applies.
 */
static void mil_cmdfunc(struct mtd_info *mtd,
                              unsigned command, int column, int page)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc cmdfunc] command: 0x%02x, "
            "column: 0x%04x, page: 0x%06x\n", command, column, page);

      add_event("Entering mil_cmdfunc", 1);

      /* Record the command and the fact that it hasn't yet been sent. */

      this->command        = command;
      this->command_is_new = true;

      /*
       * Process the command code.
       *
       * Note the default case to trap unrecognized codes. Thus, every command
       * we support must have a case here, even if we don't have to do any
       * pre-processing work. If the HIL changes and starts sending commands
       * we haven't explicitly implemented, this will warn us.
       */

      switch (command) {

      case NAND_CMD_READ0:
            add_event("NAND_CMD_READ0", 0);
            /*
             * After calling this function to send the command and
             * addresses, the HIL will call ecc.read_page() or
             * ecc.read_page_raw() to collect the data.
             *
             * The column address from the HIL is always zero. The only
             * information we need to keep from this call is the page
             * address.
             */
            this->page_address = page;
            break;

      case NAND_CMD_STATUS:
            add_event("NAND_CMD_STATUS", 0);
            /*
             * After calling this function to send the command, the HIL
             * will call read_byte() once to collect the status.
             */
            break;

      case NAND_CMD_READID:
            add_event("NAND_CMD_READID", 0);
            /*
             * After calling this function to send the command, the HIL
             * will call read_byte() repeatedly to collect ID bytes.
             */
            break;

      case NAND_CMD_RESET:
            add_event("NAND_CMD_RESET", 0);
            mal_reset(this, this->current_chip);
            break;

      default:
            dev_emerg(this->dev, "Unsupported NAND Flash command code: "
                                          "0x%02x\n", command);
            BUG();
            break;

      }

      add_event("Exiting mil_cmdfunc", -1);

}

/**
 * mil_waitfunc() - MTD Interface waifunc()
 *
 * This function blocks until the current chip is ready and then returns the
 * contents of the chip's status register. The HIL only calls this function
 * after starting an erase operation.
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 */
static int mil_waitfunc(struct mtd_info *mtd, struct nand_chip *nand)
{
      int                  status;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc waitfunc]\n");

      add_event("Entering mil_waitfunc", 1);

      /* Wait for the NFC to finish. */

      nfc_util_wait_for_the_nfc(this, true);

      /* Get the status. */

      status = mal_get_status(this, this->current_chip);

      add_event("Exiting mil_waitfunc", -1);

      return status;

}

/**
 * mil_read_byte() - MTD Interface read_byte().
 *
 * @mtd:  A pointer to the owning MTD.
 */
static uint8_t mil_read_byte(struct mtd_info *mtd)
{
      uint8_t              byte = 0;
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      add_event("Entering mil_read_byte", 1);

      /*
       * The command sent by the HIL before it called this function determines
       * how we get the byte we're going to return.
       */

      switch (this->command) {

      case NAND_CMD_STATUS:
            add_event("NAND_CMD_STATUS", 0);
            byte = mal_get_status(this, this->current_chip);
            break;

      case NAND_CMD_READID:
            add_event("NAND_CMD_READID", 0);

            /*
             * Check if the command is new. If so, then the HIL just
             * recently called cmdfunc(), so the current chip isn't selected
             * and the command hasn't been sent to the chip.
             */

            if (this->command_is_new) {
                  add_event("Sending the \"Read ID\" command...", 0);
                  this->nfc->select_chip(this, this->current_chip);
                  nfc_util_send_cmd_and_addrs(this,
                                          NAND_CMD_READID, 0, -1);
                  this->command_is_new = false;
            }

            /* Read the ID byte. */

            add_event("Reading the ID byte...", 0);

            byte = this->nfc->read_cycle(this);

            break;

      default:
            dev_emerg(this->dev, "Unsupported NAND Flash command code: "
                                    "0x%02x\n", this->command);
            BUG();
            break;

      }

      DEBUG(MTD_DEBUG_LEVEL2,
                  "[imx_nfc read_byte] Returning: 0x%02x\n", byte);

      add_event("Exiting mil_read_byte", -1);

      return byte;

}

/**
 * mil_read_word() - MTD Interface read_word().
 *
 * @mtd:  A pointer to the owning MTD.
 */
static uint16_t mil_read_word(struct mtd_info *mtd)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
      return 0;
}

/**
 * mil_read_buf() - MTD Interface read_buf().
 *
 * @mtd:  A pointer to the owning MTD.
 * @buf:  The destination buffer.
 * @len:  The number of bytes to read.
 */
static void mil_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_write_buf() - MTD Interface write_buf().
 *
 * @mtd:  A pointer to the owning MTD.
 * @buf:  The source buffer.
 * @len:  The number of bytes to read.
 */
static void mil_write_buf(struct mtd_info *mtd, const uint8_t * buf, int len)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_verify_buf() - MTD Interface verify_buf().
 *
 * @mtd:  A pointer to the owning MTD.
 * @buf:  The destination buffer.
 * @len:  The number of bytes to read.
 */
static int mil_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
      return 0;
}

/**
 * mil_ecc_hwctl() - MTD Interface ecc.hwctl().
 *
 * @mtd:   A pointer to the owning MTD.
 * @mode:  The ECC mode.
 */
static void mil_ecc_hwctl(struct mtd_info *mtd, int mode)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_ecc_calculate() - MTD Interface ecc.calculate().
 *
 * @mtd:       A pointer to the owning MTD.
 * @dat:       A pointer to the source data.
 * @ecc_code:  A pointer to a buffer that will receive the resulting ECC.
 */
static int mil_ecc_calculate(struct mtd_info *mtd,
                              const uint8_t *dat, uint8_t *ecc_code)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
      return 0;
}

/**
 * mil_ecc_correct() - MTD Interface ecc.correct().
 *
 * @mtd:       A pointer to the owning MTD.
 * @dat:       A pointer to the source data.
 * @read_ecc:  A pointer to the ECC that was read from the medium.
 * @calc_ecc:  A pointer to the ECC that was calculated for the source data.
 */
static int mil_ecc_correct(struct mtd_info *mtd,
                  uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
      return 0;
}

/**
 * mil_ecc_read_page() - MTD Interface ecc.read_page().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the destination buffer.
 */
static int mil_ecc_read_page(struct mtd_info *mtd,
                              struct nand_chip *nand, uint8_t *buf)
{
      int                  ecc_status;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page]\n");

      add_event("Entering mil_ecc_read_page", 1);

      /* Read the page. */

      ecc_status =
            mal_read_a_page(this, true, this->current_chip,
                              this->page_address, buf, nand->oob_poi);

      /* Propagate ECC information. */

      if (ecc_status < 0) {
            add_event("ECC Failure", 0);
            mtd->ecc_stats.failed++;
      } else if (ecc_status > 0) {
            add_event("ECC Corrections", 0);
            mtd->ecc_stats.corrected += ecc_status;
      }

      add_event("Exiting mil_ecc_read_page", -1);

      return 0;

}

/**
 * mil_ecc_read_page_raw() - MTD Interface ecc.read_page_raw().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the destination buffer.
 */
static int mil_ecc_read_page_raw(struct mtd_info *mtd,
                              struct nand_chip *nand, uint8_t *buf)
{
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page_raw]\n");

      add_event("Entering mil_ecc_read_page_raw", 1);

      mal_read_a_page(this, false, this->current_chip,
                              this->page_address, buf, nand->oob_poi);

      add_event("Exiting mil_ecc_read_page_raw", -1);

      return 0;

}

/**
 * mil_ecc_write_page() - MTD Interface ecc.write_page().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the source buffer.
 */
static void mil_ecc_write_page(struct mtd_info *mtd,
                        struct nand_chip *nand, const uint8_t *buf)
{
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_ecc_write_page_raw() - MTD Interface ecc.write_page_raw().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the source buffer.
 */
static void mil_ecc_write_page_raw(struct mtd_info *mtd,
                        struct nand_chip *nand, const uint8_t *buf)
{
      struct imx_nfc_data  *this = nand->priv;
      unimplemented(this, __func__);
}

/**
 * mil_write_page() - MTD Interface ecc.write_page().
 *
 * @mtd:     A pointer to the owning MTD.
 * @nand:    A pointer to the owning NAND Flash MTD.
 * @buf:     A pointer to the source buffer.
 * @page:    The page number to write.
 * @cached:  Indicates cached programming (ignored).
 * @raw:     Indicates not to use ECC.
 */
static int mil_write_page(struct mtd_info *mtd,
            struct nand_chip *nand, const uint8_t *buf,
            int page, int cached, int raw)
{
      int                  return_value;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc write_page]\n");

      add_event("Entering mil_write_page", 1);

      return_value = mal_write_a_page(this, !raw,
                        this->current_chip, page, buf, nand->oob_poi);

      add_event("Exiting mil_write_page", -1);

      return return_value;

}

/**
 * mil_ecc_read_oob() - MTD Interface read_oob().
 *
 * @mtd:     A pointer to the owning MTD.
 * @nand:    A pointer to the owning NAND Flash MTD.
 * @page:    The page number to read.
 * @sndcmd:  Indicates this function should send a command to the chip before
 *           reading the out-of-band bytes. This is only false for small page
 *           chips that support auto-increment.
 */
static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
                                          int page, int sndcmd)
{
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_oob] "
            "page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No");

      add_event("Entering mil_ecc_read_oob", 1);

      mal_read_a_page(this, false,
                        this->current_chip, page, 0, nand->oob_poi);

      add_event("Exiting mil_ecc_read_oob", -1);

      /*
       * Return true, indicating that the next call to this function must send
       * a command.
       */

      return true;

}

/**
 * mil_ecc_write_oob() - MTD Interface write_oob().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @page:  The page number to write.
 */
static int mil_ecc_write_oob(struct mtd_info *mtd,
                              struct nand_chip *nand, int page)
{
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_write_oob] page: 0x%06x\n", page);

      /*
       * There are fundamental incompatibilities between the i.MX NFC and the
       * NAND Flash MTD model that make it essentially impossible to write the
       * out-of-band bytes.
       */

      dev_emerg(this->dev, "This driver doesn't support writing the OOB\n");
      WARN_ON(1);

      /* Return status. */

      return -EIO;

}

/**
 * mil_erase_cmd() - MTD Interface erase_cmd().
 *
 * We set the erase_cmd pointer in struct nand_chip to point to this function.
 * Thus, the HIL will call here for all erase operations.
 *
 * Strictly speaking, since the erase_cmd pointer is marked "Internal," we
 * shouldn't be doing this. However, the only reason the HIL uses that pointer
 * is to install a different function for erasing conventional NAND Flash or AND
 * Flash. Since AND Flash is obsolete and we don't support it, this isn't
 * important.
 *
 * Furthermore, to cleanly implement interleaving (which is critical to speeding
 * up erase operations), we want to "hook into" the operation at the highest
 * semantic level possible. If we don't hook this function, then the only way
 * we'll know that an erase is happening is when the HIL calls cmdfunc() with
 * an erase command. Implementing interleaving at that level is roughly a
 * billion times less desirable.
 *
 * This function does *not* wait for the operation to finish. The HIL will call
 * waitfunc() later to wait for the operation to finish.
 *
 * @mtd:   A pointer to the owning MTD.
 * @page:  A page address that identifies the block to erase.
 */
static void mil_erase_cmd(struct mtd_info *mtd, int page)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc erase_cmd] page: 0x%06x\n", page);

      add_event("Entering mil_erase_cmd", 1);

      mal_erase_a_block(this, this->current_chip, page);

      add_event("Exiting mil_erase_cmd", -1);

}

/**
 * mil_block_bad() - MTD Interface block_bad().
 *
 * @mtd:      A pointer to the owning MTD.
 * @ofs:      The offset of the block of interest, from the start of the medium.
 * @getchip:  Indicates this function must acquire the MTD before using it.
 */
#if 0

/* TODO: Finish this function and plug it in. */

static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
      unsigned int         chip;
      unsigned int         page;
      int                  return_value;
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc block_bad] page: 0x%06x\n", page);

      add_event("Entering mil_block_bad", 1);

      /* Compute the logical chip number that contains the given offset. */

      chip = (unsigned int) (ofs >> nand->chip_shift);

      /* Compute the logical page address within the logical chip. */

      page = ((unsigned int) (ofs >> nand->page_shift)) & nand->pagemask;

      /* Check if the block is bad. */

      return_value = mal_is_block_bad(this, chip, page);

      if (return_value)
            add_event("Bad block", 0);

      /* Return. */

      add_event("Exiting mil_block_bad", -1);

      return return_value;

}
#endif

/**
 * mil_scan_bbt() - MTD Interface scan_bbt().
 *
 * The HIL calls this function once, when it initializes the NAND Flash MTD.
 *
 * Nominally, the purpose of this function is to look for or create the bad
 * block table. In fact, since the HIL calls this function at the very end of
 * the initialization process started by nand_scan(), and the HIL doesn't have a
 * more formal mechanism, everyone "hooks" this function to continue the
 * initialization process.
 *
 * At this point, the physical NAND Flash chips have been identified and
 * counted, so we know the physical geometry. This enables us to make some
 * important configuration decisions.
 *
 * The return value of this function propogates directly back to this driver's
 * call to nand_scan(). Anything other than zero will cause this driver to
 * tear everything down and declare failure.
 *
 * @mtd:  A pointer to the owning MTD.
 */
static int mil_scan_bbt(struct mtd_info *mtd)
{
      struct nand_chip     *nand = mtd->priv;
      struct imx_nfc_data  *this = nand->priv;

      DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc scan_bbt] \n");

      add_event("Entering mil_scan_bbt", 1);

      /*
       * We replace the erase_cmd() function that the MTD NAND Flash system
       * has installed with our own. See mil_erase_cmd() for the reasons.
       */

      nand->erase_cmd = mil_erase_cmd;

      /*
       * Tell MTD users that the out-of-band area can't be written.
       *
       * This flag is not part of the standard kernel source tree. It comes
       * from a patch that touches both MTD and JFFS2.
       *
       * The problem is that, without this patch, JFFS2 believes it can write
       * the data area and the out-of-band area separately. This is wrong for
       * two reasons:
       *
       *     1)  Our NFC distributes out-of-band bytes throughout the page,
       *         intermingled with the data, and covered by the same ECC.
       *         Thus, it's not possible to write the out-of-band bytes and
       *         data bytes separately.
       *
       *     2)  Large page (MLC) Flash chips don't support partial page
       *         writes. You must write the entire page at a time. Thus, even
       *         if our NFC didn't force you to write out-of-band and data
       *         bytes together, it would *still* be a bad idea to do
       *         otherwise.
       */

      mtd->flags &= ~MTD_OOB_WRITEABLE;

      /* Set up geometry. */

      mal_set_geometry(this);

      /* We use the reference implementation for bad block management. */

      add_event("Exiting mil_scan_bbt", -1);

      return nand_scan_bbt(mtd, nand->badblock_pattern);

}

/**
 * parse_bool_param() - Parses the value of a boolean parameter string.
 *
 * @s: The string to parse.
 */
static int parse_bool_param(const char *s)
{

      if (!strcmp(s, "1") || !strcmp(s, "on") ||
                        !strcmp(s, "yes") ||    !strcmp(s, "true")) {
            return 1;
      } else if (!strcmp(s, "0") || !strcmp(s, "off") ||
                        !strcmp(s, "no") || !strcmp(s, "false")) {
            return 0;
      } else {
            return -1;
      }

}

/**
 * set_module_enable() - Controls whether this driver is enabled.
 *
 * Note that this state can be controlled from the command line. Disabling this
 * driver is sometimes useful for debugging.
 *
 * @s:   The new value of the parameter.
 * @kp:  The owning kernel parameter.
 */
static int set_module_enable(const char *s, struct kernel_param *kp)
{

      switch (parse_bool_param(s)) {

      case 1:
            imx_nfc_module_enable = true;
            break;

      case 0:
            imx_nfc_module_enable = false;
            break;

      default:
            return -EINVAL;
            break;

      }

      return 0;

}

/**
 * get_module_enable() - Indicates whether this driver is enabled.
 *
 * @p:   A pointer to a (small) buffer that will receive the response.
 * @kp:  The owning kernel parameter.
 */
static int get_module_enable(char *p, struct kernel_param *kp)
{
      p[0] = imx_nfc_module_enable ? '1' : '0';
      p[1] = 0;
      return 1;
}

#ifdef EVENT_REPORTING

/**
 * set_module_report_events() - Controls whether this driver reports events.
 *
 * @s:   The new value of the parameter.
 * @kp:  The owning kernel parameter.
 */
static int set_module_report_events(const char *s, struct kernel_param *kp)
{

      switch (parse_bool_param(s)) {

      case 1:
            imx_nfc_module_report_events = true;
            break;

      case 0:
            imx_nfc_module_report_events = false;
            reset_event_trace();
            break;

      default:
            return -EINVAL;
            break;

      }

      return 0;

}

/**
 * get_module_report_events() - Indicates whether the driver reports events.
 *
 * @p:   A pointer to a (small) buffer that will receive the response.
 * @kp:  The owning kernel parameter.
 */
static int get_module_report_events(char *p, struct kernel_param *kp)
{
      p[0] = imx_nfc_module_report_events ? '1' : '0';
      p[1] = 0;
      return 1;
}

/**
 * set_module_dump_events() - Forces the driver to dump current events.
 *
 * @s:   The new value of the parameter.
 * @kp:  The owning kernel parameter.
 */
static int set_module_dump_events(const char *s, struct kernel_param *kp)
{
      dump_event_trace();
      return 0;
}

#endif /*EVENT_REPORTING*/

/**
 * set_module_interleave_override() - Controls the interleave override.
 *
 * @s:   The new value of the parameter.
 * @kp:  The owning kernel parameter.
 */
static int set_module_interleave_override(const char *s,
                                          struct kernel_param *kp)
{

      if      (!strcmp(s, "-1"))
            imx_nfc_module_interleave_override = NEVER;
      else if (!strcmp(s, "0"))
            imx_nfc_module_interleave_override = DRIVER_CHOICE;
      else if (!strcmp(s, "1"))
            imx_nfc_module_interleave_override = ALWAYS;
      else
            return -EINVAL;

      return 0;

}

/**
 * get_module_interleave_override() - Indicates the interleave override state.
 *
 * @p:   A pointer to a (small) buffer that will receive the response.
 * @kp:  The owning kernel parameter.
 */
static int get_module_interleave_override(char *p, struct kernel_param *kp)
{
      return sprintf(p, "%d", imx_nfc_module_interleave_override);
}

/**
 * set_force_bytewise_copy() - Controls forced bytewise copy from/to the NFC.
 *
 * @s:   The new value of the parameter.
 * @kp:  The owning kernel parameter.
 */
static int set_module_force_bytewise_copy(const char *s,
                                          struct kernel_param *kp)
{

      switch (parse_bool_param(s)) {

      case 1:
            imx_nfc_module_force_bytewise_copy = true;
            break;

      case 0:
            imx_nfc_module_force_bytewise_copy = false;
            break;

      default:
            return -EINVAL;
            break;

      }

      return 0;

}

/**
 * get_force_bytewise_copy() - Indicates whether bytewise copy is being forced.
 *
 * @p:   A pointer to a (small) buffer that will receive the response.
 * @kp:  The owning kernel parameter.
 */
static int get_module_force_bytewise_copy(char *p, struct kernel_param *kp)
{
      p[0] = imx_nfc_module_force_bytewise_copy ? '1' : '0';
      p[1] = 0;
      return 1;
}

/* Module attributes that appear in sysfs. */

module_param_call(enable, set_module_enable, get_module_enable, 0, 0444);
MODULE_PARM_DESC(enable, "enables/disables probing");

#ifdef EVENT_REPORTING
module_param_call(report_events,
            set_module_report_events, get_module_report_events, 0, 0644);
MODULE_PARM_DESC(report_events, "enables/disables event reporting");

module_param_call(dump_events, set_module_dump_events, 0, 0, 0644);
MODULE_PARM_DESC(dump_events, "forces current event dump");
#endif

module_param_call(interleave_override, set_module_interleave_override,
                        get_module_interleave_override, 0, 0444);
MODULE_PARM_DESC(interleave_override, "overrides interleaving choice");

module_param_call(force_bytewise_copy, set_module_force_bytewise_copy,
                        get_module_force_bytewise_copy, 0, 0644);
MODULE_PARM_DESC(force_bytewise_copy, "forces bytewise copy from/to NFC");

/**
 * show_device_platform_info() - Shows the device's platform information.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_platform_info(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      int                           o = 0;
      unsigned int                  i;
      void                          *buffer_base;
      void                          *primary_base;
      void                          *secondary_base;
      unsigned int                  interrupt_number;
      struct resource               *r;
      struct imx_nfc_data           *this  = dev_get_drvdata(dev);
      struct platform_device        *pdev  = this->pdev;
      struct imx_nfc_platform_data  *pdata = this->pdata;
      struct mtd_partition          *partition;

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                              IMX_NFC_BUFFERS_ADDR_RES_NAME);

      buffer_base = (void *) r->start;

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                              IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);

      primary_base = (void *) r->start;

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                              IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);

      secondary_base = (void *) r->start;

      r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                              IMX_NFC_INTERRUPT_RES_NAME);

      interrupt_number = r->start;

      o += sprintf(buf,
            "NFC Major Version       : %u\n"
            "NFC Minor Version       : %u\n"
            "Force CE                : %s\n"
            "Target Cycle in ns      : %u\n"
            "Clock Name              : %s\n"
            "Interleave              : %s\n"
            "Buffer Base             : 0x%p\n"
            "Primary Registers Base  : 0x%p\n"
            "Secondary Registers Base: 0x%p\n"
            "Interrupt Number        : %u\n"
            ,
            pdata->nfc_major_version,
            pdata->nfc_minor_version,
            pdata->force_ce ? "Yes" : "No",
            pdata->target_cycle_in_ns,
            pdata->clock_name,
            pdata->interleave ? "Yes" : "No",
            buffer_base,
            primary_base,
            secondary_base,
            interrupt_number
            );

      #ifdef CONFIG_MTD_PARTITIONS

            o += sprintf(buf + o,
                  "Partition Count         : %u\n"
                  ,
                  pdata->partition_count
            );

            /* Loop over partitions. */

            for (i = 0; i < pdata->partition_count; i++) {

                  partition = pdata->partitions + i;

                  o += sprintf(buf+o, "  [%d]\n", i);
                  o += sprintf(buf+o, "  Name  : %s\n", partition->name);

                  switch (partition->offset) {

                  case MTDPART_OFS_NXTBLK:
                        o += sprintf(buf+o, "  Offset: "
                                          "MTDPART_OFS_NXTBLK\n");
                        break;
                  case MTDPART_OFS_APPEND:
                        o += sprintf(buf+o, "  Offset: "
                                          "MTDPART_OFS_APPEND\n");
                        break;
                  default:
                        o += sprintf(buf+o, "  Offset: %u (%u MiB)\n",
                              partition->offset,
                              partition->offset / (1024 * 1024));
                        break;

                  }

                  if (partition->size == MTDPART_SIZ_FULL) {
                        o += sprintf(buf+o, "  Size  : "
                                          "MTDPART_SIZ_FULL\n");
                  } else {
                        o += sprintf(buf+o, "  Size  : %u (%u MiB)\n",
                              partition->size,
                              partition->size / (1024 * 1024));
                  }

            }

      #endif

      return o;

}

/**
 * show_device_physical_geometry() - Shows the physical Flash device geometry.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_physical_geometry(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data       *this     = dev_get_drvdata(dev);
      struct physical_geometry  *physical = &this->physical_geometry;

      return sprintf(buf,
            "Chip Count             : %u\n"
            "Chip Size in Bytes     : %llu\n"
            "Block Size in Bytes    : %u\n"
            "Page Data Size in Bytes: %u\n"
            "Page OOB Size in Bytes : %u\n"
            ,
            physical->chip_count,
            physical->chip_size,
            physical->block_size,
            physical->page_data_size,
            physical->page_oob_size
      );

}

/**
 * show_device_nfc_info() - Shows the NFC-specific information.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_nfc_info(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      unsigned long        parent_clock_rate_in_hz;
      unsigned long        clock_rate_in_hz;
      struct clk           *parent_clock;
      struct imx_nfc_data  *this = dev_get_drvdata(dev);
      struct nfc_hal       *nfc  = this->nfc;

      parent_clock            = clk_get_parent(this->clock);
      parent_clock_rate_in_hz = clk_get_rate(parent_clock);
      clock_rate_in_hz        = clk_get_rate(this->clock);

      return sprintf(buf,
            "Major Version           : %u\n"
            "Minor Version           : %u\n"
            "Max Chip Count          : %u\n"
            "Max Buffer Count        : %u\n"
            "Spare Buffer Stride     : %u\n"
            "Has Secondary Registers : %s\n"
            "Can Be Symmetric        : %s\n"
            "Exposes Ready/Busy      : %s\n"
            "Parent Clock Rate in Hz : %lu\n"
            "Clock Rate in Hz        : %lu\n"
            "Symmetric Clock         : %s\n"
            ,
            nfc->major_version,
            nfc->minor_version,
            nfc->max_chip_count,
            nfc->max_buffer_count,
            nfc->spare_buf_stride,
            nfc->has_secondary_regs ? "Yes" : "No",
            nfc->can_be_symmetric ? "Yes" : "No",
            nfc->is_ready ? "Yes" : "No",
            parent_clock_rate_in_hz,
            clock_rate_in_hz,
            this->nfc->get_symmetric(this) ? "Yes" : "No"
      );

}

/**
 * show_device_nfc_geometry() - Shows the NFC view of the device geometry.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_nfc_geometry(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      return sprintf(buf,
            "Page Data Size in Bytes : %u\n"
            "Page OOB Size in Bytes  : %u\n"
            "ECC Algorithm           : %s\n"
            "ECC Strength            : %u\n"
            "Buffers in Use          : %u\n"
            "Spare Buffer Size in Use: %u\n"
            "Spare Buffer Spillover  : %u\n"
            ,
            this->nfc_geometry->page_data_size,
            this->nfc_geometry->page_oob_size,
            this->nfc_geometry->ecc_algorithm,
            this->nfc_geometry->ecc_strength,
            this->nfc_geometry->buffer_count,
            this->nfc_geometry->spare_buf_size,
            this->nfc_geometry->spare_buf_spill
      );

}

/**
 * show_device_logical_geometry() - Shows the logical device geometry.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_logical_geometry(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data      *this    = dev_get_drvdata(dev);
      struct logical_geometry  *logical = &this->logical_geometry;

      return sprintf(buf,
            "Chip Count             : %u\n"
            "Chip Size in Bytes     : %u\n"
            "Usable Size in Bytes   : %u\n"
            "Block Size in Bytes    : %u\n"
            "Page Data Size in Bytes: %u\n"
            "Page OOB Size in Bytes : %u\n"
            ,
            logical->chip_count,
            logical->chip_size,
            logical->usable_size,
            logical->block_size,
            logical->page_data_size,
            logical->page_oob_size
      );

}

/**
 * show_device_mtd_nand_info() - Shows the device's MTD NAND-specific info.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_mtd_nand_info(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      int                        o = 0;
      unsigned int               i;
      unsigned int               j;
      static const unsigned int  columns = 8;
      struct imx_nfc_data        *this = dev_get_drvdata(dev);
      struct nand_chip           *nand = &this->nand;

      o += sprintf(buf + o,
            "Options              : 0x%08x\n"
            "Chip Count           : %u\n"
            "Chip Size            : %lu\n"
            "Minimum Writable Size: %u\n"
            "Page Shift           : %u\n"
            "Page Mask            : 0x%x\n"
            "Block Shift          : %u\n"
            "BBT Block Shift      : %u\n"
            "Chip Shift           : %u\n"
            "Block Mark Offset    : %u\n"
            "Cached Page Number   : %d\n"
            ,
            nand->options,
            nand->numchips,
            nand->chipsize,
            nand->subpagesize,
            nand->page_shift,
            nand->pagemask,
            nand->phys_erase_shift,
            nand->bbt_erase_shift,
            nand->chip_shift,
            nand->badblockpos,
            nand->pagebuf
      );

      o += sprintf(buf + o,
            "ECC Byte Count       : %u\n"
            ,
            nand->ecc.layout->eccbytes
      );

      /* Loop over rows. */

      for (i = 0; (i * columns) < nand->ecc.layout->eccbytes; i++) {

            /* Loop over columns within rows. */

            for (j = 0; j < columns; j++) {

                  if (((i * columns) + j) >= nand->ecc.layout->eccbytes)
                        break;

                  o += sprintf(buf + o, " %3u",
                        nand->ecc.layout->eccpos[(i * columns) + j]);

            }

            o += sprintf(buf + o, "\n");

      }

      o += sprintf(buf + o,
            "OOB Available Bytes  : %u\n"
            ,
            nand->ecc.layout->oobavail
      );

      j = 0;

      for (i = 0; j < nand->ecc.layout->oobavail; i++) {

            j += nand->ecc.layout->oobfree[i].length;

            o += sprintf(buf + o,
                  "  [%3u, %2u]\n"
                  ,
                  nand->ecc.layout->oobfree[i].offset,
                  nand->ecc.layout->oobfree[i].length
            );

      }

      return o;

}

/**
 * show_device_mtd_info() - Shows the device's MTD-specific information.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_mtd_info(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      int                        o = 0;
      unsigned int               i;
      unsigned int               j;
      static const unsigned int  columns = 8;
      struct imx_nfc_data        *this = dev_get_drvdata(dev);
      struct mtd_info            *mtd  = &this->mtd;

      o += sprintf(buf + o,
            "Name               : %s\n"
            "Type               : %u\n"
            "Flags              : 0x%08x\n"
            "Size in Bytes      : %u\n"
            "Erase Region Count : %d\n"
            "Erase Size in Bytes: %u\n"
            "Write Size in Bytes: %u\n"
            "OOB Size in Bytes  : %u\n"
            "Errors Corrected   : %u\n"
            "Failed Reads       : %u\n"
            "Bad Block Count    : %u\n"
            "BBT Block Count    : %u\n"
            ,
            mtd->name,
            mtd->type,
            mtd->flags,
            mtd->size,
            mtd->numeraseregions,
            mtd->erasesize,
            mtd->writesize,
            mtd->oobsize,
            mtd->ecc_stats.corrected,
            mtd->ecc_stats.failed,
            mtd->ecc_stats.badblocks,
            mtd->ecc_stats.bbtblocks
      );

      o += sprintf(buf + o,
            "ECC Byte Count     : %u\n"
            ,
            mtd->ecclayout->eccbytes
      );

      /* Loop over rows. */

      for (i = 0; (i * columns) < mtd->ecclayout->eccbytes; i++) {

            /* Loop over columns within rows. */

            for (j = 0; j < columns; j++) {

                  if (((i * columns) + j) >= mtd->ecclayout->eccbytes)
                        break;

                  o += sprintf(buf + o, " %3u",
                        mtd->ecclayout->eccpos[(i * columns) + j]);

            }

            o += sprintf(buf + o, "\n");

      }

      o += sprintf(buf + o,
            "OOB Available Bytes: %u\n"
            ,
            mtd->ecclayout->oobavail
      );

      j = 0;

      for (i = 0; j < mtd->ecclayout->oobavail; i++) {

            j += mtd->ecclayout->oobfree[i].length;

            o += sprintf(buf + o,
                  "  [%3u, %2u]\n"
                  ,
                  mtd->ecclayout->oobfree[i].offset,
                  mtd->ecclayout->oobfree[i].length
            );

      }

      return o;

}

/**
 * show_device_bbt_pages() - Shows the pages in which BBT's appear.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_bbt_pages(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      int                  o = 0;
      unsigned int         i;
      struct imx_nfc_data  *this = dev_get_drvdata(dev);
      struct nand_chip     *nand = &this->nand;

      /* Loop over main BBT pages. */

      if (nand->bbt_td)
            for (i = 0; i < NAND_MAX_CHIPS; i++)
                  o += sprintf(buf + o, "%d: 0x%08x\n",
                                    i, nand->bbt_td->pages[i]);

      /* Loop over mirror BBT pages. */

      if (nand->bbt_md)
            for (i = 0; i < NAND_MAX_CHIPS; i++)
                  o += sprintf(buf + o, "%d: 0x%08x\n",
                                    i, nand->bbt_md->pages[i]);

      return o;

}

/**
 * show_device_cycle_in_ns() - Shows the device's cycle in ns.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_cycle_in_ns(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);
      return sprintf(buf, "%u\n", get_cycle_in_ns(this));
}

/**
 * store_device_cycle_in_ns() - Sets the device's cycle in ns.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer containing a new attribute value.
 * @size:  The size of the buffer.
 */
static ssize_t store_device_cycle_in_ns(struct device *dev,
            struct device_attribute *attr, const char *buf, size_t size)
{
      int                  error;
      unsigned long        new_cycle_in_ns;
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      /* Look for nonsense. */

      if (!size)
            return -EINVAL;

      /* Try to understand the new cycle period. */

      if (strict_strtoul(buf, 0, &new_cycle_in_ns))
            return -EINVAL;

      /* Try to implement the new cycle period. */

      error = this->nfc->set_closest_cycle(this, new_cycle_in_ns);

      if (error)
            return -EINVAL;

      /* Return success. */

      return size;

}

/**
 * show_device_interrupt_override() - Shows the device's interrupt override.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_interrupt_override(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      switch (this->interrupt_override) {

      case NEVER:
            return sprintf(buf, "-1\n");
            break;

      case DRIVER_CHOICE:
            return sprintf(buf, "0\n");
            break;

      case ALWAYS:
            return sprintf(buf, "1\n");
            break;

      default:
            return sprintf(buf, "?\n");
            break;

      }

}

/**
 * store_device_interrupt_override() - Sets the device's interrupt override.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer containing a new attribute value.
 * @size:  The size of the buffer.
 */
static ssize_t store_device_interrupt_override(struct device *dev,
            struct device_attribute *attr, const char *buf, size_t size)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      if      (!strcmp(buf, "-1"))
            this->interrupt_override = NEVER;
      else if (!strcmp(buf, "0"))
            this->interrupt_override = DRIVER_CHOICE;
      else if (!strcmp(buf, "1"))
            this->interrupt_override = ALWAYS;
      else
            return -EINVAL;

      return size;

}

/**
 * show_device_auto_op_override() - Shows the device's automatic op override.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_auto_op_override(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      switch (this->auto_op_override) {

      case NEVER:
            return sprintf(buf, "-1\n");
            break;

      case DRIVER_CHOICE:
            return sprintf(buf, "0\n");
            break;

      case ALWAYS:
            return sprintf(buf, "1\n");
            break;

      default:
            return sprintf(buf, "?\n");
            break;

      }

}

/**
 * store_device_auto_op_override() - Sets the device's automatic op override.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer containing a new attribute value.
 * @size:  The size of the buffer.
 */
static ssize_t store_device_auto_op_override(struct device *dev,
            struct device_attribute *attr, const char *buf, size_t size)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      if      (!strcmp(buf, "-1"))
            this->auto_op_override = NEVER;
      else if (!strcmp(buf, "0"))
            this->auto_op_override = DRIVER_CHOICE;
      else if (!strcmp(buf, "1"))
            this->auto_op_override = ALWAYS;
      else
            return -EINVAL;

      return size;

}

/**
 * show_device_inject_ecc_error() - Shows the device's error injection flag.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer that will receive a representation of the attribute.
 */
static ssize_t show_device_inject_ecc_error(struct device *dev,
                        struct device_attribute *attr, char *buf)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      return sprintf(buf, "%d\n", this->inject_ecc_error);

}

/**
 * store_device_inject_ecc_error() - Sets the device's error injection flag.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer containing a new attribute value.
 * @size:  The size of the buffer.
 */
static ssize_t store_device_inject_ecc_error(struct device *dev,
            struct device_attribute *attr, const char *buf, size_t size)
{
      unsigned long        new_inject_ecc_error;
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      /* Look for nonsense. */

      if (!size)
            return -EINVAL;

      /* Try to understand the new cycle period. */

      if (strict_strtol(buf, 0, &new_inject_ecc_error))
            return -EINVAL;

      /* Store the value. */

      this->inject_ecc_error = new_inject_ecc_error;

      /* Return success. */

      return size;

}

/**
 * store_device_invalidate_page_cache() - Invalidates the device's page cache.
 *
 * @dev:   The device of interest.
 * @attr:  The attribute of interest.
 * @buf:   A buffer containing a new attribute value.
 * @size:  The size of the buffer.
 */
static ssize_t store_device_invalidate_page_cache(struct device *dev,
            struct device_attribute *attr, const char *buf, size_t size)
{
      struct imx_nfc_data  *this = dev_get_drvdata(dev);

      /* Invalidate the page cache. */

      this->nand.pagebuf = -1;

      /* Return success. */

      return size;

}

/* Device attributes that appear in sysfs. */

static DEVICE_ATTR(platform_info    , 0444, show_device_platform_info    , 0);
static DEVICE_ATTR(physical_geometry, 0444, show_device_physical_geometry, 0);
static DEVICE_ATTR(nfc_info         , 0444, show_device_nfc_info         , 0);
static DEVICE_ATTR(nfc_geometry     , 0444, show_device_nfc_geometry     , 0);
static DEVICE_ATTR(logical_geometry , 0444, show_device_logical_geometry , 0);
static DEVICE_ATTR(mtd_nand_info    , 0444, show_device_mtd_nand_info    , 0);
static DEVICE_ATTR(mtd_info         , 0444, show_device_mtd_info         , 0);
static DEVICE_ATTR(bbt_pages        , 0444, show_device_bbt_pages        , 0);

static DEVICE_ATTR(cycle_in_ns, 0644,
      show_device_cycle_in_ns, store_device_cycle_in_ns);

static DEVICE_ATTR(interrupt_override, 0644,
      show_device_interrupt_override, store_device_interrupt_override);

static DEVICE_ATTR(auto_op_override, 0644,
      show_device_auto_op_override, store_device_auto_op_override);

static DEVICE_ATTR(inject_ecc_error, 0644,
      show_device_inject_ecc_error, store_device_inject_ecc_error);

static DEVICE_ATTR(invalidate_page_cache, 0644,
      0, store_device_invalidate_page_cache);

static struct device_attribute *device_attributes[] = {
      &dev_attr_platform_info,
      &dev_attr_physical_geometry,
      &dev_attr_nfc_info,
      &dev_attr_nfc_geometry,
      &dev_attr_logical_geometry,
      &dev_attr_mtd_nand_info,
      &dev_attr_mtd_info,
      &dev_attr_bbt_pages,
      &dev_attr_cycle_in_ns,
      &dev_attr_interrupt_override,
      &dev_attr_auto_op_override,
      &dev_attr_inject_ecc_error,
      &dev_attr_invalidate_page_cache,
};

/**
 * validate_the_platform() - Validates information about the platform.
 *
 * Note that this function doesn't validate the NFC version. That's done when
 * the probing process attempts to configure for the specific hardware version.
 *
 * @pdev:   A pointer to the platform device.
 */
static int validate_the_platform(struct platform_device *pdev)
{
      struct device                 *dev   = &pdev->dev;
      struct imx_nfc_platform_data  *pdata = pdev->dev.platform_data;

      /* Validate the clock name. */

      if (!pdata->clock_name) {
            dev_err(dev, "No clock name\n");
            return -ENXIO;
      }

      /* Validate the partitions. */

      if ((pdata->partitions && (!pdata->partition_count)) ||
                  (!pdata->partitions && (pdata->partition_count))) {
            dev_err(dev, "Bad partition data\n");
            return -ENXIO;
      }

      /* Return success */

      return 0;

}

/**
 * set_up_the_nfc_hal() - Sets up for the specific NFC hardware HAL.
 *
 * @this:  Per-device data.
 */
static int set_up_the_nfc_hal(struct imx_nfc_data *this)
{
      unsigned int                  i;
      struct nfc_hal                *p;
      struct imx_nfc_platform_data  *pdata = this->pdata;

      for (i = 0; i < ARRAY_SIZE(nfc_hals); i++) {

            p = nfc_hals[i];

            /*
             * Restrict to 3.2 until others are fully implemented.
             *
             * TODO: Remove this.
             */

            if ((p->major_version != 3) && (p->minor_version != 2))
                  continue;

            if ((p->major_version == pdata->nfc_major_version) &&
                  (p->minor_version == pdata->nfc_minor_version)) {
                  this->nfc = p;
                  return 0;
                  break;
            }

      }

      dev_err(this->dev, "Unkown NFC version %d.%d\n",
                  pdata->nfc_major_version, pdata->nfc_minor_version);

      return !0;

}

/**
 * acquire_resources() - Tries to acquire resources.
 *
 * @this:  Per-device data.
 */
static int acquire_resources(struct imx_nfc_data *this)
{

      int                           error  = 0;
      struct platform_device        *pdev  = this->pdev;
      struct device                 *dev   = this->dev;
      struct imx_nfc_platform_data  *pdata = this->pdata;
      struct resource               *r;

      /* Find the buffers and map them. */

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                    IMX_NFC_BUFFERS_ADDR_RES_NAME);

      if (!r) {
            dev_err(dev, "Can't get '%s'\n", IMX_NFC_BUFFERS_ADDR_RES_NAME);
            error = -ENXIO;
            goto exit_buffers;
      }

      this->buffers = ioremap(r->start, r->end - r->start + 1);

      if (!this->buffers) {
            dev_err(dev, "Can't remap buffers\n");
            error = -EIO;
            goto exit_buffers;
      }

      /* Find the primary registers and map them. */

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                              IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);

      if (!r) {
            dev_err(dev, "Can't get '%s'\n",
                              IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
            error = -ENXIO;
            goto exit_primary_registers;
      }

      this->primary_regs = ioremap(r->start, r->end - r->start + 1);

      if (!this->primary_regs) {
            dev_err(dev, "Can't remap the primary registers\n");
            error = -EIO;
            goto exit_primary_registers;
      }

      /* Check for secondary registers. */

      r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                              IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);

      if (r && !this->nfc->has_secondary_regs) {

            dev_err(dev, "Resource '%s' should not be present\n",
                              IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
            error = -ENXIO;
            goto exit_secondary_registers;

      }

      if (this->nfc->has_secondary_regs) {

            if (!r) {
                  dev_err(dev, "Can't get '%s'\n",
                              IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
                  error = -ENXIO;
                  goto exit_secondary_registers;
            }

            this->secondary_regs = ioremap(r->start, r->end - r->start + 1);

            if (!this->secondary_regs) {
                  dev_err(dev,
                        "Can't remap the secondary registers\n");
                  error = -EIO;
                  goto exit_secondary_registers;
            }

      }

      /* Find out what our interrupt is and try to own it. */

      r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                                    IMX_NFC_INTERRUPT_RES_NAME);

      if (!r) {
            dev_err(dev, "Can't get '%s'\n", IMX_NFC_INTERRUPT_RES_NAME);
            error = -ENXIO;
            goto exit_irq;
      }

      this->interrupt = r->start;

      error = request_irq(this->interrupt,
                        nfc_util_isr, 0, this->dev->bus_id, this);

      if (error) {
            dev_err(dev, "Can't own interrupt %d\n", this->interrupt);
            goto exit_irq;
      }

      /* Find out the name of our clock and try to own it. */

      this->clock = clk_get(dev, pdata->clock_name);

      if (this->clock == ERR_PTR(-ENOENT)) {
            dev_err(dev, "Can't get clock '%s'\n", pdata->clock_name);
            error = -ENXIO;
            goto exit_clock;
      }

      /* Return success. */

      return 0;

      /* Error return paths begin here. */

exit_clock:
      free_irq(this->interrupt, this);
exit_irq:
      if (this->secondary_regs)
            iounmap(this->secondary_regs);
exit_secondary_registers:
      iounmap(this->primary_regs);
exit_primary_registers:
      iounmap(this->buffers);
exit_buffers:
      return error;

}

/**
 * release_resources() - Releases resources.
 *
 * @this:  Per-device data.
 */
static void release_resources(struct imx_nfc_data *this)
{

      /* Release our clock. */

      clk_disable(this->clock);
      clk_put(this->clock);

      /* Release our interrupt. */

      free_irq(this->interrupt, this);

      /* Release mapped memory. */

      iounmap(this->buffers);
      iounmap(this->primary_regs);
      if (this->secondary_regs)
            iounmap(this->secondary_regs);

}

/**
 * register_with_mtd() - Registers this medium with MTD.
 *
 * @this:  Per-device data.
 */
static int register_with_mtd(struct imx_nfc_data *this)
{
      int                           error  = 0;
      struct mtd_info               *mtd   = &this->mtd;
      struct nand_chip              *nand  = &this->nand;
      struct device                 *dev   = this->dev;
      struct imx_nfc_platform_data  *pdata = this->pdata;

      /* Link the MTD structures together, along with our own data. */

      mtd->priv  = nand;
      nand->priv = this;

      /* Prepare the MTD structure. */

      mtd->owner = THIS_MODULE;

      /*
       * Signal Control Functions
       */

      nand->cmd_ctrl = mil_cmd_ctrl;

      /*
       * Chip Control Functions
       *
       * Not all of our NFC hardware versions expose Ready/Busy signals. For
       * versions that don't, the is_ready function pointer will be NULL. In
       * those cases, we leave the dev_ready member unassigned, which will
       * cause the HIL to use a reference implementation's algorithm to
       * discover when the hardware is ready.
       */

      nand->select_chip = mil_select_chip;
      nand->cmdfunc     = mil_cmdfunc;
      nand->waitfunc    = mil_waitfunc;

      if (this->nfc->is_ready)
            nand->dev_ready = mil_dev_ready;

      /*
       * Low-level I/O Functions
       */

      nand->read_byte  = mil_read_byte;
      nand->read_word  = mil_read_word;
      nand->read_buf   = mil_read_buf;
      nand->write_buf  = mil_write_buf;
      nand->verify_buf = mil_verify_buf;

      /*
       * ECC Control Functions
       */

      nand->ecc.hwctl     = mil_ecc_hwctl;
      nand->ecc.calculate = mil_ecc_calculate;
      nand->ecc.correct   = mil_ecc_correct;

      /*
       * ECC-Aware I/O Functions
       */

      nand->ecc.read_page      = mil_ecc_read_page;
      nand->ecc.read_page_raw  = mil_ecc_read_page_raw;
      nand->ecc.write_page     = mil_ecc_write_page;
      nand->ecc.write_page_raw = mil_ecc_write_page_raw;

      /*
       * High-level I/O Functions
       */

      nand->write_page    = mil_write_page;
      nand->ecc.read_oob  = mil_ecc_read_oob;
      nand->ecc.write_oob = mil_ecc_write_oob;

      /*
       * Bad Block Marking Functions
       *
       * We want to use the reference block_bad and block_markbad
       * implementations, so we don't assign those members.
       */

      nand->scan_bbt = mil_scan_bbt;

      /*
       * Error Recovery Functions
       *
       * We don't fill in the errstat function pointer because it's optional
       * and we don't have a need for it.
       */

      /*
       * Device Attributes
       *
       * We don't fill in the chip_delay member because we don't have a need
       * for it.
       *
       * We support only 8-bit Flash bus width.
       */

      /*
       * ECC Attributes
       *
       * Members that aren't set here are configured by a version-specific
       * set_geometry() function.
       */

      nand->ecc.mode    = NAND_ECC_HW;
      nand->ecc.size    = NFC_MAIN_BUF_SIZE;
      nand->ecc.prepad  = 0;
      nand->ecc.postpad = 0;

      /*
       * Bad Block Management Attributes
       *
       * We don't fill in the following attributes:
       *
       *     badblockpos
       *     bbt
       *     badblock_pattern
       *     bbt_erase_shift
       *
       * These attributes aren't hardware-specific, and the HIL makes fine
       * choices without our help.
       */

      nand->options |= NAND_USE_FLASH_BBT;
      nand->bbt_td   = &bbt_main_descriptor;
      nand->bbt_md   = &bbt_mirror_descriptor;

      /*
       * Device Control
       *
       * We don't fill in the controller attribute. In principle, we could set
       * up a structure to represent the controller. However, since it's
       * vanishingly improbable that we'll have more than one medium behind
       * the controller, it's not worth the effort. We let the HIL handle it.
       */

      /*
       * Memory-mapped I/O
       *
       * We don't fill in the following attributes:
       *
       *     IO_ADDR_R
       *     IO_ADDR_W
       *
       * None of these are necessary because we don't have a memory-mapped
       * implementation.
       */

      /*
       * Install a "fake" ECC layout.
       *
       * We'll be calling nand_scan() to do the final MTD setup. If we haven't
       * already chosen an ECC layout, then nand_scan() will choose one based
       * on the part geometry it discovers. Unfortunately, it doesn't make
       * good choices. It would be best if we could install the correct ECC
       * layout now, before we call nand_scan(). We can't do that because we
       * don't know the medium geometry yet. Here, we install a "fake" ECC
       * layout just to stop nand_scan() from trying to pick on for itself.
       * Later, in imx_nfc_scan_bbt(), when we know the medium geometry, we'll
       * install the correct choice.
       *
       * Of course, this tactic depends critically on nand_scan() not using
       * the fake layout before we can install a good one. This is in fact the
       * case.
       */

      nand->ecc.layout = &nfc_geometry_512_16_RS_ECC1.mtd_layout;

      /*
       * Ask the NAND Flash system to scan for chips.
       *
       * This will fill in reference implementations for all the members of
       * the MTD structures that we didn't set, and will make the medium fully
       * usable.
       */

      error = nand_scan(mtd, this->nfc->max_chip_count);

      if (error) {
            dev_err(dev, "Chip scan failed\n");
            error = -ENXIO;
            goto exit_scan;
      }

      /* Register the MTD that represents the entire medium. */

      mtd->name = "NAND Flash Medium";

      add_mtd_device(mtd);

      /* Check if we're doing partitions and register MTD's accordingly. */

      #ifdef CONFIG_MTD_PARTITIONS

            /*
             * Look for partition information. If we find some, install
             * them. Otherwise, use the partitions handed to us by the
             * platform.
             */

            this->partition_count =
                  parse_mtd_partitions(mtd, partition_source_types,
                                          &this->partitions, 0);

            if ((this->partition_count <= 0) && (pdata->partitions)) {
                  this->partition_count = pdata->partition_count;
                  this->partitions      = pdata->partitions;
            }

            if (this->partitions)
                  add_mtd_partitions(mtd, this->partitions,
                                          this->partition_count);

      #endif

      /* Return success. */

      return 0;

      /* Error return paths begin here. */

exit_scan:
      return error;

}

/**
 * unregister_with_mtd() - Unregisters this medium with MTD.
 *
 * @this:  Per-device data.
 */
static void unregister_with_mtd(struct imx_nfc_data *this)
{

      /* Get MTD to let go. */

      nand_release(&this->mtd);

}

/**
 * manage_sysfs_files() - Creates/removes sysfs files for this device.
 *
 * @this:  Per-device data.
 */
static void manage_sysfs_files(struct imx_nfc_data *this, int create)
{
      int                      error;
      unsigned int             i;
      struct device_attribute  **attr;

      for (i = 0, attr = device_attributes;
                  i < ARRAY_SIZE(device_attributes); i++, attr++) {

            if (create) {
                  error = device_create_file(this->dev, *attr);
                  if (error) {
                        while (--attr >= device_attributes)
                              device_remove_file(this->dev, *attr);
                        return;
                  }
            } else {
                  device_remove_file(this->dev, *attr);
            }

      }

}

/**
 * imx_nfc_probe() - Probes for a device and, if possible, takes ownership.
 *
 * @pdev:  A pointer to the platform device.
 */
static int imx_nfc_probe(struct platform_device *pdev)
{
      int                           error  = 0;
      char                          *symmetric_clock;
      struct clk                    *parent_clock;
      unsigned long                 parent_clock_rate_in_hz;
      unsigned long                 parent_clock_rate_in_mhz;
      unsigned long                 nfc_clock_rate_in_hz;
      unsigned long                 nfc_clock_rate_in_mhz;
      unsigned long                 clock_divisor;
      unsigned long                 cycle_in_ns;
      struct device                 *dev   = &pdev->dev;
      struct imx_nfc_platform_data  *pdata = pdev->dev.platform_data;
      struct imx_nfc_data           *this  = 0;

      /* Say hello. */

      dev_info(dev, "Probing...\n");

      /* Check if we're enabled. */

      if (!imx_nfc_module_enable) {
            dev_info(dev, "Disabled\n");
            return -ENXIO;
      }

      /* Validate the platform device data. */

      error = validate_the_platform(pdev);

      if (error)
            goto exit_validate_platform;

      /* Allocate memory for the per-device data. */

      this = kzalloc(sizeof(*this), GFP_KERNEL);

      if (!this) {
            dev_err(dev, "Failed to allocate per-device memory\n");
            error = -ENOMEM;
            goto exit_allocate_this;
      }

      /* Link our per-device data to the owning device. */

      platform_set_drvdata(pdev, this);

      /* Fill in the convenience pointers in our per-device data. */

      this->pdev  = pdev;
      this->dev   = &pdev->dev;
      this->pdata = pdata;

      /* Initialize the interrupt service pathway. */

      init_completion(&this->done);

      /* Set up the NFC HAL. */

      error = set_up_the_nfc_hal(this);

      if (error)
            goto exit_set_up_nfc_hal;

      /* Attempt to acquire the resources we need. */

      error = acquire_resources(this);

      if (error)
            goto exit_acquire_resources;

      /* Initialize the NFC HAL. */

      if (this->nfc->init(this)) {
            error = -ENXIO;
            goto exit_nfc_init;
      }

      /* Tell the platform we're bringing this device up. */

      if (pdata->init)
            error = pdata->init();

      if (error)
            goto exit_platform_init;

      /* Report. */

      parent_clock             = clk_get_parent(this->clock);
      parent_clock_rate_in_hz  = clk_get_rate(parent_clock);
      parent_clock_rate_in_mhz = parent_clock_rate_in_hz / 1000000;
      nfc_clock_rate_in_hz     = clk_get_rate(this->clock);
      nfc_clock_rate_in_mhz    = nfc_clock_rate_in_hz / 1000000;

      clock_divisor   = parent_clock_rate_in_hz / nfc_clock_rate_in_hz;
      symmetric_clock = this->nfc->get_symmetric(this) ? "Yes" : "No";
      cycle_in_ns     = get_cycle_in_ns(this);

      dev_dbg(dev, "-------------\n");
      dev_dbg(dev, "Configuration\n");
      dev_dbg(dev, "-------------\n");
      dev_dbg(dev, "NFC Version      : %d.%d\n"  , this->nfc->major_version,
                                         this->nfc->minor_version);
      dev_dbg(dev, "Buffers          : 0x%p\n"   , this->buffers);
      dev_dbg(dev, "Primary Regs     : 0x%p\n"   , this->primary_regs);
      dev_dbg(dev, "Secondary Regs   : 0x%p\n"   , this->secondary_regs);
      dev_dbg(dev, "Interrupt        : %u\n"     , this->interrupt);
      dev_dbg(dev, "Clock Name       : %s\n"     , pdata->clock_name);
      dev_dbg(dev, "Parent Clock Rate: %lu Hz (%lu MHz)\n",
                                         parent_clock_rate_in_hz,
                                         parent_clock_rate_in_mhz);
      dev_dbg(dev, "Clock Divisor    : %lu\n",     clock_divisor);
      dev_dbg(dev, "NFC Clock Rate   : %lu Hz (%lu MHz)\n",
                                         nfc_clock_rate_in_hz,
                                         nfc_clock_rate_in_mhz);
      dev_dbg(dev, "Symmetric Clock  : %s\n"     , symmetric_clock);
      dev_dbg(dev, "Actual Cycle     : %lu ns\n" , cycle_in_ns);
      dev_dbg(dev, "Target Cycle     : %u ns\n"  , pdata->target_cycle_in_ns);

      /* Initialize the Medium Abstraction Layer. */

      mal_init(this);

      /* Initialize the MTD Interface Layer. */

      mil_init(this);

      /* Register this medium with MTD. */

      error = register_with_mtd(this);

      if (error)
            goto exit_mtd_registration;

      /* Create sysfs entries for this device. */

      manage_sysfs_files(this, true);

      /* Return success. */

      return 0;

      /* Error return paths begin here. */

exit_mtd_registration:
      if (pdata->exit)
            pdata->exit();
exit_platform_init:
      this->nfc->exit(this);
exit_nfc_init:
exit_acquire_resources:
exit_set_up_nfc_hal:
      platform_set_drvdata(pdev, NULL);
      kfree(this);
exit_allocate_this:
exit_validate_platform:
      return error;

}

/**
 * imx_nfc_remove() - Dissociates this driver from the given device.
 *
 * @pdev:  A pointer to the device.
 */
static int __exit imx_nfc_remove(struct platform_device *pdev)
{
      struct imx_nfc_data  *this  = platform_get_drvdata(pdev);

      /* Remove sysfs entries for this device. */

      manage_sysfs_files(this, false);

      /* Unregister with the NAND Flash MTD system. */

      unregister_with_mtd(this);

      /* Tell the platform we're shutting down this device. */

      if (this->pdata->exit)
            this->pdata->exit();

      /* Shut down the NFC. */

      this->nfc->exit(this);

      /* Release our resources. */

      release_resources(this);

      /* Unlink our per-device data from the platform device. */

      platform_set_drvdata(pdev, NULL);

      /* Free our per-device data. */

      kfree(this);

      /* Return success. */

      return 0;
}

#ifdef CONFIG_PM

/**
 * suspend() - Puts the NFC in a low power state.
 *
 * Refer to Documentation/driver-model/driver.txt for more information.
 *
 * @pdev:  A pointer to the device.
 * @state: The new power state.
 */

static int imx_nfc_suspend(struct platform_device *pdev, pm_message_t state)
{
      int                  error = 0;
      struct imx_nfc_data  *this = platform_get_drvdata(pdev);
      struct mtd_info      *mtd  = &this->mtd;
      struct device        *dev  = &this->pdev->dev;

      dev_dbg(dev, "Suspending...\n");

      /* Suspend MTD's use of this device. */

      error = mtd->suspend(mtd);

      /* Suspend the actual hardware. */

      clk_disable(this->clock);

      return error;

}

/**
 * resume() - Brings the NFC back from a low power state.
 *
 * Refer to Documentation/driver-model/driver.txt for more information.
 *
 * @pdev:  A pointer to the device.
 */
static int imx_nfc_resume(struct platform_device *pdev)
{
      struct imx_nfc_data  *this = platform_get_drvdata(pdev);
      struct mtd_info      *mtd  = &this->mtd;
      struct device        *dev  = &this->pdev->dev;

      dev_dbg(dev, "Resuming...\n");

      /* Resume MTD's use of this device. */

      mtd->resume(mtd);

      return 0;

}

#else

#define suspend  NULL
#define resume   NULL

#endif  /* CONFIG_PM */

/*
 * This structure represents this driver to the platform management system.
 */
static struct platform_driver imx_nfc_driver = {
      .driver = {
            .name = IMX_NFC_DRIVER_NAME,
      },
      .probe   = imx_nfc_probe,
      .remove  = __exit_p(imx_nfc_remove),
      .suspend = imx_nfc_suspend,
      .resume  = imx_nfc_resume,
};

/**
 * imx_nfc_init() - Initializes this module.
 */
static int __init imx_nfc_init(void)
{

      pr_info("i.MX NFC driver %s\n", DRIVER_VERSION);

      /* Register this driver with the platform management system. */

      if (platform_driver_register(&imx_nfc_driver) != 0) {
            pr_err("i.MX NFC driver registration failed\n");
            return -ENODEV;
      }

      return 0;

}

/**
 * imx_nfc_exit() - Deactivates this module.
 */
static void __exit imx_nfc_exit(void)
{
      pr_debug("i.MX NFC driver exiting...\n");
      platform_driver_unregister(&imx_nfc_driver);
}

module_init(imx_nfc_init);
module_exit(imx_nfc_exit);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX NAND Flash Controller Driver");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index