diff options
author | Dinh Nguyen <Dinh.Nguyen@freescale.com> | 2010-08-17 16:46:40 -0500 |
---|---|---|
committer | Dinh Nguyen <Dinh.Nguyen@freescale.com> | 2010-09-01 10:42:59 -0500 |
commit | 3eb9039c5392abaabd96d08c0006ea4066346a60 (patch) | |
tree | 1b23d62933572db70a4aef7476a07e73f75e43d1 /drivers/mtd/nand | |
parent | 4dcb1e4f577f7331930301798da548447208f35a (diff) |
ENGR00126692-3: Upgrade kernel to 2.6.35
This patch contains changes to /drivers files
Contains all checkpatch and copyright fixes.
Acked-by: Rob Herring <r.herring@freescale.com>
Signed-off-by: Dinh Nguyen <Dinh.Nguyen@freescale.com>
Diffstat (limited to 'drivers/mtd/nand')
25 files changed, 25294 insertions, 1 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ffc3720929f1..12ee9059c948 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -432,6 +432,68 @@ config MTD_NAND_NANDSIM The simulator may simulate various NAND flash chips for the MTD nand layer. +config MTD_NAND_IMX_NFC + tristate "i.MX NAND Flash Controller driver" + depends on MTD_NAND && (ARCH_MX2 || ARCH_MX3 || ARCH_MX5) + help + Enables the i.MX NAND Flash controller driver. + +config MTD_NAND_MXC + tristate "MXC NAND support" + depends on MTD_NAND && ARCH_MXC_HAS_NFC_V1 + help + This enables the driver for the NAND flash controller on the + MXC processors. + +config MTD_NAND_MXC_V2 + tristate "MXC NAND Version 2 support" + depends on MTD_NAND && ARCH_MXC_HAS_NFC_V2 + help + This enables the driver for the version 2 of NAND flash controller + on the MXC processors. + +config MTD_NAND_MXC_V3 + tristate "MXC NAND Version 3 support" + depends on MTD_NAND && ARCH_MXC_HAS_NFC_V3 + help + This enables the driver for the version 3 of NAND flash controller + on the MXC processors. + +config MTD_NAND_MXC_SWECC + bool "Software ECC support " + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3 + help + This enables the support for Software ECC handling. By + default MXC NAND controller Hardware ECC is supported. + + +config MTD_NAND_MXC_FORCE_CE + bool "NAND chip select operation support" + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2|| MTD_NAND_MXC_V3 + help + This enables the NAND chip select by using CE control line. By + default CE operation is disabled. + +config MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + bool "ECC correction in S/W" + depends on MTD_NAND_MXC + help + This enables the Option2 NFC ECC correction in software. By + default Option 1 is selected. Enable if you need option2 ECC correction. + +config MXC_NAND_LOW_LEVEL_ERASE + bool "Low level NAND erase" + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3 + help + This enables the erase of whole NAND flash. By + default low level erase operation is disabled. + +config MTD_NAND_GPMI_NFC + tristate "GPMI NAND Flash Controller driver" + depends on MTD_NAND && (ARCH_MX23 || ARCH_MX28) + help + Enables NAND Flash support. + config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" depends on MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e8ab884ba47b..87ab9180433c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -38,7 +38,11 @@ obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o +obj-$(CONFIG_MTD_NAND_IMX_NFC) += imx_nfc.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o +obj-$(CONFIG_MTD_NAND_MXC_V2) += mxc_nd2.o nand_device_info.o +obj-$(CONFIG_MTD_NAND_MXC_V3) += mxc_nd2.o nand_device_info.o +obj-$(CONFIG_MTD_NAND_GPMI_NFC) += gpmi-nfc/ nand_device_info.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o diff --git a/drivers/mtd/nand/gpmi-nfc/Makefile b/drivers/mtd/nand/gpmi-nfc/Makefile new file mode 100644 index 000000000000..e3d5660735b6 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_MTD_NAND_GPMI_NFC) += gpmi-nfc.o +gpmi-nfc-objs += gpmi-nfc-main.o +gpmi-nfc-objs += gpmi-nfc-event-reporting.o +gpmi-nfc-objs += gpmi-nfc-hal-common.o +gpmi-nfc-objs += gpmi-nfc-hal-v0.o +gpmi-nfc-objs += gpmi-nfc-hal-v1.o +gpmi-nfc-objs += gpmi-nfc-rom-common.o +gpmi-nfc-objs += gpmi-nfc-rom-v0.o +gpmi-nfc-objs += gpmi-nfc-rom-v1.o +gpmi-nfc-objs += gpmi-nfc-mil.o diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h new file mode 100644 index 000000000000..9af4feb29021 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h @@ -0,0 +1,550 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __GPMI_NFC_BCH_REGS_H +#define __GPMI_NFC_BCH_REGS_H + +/*============================================================================*/ + +#define HW_BCH_CTRL (0x00000000) +#define HW_BCH_CTRL_SET (0x00000004) +#define HW_BCH_CTRL_CLR (0x00000008) +#define HW_BCH_CTRL_TOG (0x0000000c) + +#define BM_BCH_CTRL_SFTRST 0x80000000 +#define BV_BCH_CTRL_SFTRST__RUN 0x0 +#define BV_BCH_CTRL_SFTRST__RESET 0x1 +#define BM_BCH_CTRL_CLKGATE 0x40000000 +#define BV_BCH_CTRL_CLKGATE__RUN 0x0 +#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1 +#define BP_BCH_CTRL_RSVD5 23 +#define BM_BCH_CTRL_RSVD5 0x3F800000 +#define BF_BCH_CTRL_RSVD5(v) (((v) << 23) & BM_BCH_CTRL_RSVD5) +#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000 +#define BP_BCH_CTRL_RSVD4 20 +#define BM_BCH_CTRL_RSVD4 0x00300000 +#define BF_BCH_CTRL_RSVD4(v) (((v) << 20) & BM_BCH_CTRL_RSVD4) +#define BP_BCH_CTRL_M2M_LAYOUT 18 +#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000 +#define BF_BCH_CTRL_M2M_LAYOUT(v) (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT) +#define BM_BCH_CTRL_M2M_ENCODE 0x00020000 +#define BM_BCH_CTRL_M2M_ENABLE 0x00010000 +#define BP_BCH_CTRL_RSVD3 11 +#define BM_BCH_CTRL_RSVD3 0x0000F800 +#define BF_BCH_CTRL_RSVD3(v) (((v) << 11) & BM_BCH_CTRL_RSVD3) +#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400 +#define BM_BCH_CTRL_RSVD2 0x00000200 +#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100 +#define BP_BCH_CTRL_RSVD1 4 +#define BM_BCH_CTRL_RSVD1 0x000000F0 +#define BF_BCH_CTRL_RSVD1(v) (((v) << 4) & BM_BCH_CTRL_RSVD1) +#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008 +#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004 +#define BM_BCH_CTRL_RSVD0 0x00000002 +#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001 + +/*============================================================================*/ + +#define HW_BCH_STATUS0 (0x00000010) + +#define BP_BCH_STATUS0_HANDLE 20 +#define BM_BCH_STATUS0_HANDLE 0xFFF00000 +#define BF_BCH_STATUS0_HANDLE(v) \ + (((v) << 20) & BM_BCH_STATUS0_HANDLE) +#define BP_BCH_STATUS0_COMPLETED_CE 16 +#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000 +#define BF_BCH_STATUS0_COMPLETED_CE(v) \ + (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE) +#define BP_BCH_STATUS0_STATUS_BLK0 8 +#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00 +#define BF_BCH_STATUS0_STATUS_BLK0(v) \ + (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0) +#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04 +#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE +#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF +#define BP_BCH_STATUS0_RSVD1 5 +#define BM_BCH_STATUS0_RSVD1 0x000000E0 +#define BF_BCH_STATUS0_RSVD1(v) \ + (((v) << 5) & BM_BCH_STATUS0_RSVD1) +#define BM_BCH_STATUS0_ALLONES 0x00000010 +#define BM_BCH_STATUS0_CORRECTED 0x00000008 +#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004 +#define BP_BCH_STATUS0_RSVD0 0 +#define BM_BCH_STATUS0_RSVD0 0x00000003 +#define BF_BCH_STATUS0_RSVD0(v) \ + (((v) << 0) & BM_BCH_STATUS0_RSVD0) + +/*============================================================================*/ + +#define HW_BCH_MODE (0x00000020) + +#define BP_BCH_MODE_RSVD 8 +#define BM_BCH_MODE_RSVD 0xFFFFFF00 +#define BF_BCH_MODE_RSVD(v) \ + (((v) << 8) & BM_BCH_MODE_RSVD) +#define BP_BCH_MODE_ERASE_THRESHOLD 0 +#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF +#define BF_BCH_MODE_ERASE_THRESHOLD(v) \ + (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD) + +/*============================================================================*/ + +#define HW_BCH_ENCODEPTR (0x00000030) + +#define BP_BCH_ENCODEPTR_ADDR 0 +#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF +#define BF_BCH_ENCODEPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DATAPTR (0x00000040) + +#define BP_BCH_DATAPTR_ADDR 0 +#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF +#define BF_BCH_DATAPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_METAPTR (0x00000050) + +#define BP_BCH_METAPTR_ADDR 0 +#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF +#define BF_BCH_METAPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_LAYOUTSELECT (0x00000070) + +#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30 +#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000 +#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \ + (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT) +#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28 +#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000 +#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \ + (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT) +#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26 +#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000 +#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \ + (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT) +#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24 +#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000 +#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \ + (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT) +#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22 +#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000 +#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \ + (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT) +#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20 +#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000 +#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \ + (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT) +#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18 +#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000 +#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \ + (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT) +#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16 +#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000 +#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \ + (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT) +#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14 +#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000 +#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \ + (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT) +#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12 +#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000 +#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \ + (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT) +#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10 +#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00 +#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \ + (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT) +#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8 +#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300 +#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \ + (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT) +#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6 +#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0 +#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \ + (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT) +#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4 +#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030 +#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \ + (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT) +#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2 +#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C +#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \ + (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT) +#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0 +#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003 +#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \ + (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT) + +/*============================================================================*/ + +#define HW_BCH_FLASH0LAYOUT0 (0x00000080) + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE) +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH0LAYOUT0_ECC0) +#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH0LAYOUT1 (0x00000090) + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH0LAYOUT1_ECCN) +#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH1LAYOUT0 (0x000000a0) + +#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE) +#define BP_BCH_FLASH1LAYOUT0_ECC0 12 +#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH1LAYOUT0_ECC0) +#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH1LAYOUT1 (0x000000b0) + +#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH1LAYOUT1_ECCN 12 +#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH1LAYOUT1_ECCN) +#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH2LAYOUT0 (0x000000c0) + +#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE) +#define BP_BCH_FLASH2LAYOUT0_ECC0 12 +#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH2LAYOUT0_ECC0) +#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH2LAYOUT1 (0x000000d0) + +#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH2LAYOUT1_ECCN 12 +#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH2LAYOUT1_ECCN) +#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH3LAYOUT0 (0x000000e0) + +#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE) +#define BP_BCH_FLASH3LAYOUT0_ECC0 12 +#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH3LAYOUT0_ECC0) +#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH3LAYOUT1 (0x000000f0) + +#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH3LAYOUT1_ECCN 12 +#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH3LAYOUT1_ECCN) +#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_DEBUG0 (0x00000100) +#define HW_BCH_DEBUG0_SET (0x00000104) +#define HW_BCH_DEBUG0_CLR (0x00000108) +#define HW_BCH_DEBUG0_TOG (0x0000010c) + +#define BP_BCH_DEBUG0_RSVD1 27 +#define BM_BCH_DEBUG0_RSVD1 0xF8000000 +#define BF_BCH_DEBUG0_RSVD1(v) \ + (((v) << 27) & BM_BCH_DEBUG0_RSVD1) +#define BM_BCH_DEBUG0_ROM_BIST_ENABLE 0x04000000 +#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE 0x02000000 +#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16 +#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000 +#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \ + (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL) +#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000 +#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000 +#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1 +#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000 +#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1 +#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000 +#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800 +#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400 +#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200 +#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1 +#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100 +#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0 +#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1 +#define BP_BCH_DEBUG0_RSVD0 6 +#define BM_BCH_DEBUG0_RSVD0 0x000000C0 +#define BF_BCH_DEBUG0_RSVD0(v) \ + (((v) << 6) & BM_BCH_DEBUG0_RSVD0) +#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0 +#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F +#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \ + (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT) + +/*============================================================================*/ + +#define HW_BCH_DBGKESREAD (0x00000110) + +#define BP_BCH_DBGKESREAD_VALUES 0 +#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGKESREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGCSFEREAD (0x00000120) + +#define BP_BCH_DBGCSFEREAD_VALUES 0 +#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGCSFEREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGSYNDGENREAD (0x00000130) + +#define BP_BCH_DBGSYNDGENREAD_VALUES 0 +#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGAHBMREAD (0x00000140) + +#define BP_BCH_DBGAHBMREAD_VALUES 0 +#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGAHBMREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_BLOCKNAME (0x00000150) + +#define BP_BCH_BLOCKNAME_NAME 0 +#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF +#define BF_BCH_BLOCKNAME_NAME(v) (v) + +/*============================================================================*/ + +#define HW_BCH_VERSION (0x00000160) + +#define BP_BCH_VERSION_MAJOR 24 +#define BM_BCH_VERSION_MAJOR 0xFF000000 +#define BF_BCH_VERSION_MAJOR(v) \ + (((v) << 24) & BM_BCH_VERSION_MAJOR) +#define BP_BCH_VERSION_MINOR 16 +#define BM_BCH_VERSION_MINOR 0x00FF0000 +#define BF_BCH_VERSION_MINOR(v) \ + (((v) << 16) & BM_BCH_VERSION_MINOR) +#define BP_BCH_VERSION_STEP 0 +#define BM_BCH_VERSION_STEP 0x0000FFFF +#define BF_BCH_VERSION_STEP(v) \ + (((v) << 0) & BM_BCH_VERSION_STEP) + +/*============================================================================*/ + +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h new file mode 100644 index 000000000000..692db086de4d --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h @@ -0,0 +1,557 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Xml Revision: 2.5 + * Template revision: 26195 + */ + +#ifndef __GPMI_NFC_BCH_REGS_H +#define __GPMI_NFC_BCH_REGS_H + +/*============================================================================*/ + +#define HW_BCH_CTRL (0x00000000) +#define HW_BCH_CTRL_SET (0x00000004) +#define HW_BCH_CTRL_CLR (0x00000008) +#define HW_BCH_CTRL_TOG (0x0000000c) + +#define BM_BCH_CTRL_SFTRST 0x80000000 +#define BV_BCH_CTRL_SFTRST__RUN 0x0 +#define BV_BCH_CTRL_SFTRST__RESET 0x1 +#define BM_BCH_CTRL_CLKGATE 0x40000000 +#define BV_BCH_CTRL_CLKGATE__RUN 0x0 +#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1 +#define BP_BCH_CTRL_RSVD5 23 +#define BM_BCH_CTRL_RSVD5 0x3F800000 +#define BF_BCH_CTRL_RSVD5(v) \ + (((v) << 23) & BM_BCH_CTRL_RSVD5) +#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000 +#define BP_BCH_CTRL_RSVD4 20 +#define BM_BCH_CTRL_RSVD4 0x00300000 +#define BF_BCH_CTRL_RSVD4(v) \ + (((v) << 20) & BM_BCH_CTRL_RSVD4) +#define BP_BCH_CTRL_M2M_LAYOUT 18 +#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000 +#define BF_BCH_CTRL_M2M_LAYOUT(v) \ + (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT) +#define BM_BCH_CTRL_M2M_ENCODE 0x00020000 +#define BM_BCH_CTRL_M2M_ENABLE 0x00010000 +#define BP_BCH_CTRL_RSVD3 11 +#define BM_BCH_CTRL_RSVD3 0x0000F800 +#define BF_BCH_CTRL_RSVD3(v) \ + (((v) << 11) & BM_BCH_CTRL_RSVD3) +#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400 +#define BM_BCH_CTRL_RSVD2 0x00000200 +#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100 +#define BP_BCH_CTRL_RSVD1 4 +#define BM_BCH_CTRL_RSVD1 0x000000F0 +#define BF_BCH_CTRL_RSVD1(v) \ + (((v) << 4) & BM_BCH_CTRL_RSVD1) +#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008 +#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004 +#define BM_BCH_CTRL_RSVD0 0x00000002 +#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001 + +/*============================================================================*/ + +#define HW_BCH_STATUS0 (0x00000010) + +#define BP_BCH_STATUS0_HANDLE 20 +#define BM_BCH_STATUS0_HANDLE 0xFFF00000 +#define BF_BCH_STATUS0_HANDLE(v) \ + (((v) << 20) & BM_BCH_STATUS0_HANDLE) +#define BP_BCH_STATUS0_COMPLETED_CE 16 +#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000 +#define BF_BCH_STATUS0_COMPLETED_CE(v) \ + (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE) +#define BP_BCH_STATUS0_STATUS_BLK0 8 +#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00 +#define BF_BCH_STATUS0_STATUS_BLK0(v) \ + (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0) +#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03 +#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04 +#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE +#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF +#define BP_BCH_STATUS0_RSVD1 5 +#define BM_BCH_STATUS0_RSVD1 0x000000E0 +#define BF_BCH_STATUS0_RSVD1(v) \ + (((v) << 5) & BM_BCH_STATUS0_RSVD1) +#define BM_BCH_STATUS0_ALLONES 0x00000010 +#define BM_BCH_STATUS0_CORRECTED 0x00000008 +#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004 +#define BP_BCH_STATUS0_RSVD0 0 +#define BM_BCH_STATUS0_RSVD0 0x00000003 +#define BF_BCH_STATUS0_RSVD0(v) \ + (((v) << 0) & BM_BCH_STATUS0_RSVD0) + +/*============================================================================*/ + +#define HW_BCH_MODE (0x00000020) + +#define BP_BCH_MODE_RSVD 8 +#define BM_BCH_MODE_RSVD 0xFFFFFF00 +#define BF_BCH_MODE_RSVD(v) \ + (((v) << 8) & BM_BCH_MODE_RSVD) +#define BP_BCH_MODE_ERASE_THRESHOLD 0 +#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF +#define BF_BCH_MODE_ERASE_THRESHOLD(v) \ + (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD) + +/*============================================================================*/ + +#define HW_BCH_ENCODEPTR (0x00000030) + +#define BP_BCH_ENCODEPTR_ADDR 0 +#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF +#define BF_BCH_ENCODEPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DATAPTR (0x00000040) + +#define BP_BCH_DATAPTR_ADDR 0 +#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF +#define BF_BCH_DATAPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_METAPTR (0x00000050) + +#define BP_BCH_METAPTR_ADDR 0 +#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF +#define BF_BCH_METAPTR_ADDR(v) (v) + +/*============================================================================*/ + +#define HW_BCH_LAYOUTSELECT (0x00000070) + +#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30 +#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000 +#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \ + (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT) +#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28 +#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000 +#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \ + (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT) +#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26 +#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000 +#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \ + (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT) +#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24 +#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000 +#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \ + (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT) +#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22 +#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000 +#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \ + (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT) +#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20 +#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000 +#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \ + (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT) +#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18 +#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000 +#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \ + (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT) +#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16 +#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000 +#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \ + (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT) +#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14 +#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000 +#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \ + (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT) +#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12 +#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000 +#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \ + (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT) +#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10 +#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00 +#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \ + (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT) +#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8 +#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300 +#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \ + (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT) +#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6 +#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0 +#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \ + (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT) +#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4 +#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030 +#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \ + (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT) +#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2 +#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C +#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \ + (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT) +#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0 +#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003 +#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \ + (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT) + +/*============================================================================*/ + +#define HW_BCH_FLASH0LAYOUT0 (0x00000080) + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE) +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH0LAYOUT0_ECC0) +#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH0LAYOUT1 (0x00000090) + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH0LAYOUT1_ECCN) +#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH1LAYOUT0 (0x000000a0) + +#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE) +#define BP_BCH_FLASH1LAYOUT0_ECC0 12 +#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH1LAYOUT0_ECC0) +#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH1LAYOUT1 (0x000000b0) + +#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH1LAYOUT1_ECCN 12 +#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH1LAYOUT1_ECCN) +#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH2LAYOUT0 (0x000000c0) + +#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE) +#define BP_BCH_FLASH2LAYOUT0_ECC0 12 +#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH2LAYOUT0_ECC0) +#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH2LAYOUT1 (0x000000d0) + +#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH2LAYOUT1_ECCN 12 +#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH2LAYOUT1_ECCN) +#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH3LAYOUT0 (0x000000e0) + +#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000 +#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \ + (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS) +#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000 +#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE) +#define BP_BCH_FLASH3LAYOUT0_ECC0 12 +#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F000 +#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \ + (((v) << 12) & BM_BCH_FLASH3LAYOUT0_ECC0) +#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9 +#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA +#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x00000FFF +#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ + +#define HW_BCH_FLASH3LAYOUT1 (0x000000f0) + +#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000 +#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \ + (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE) +#define BP_BCH_FLASH3LAYOUT1_ECCN 12 +#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F000 +#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \ + (((v) << 12) & BM_BCH_FLASH3LAYOUT1_ECCN) +#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9 +#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA +#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x00000FFF +#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \ + (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE) + +/*============================================================================*/ + +#define HW_BCH_DEBUG0 (0x00000100) +#define HW_BCH_DEBUG0_SET (0x00000104) +#define HW_BCH_DEBUG0_CLR (0x00000108) +#define HW_BCH_DEBUG0_TOG (0x0000010c) + +#define BP_BCH_DEBUG0_RSVD1 27 +#define BM_BCH_DEBUG0_RSVD1 0xF8000000 +#define BF_BCH_DEBUG0_RSVD1(v) \ + (((v) << 27) & BM_BCH_DEBUG0_RSVD1) +#define BM_BCH_DEBUG0_ROM_BIST_ENABLE 0x04000000 +#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE 0x02000000 +#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16 +#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000 +#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \ + (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL) +#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000 +#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000 +#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1 +#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000 +#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1 +#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000 +#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800 +#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1 +#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400 +#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200 +#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0 +#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1 +#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100 +#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0 +#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1 +#define BP_BCH_DEBUG0_RSVD0 6 +#define BM_BCH_DEBUG0_RSVD0 0x000000C0 +#define BF_BCH_DEBUG0_RSVD0(v) \ + (((v) << 6) & BM_BCH_DEBUG0_RSVD0) +#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0 +#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F +#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \ + (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT) + +/*============================================================================*/ + +#define HW_BCH_DBGKESREAD (0x00000110) + +#define BP_BCH_DBGKESREAD_VALUES 0 +#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGKESREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGCSFEREAD (0x00000120) + +#define BP_BCH_DBGCSFEREAD_VALUES 0 +#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGCSFEREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGSYNDGENREAD (0x00000130) + +#define BP_BCH_DBGSYNDGENREAD_VALUES 0 +#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_DBGAHBMREAD (0x00000140) + +#define BP_BCH_DBGAHBMREAD_VALUES 0 +#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF +#define BF_BCH_DBGAHBMREAD_VALUES(v) (v) + +/*============================================================================*/ + +#define HW_BCH_BLOCKNAME (0x00000150) + +#define BP_BCH_BLOCKNAME_NAME 0 +#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF +#define BF_BCH_BLOCKNAME_NAME(v) (v) + +/*============================================================================*/ + +#define HW_BCH_VERSION (0x00000160) + +#define BP_BCH_VERSION_MAJOR 24 +#define BM_BCH_VERSION_MAJOR 0xFF000000 +#define BF_BCH_VERSION_MAJOR(v) \ + (((v) << 24) & BM_BCH_VERSION_MAJOR) +#define BP_BCH_VERSION_MINOR 16 +#define BM_BCH_VERSION_MINOR 0x00FF0000 +#define BF_BCH_VERSION_MINOR(v) \ + (((v) << 16) & BM_BCH_VERSION_MINOR) +#define BP_BCH_VERSION_STEP 0 +#define BM_BCH_VERSION_STEP 0x0000FFFF +#define BF_BCH_VERSION_STEP(v) \ + (((v) << 0) & BM_BCH_VERSION_STEP) + +/*============================================================================*/ + +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c new file mode 100644 index 000000000000..45574391b0f0 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c @@ -0,0 +1,307 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +#if defined(EVENT_REPORTING) + +/* + * This variable and module parameter controls whether the driver reports event + * information by printing to the console. + */ + +static int report_events; +module_param(report_events, int, 0600); + +/** + * 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. + */ + +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; + +/** + * gpmi_nfc_reset_event_trace() - Resets the event trace. + */ +void gpmi_nfc_reset_event_trace(void) +{ + event_trace.nesting = 0; + event_trace.overflow = false; + event_trace.next = 0; +} + +/** + * gpmi_nfc_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]. + */ +void gpmi_nfc_add_event(char *description, int delta) +{ + struct event *event; + + if (!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++; + +} + +/** + * gpmi_nfc_start_event_trace() - Starts an event trace. + * + * @description: A description of the first event. + */ +void gpmi_nfc_start_event_trace(char *description) +{ + + ktime_t t0; + ktime_t t1; + + if (!report_events) + return; + + gpmi_nfc_reset_event_trace(); + + t0 = ktime_get(); + t1 = ktime_get(); + + event_trace.overhead = ktime_sub(t1, t0); + + gpmi_nfc_add_event(description, 1); + +} + +/** + * gpmi_nfc_dump_event_trace() - Dumps the event trace. + */ +void gpmi_nfc_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 (!report_events) + return; + + /* Print important facts about this event trace. */ + + pr_info("\n+----------------\n"); + + pr_info("| Overhead : [%d:%d]\n", event_trace.overhead.tv.sec, + event_trace.overhead.tv.nsec); + + if (!event_trace.next) { + pr_info("| 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); + pr_info("| Elapsed Time: [%d:%d]\n", delta.tv.sec, delta.tv.nsec); + + if (event_trace.overflow) + pr_info("| 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); + } + + } + + pr_info("%s\n", line); + + } + + pr_info("+----------------\n"); + +} + +/** + * gpmi_nfc_stop_event_trace() - Stops an event trace. + * + * @description: A description of the last event. + */ +void gpmi_nfc_stop_event_trace(char *description) +{ + struct event *event; + + if (!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 { + gpmi_nfc_add_event(description, -1); + } + + gpmi_nfc_dump_event_trace(); + gpmi_nfc_reset_event_trace(); + +} + +#endif /* EVENT_REPORTING */ diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h new file mode 100644 index 000000000000..2f9fce609a34 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h @@ -0,0 +1,416 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __GPMI_NFC_GPMI_REGS_H +#define __GPMI_NFC_GPMI_REGS_H + +/*============================================================================*/ + +#define HW_GPMI_CTRL0 (0x00000000) +#define HW_GPMI_CTRL0_SET (0x00000004) +#define HW_GPMI_CTRL0_CLR (0x00000008) +#define HW_GPMI_CTRL0_TOG (0x0000000c) + +#define BM_GPMI_CTRL0_SFTRST 0x80000000 +#define BV_GPMI_CTRL0_SFTRST__RUN 0x0 +#define BV_GPMI_CTRL0_SFTRST__RESET 0x1 +#define BM_GPMI_CTRL0_CLKGATE 0x40000000 +#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0 +#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1 +#define BM_GPMI_CTRL0_RUN 0x20000000 +#define BV_GPMI_CTRL0_RUN__IDLE 0x0 +#define BV_GPMI_CTRL0_RUN__BUSY 0x1 +#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000 +#define BM_GPMI_CTRL0_TIMEOUT_IRQ_EN 0x08000000 +#define BM_GPMI_CTRL0_UDMA 0x04000000 +#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0 +#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1 +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000 +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 +#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000 +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 +#define BM_GPMI_CTRL0_LOCK_CS 0x00400000 +#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0 +#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1 +#define BP_GPMI_CTRL0_CS 20 +#define BM_GPMI_CTRL0_CS 0x00300000 +#define BF_GPMI_CTRL0_CS(v) (((v) << 20) & BM_GPMI_CTRL0_CS) +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS 0x000E0000 +#define BF_GPMI_CTRL0_ADDRESS(v) (((v) << 17) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT) + +/*============================================================================*/ + +#define HW_GPMI_COMPARE (0x00000010) + +#define BP_GPMI_COMPARE_MASK 16 +#define BM_GPMI_COMPARE_MASK 0xFFFF0000 +#define BF_GPMI_COMPARE_MASK(v) (((v) << 16) & BM_GPMI_COMPARE_MASK) +#define BP_GPMI_COMPARE_REFERENCE 0 +#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF +#define BF_GPMI_COMPARE_REFERENCE(v) \ + (((v) << 0) & BM_GPMI_COMPARE_REFERENCE) + +/*============================================================================*/ + +#define HW_GPMI_ECCCTRL (0x00000020) +#define HW_GPMI_ECCCTRL_SET (0x00000024) +#define HW_GPMI_ECCCTRL_CLR (0x00000028) +#define HW_GPMI_ECCCTRL_TOG (0x0000002c) + +#define BP_GPMI_ECCCTRL_HANDLE 16 +#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000 +#define BF_GPMI_ECCCTRL_HANDLE(v) (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE) +#define BM_GPMI_ECCCTRL_RSVD2 0x00008000 +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000 +#define BF_GPMI_ECCCTRL_ECC_CMD(v) (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT 0x1 +#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT 0x2 +#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT 0x3 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 +#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 +#define BP_GPMI_ECCCTRL_RSVD1 9 +#define BM_GPMI_ECCCTRL_RSVD1 0x00000E00 +#define BF_GPMI_ECCCTRL_RSVD1(v) (((v) << 9) & BM_GPMI_ECCCTRL_RSVD1) +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF +#define BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER7 0x080 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER6 0x040 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER5 0x020 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER4 0x010 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER3 0x008 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER2 0x004 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER1 0x002 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER0 0x001 + +/*============================================================================*/ + +#define HW_GPMI_ECCCOUNT (0x00000030) + +#define BP_GPMI_ECCCOUNT_RSVD2 16 +#define BM_GPMI_ECCCOUNT_RSVD2 0xFFFF0000 +#define BF_GPMI_ECCCOUNT_RSVD2(v) (((v) << 16) & BM_GPMI_ECCCOUNT_RSVD2) +#define BP_GPMI_ECCCOUNT_COUNT 0 +#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF +#define BF_GPMI_ECCCOUNT_COUNT(v) (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT) + +/*============================================================================*/ + +#define HW_GPMI_PAYLOAD (0x00000040) + +#define BP_GPMI_PAYLOAD_ADDRESS 2 +#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC +#define BF_GPMI_PAYLOAD_ADDRESS(v) (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS) +#define BP_GPMI_PAYLOAD_RSVD0 0 +#define BM_GPMI_PAYLOAD_RSVD0 0x00000003 +#define BF_GPMI_PAYLOAD_RSVD0(v) (((v) << 0) & BM_GPMI_PAYLOAD_RSVD0) + +/*============================================================================*/ + +#define HW_GPMI_AUXILIARY (0x00000050) + +#define BP_GPMI_AUXILIARY_ADDRESS 2 +#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC +#define BF_GPMI_AUXILIARY_ADDRESS(v) \ + (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS) +#define BP_GPMI_AUXILIARY_RSVD0 0 +#define BM_GPMI_AUXILIARY_RSVD0 0x00000003 +#define BF_GPMI_AUXILIARY_RSVD0(v) (((v) << 0) & BM_GPMI_AUXILIARY_RSVD0) + +/*============================================================================*/ + +#define HW_GPMI_CTRL1 (0x00000060) +#define HW_GPMI_CTRL1_SET (0x00000064) +#define HW_GPMI_CTRL1_CLR (0x00000068) +#define HW_GPMI_CTRL1_TOG (0x0000006c) + +#define BP_GPMI_CTRL1_RSVD2 24 +#define BM_GPMI_CTRL1_RSVD2 0xFF000000 +#define BF_GPMI_CTRL1_RSVD2(v) \ + (((v) << 24) & BM_GPMI_CTRL1_RSVD2) +#define BM_GPMI_CTRL1_CE3_SEL 0x00800000 +#define BM_GPMI_CTRL1_CE2_SEL 0x00400000 +#define BM_GPMI_CTRL1_CE1_SEL 0x00200000 +#define BM_GPMI_CTRL1_CE0_SEL 0x00100000 +#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000 +#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001 +#define BP_GPMI_CTRL1_GPMI_MODE 0 +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004 +#define BM_GPMI_CTRL1_DEV_RESET 0x00000008 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200 +#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400 +#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000 +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_BCH_MODE 0x00040000 +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000 +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000 +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000 +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY) +#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800 +#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200 +#define BM_GPMI_CTRL1_BURST_EN 0x00000100 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY3 0x00000080 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY2 0x00000040 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY1 0x00000020 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY0 0x00000010 +#define BM_GPMI_CTRL1_DEV_RESET 0x00000008 +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 +#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002 +#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001 +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +/*============================================================================*/ + +#define HW_GPMI_TIMING0 (0x00000070) + +#define BP_GPMI_TIMING0_RSVD1 24 +#define BM_GPMI_TIMING0_RSVD1 0xFF000000 +#define BF_GPMI_TIMING0_RSVD1(v) \ + (((v) << 24) & BM_GPMI_TIMING0_RSVD1) +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000 +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP) +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00 +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD) +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP) + +/*============================================================================*/ + +#define HW_GPMI_TIMING1 (0x00000080) + +#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16 +#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000 +#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \ + (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT) +#define BP_GPMI_TIMING1_RSVD1 0 +#define BM_GPMI_TIMING1_RSVD1 0x0000FFFF +#define BF_GPMI_TIMING1_RSVD1(v) \ + (((v) << 0) & BM_GPMI_TIMING1_RSVD1) + +/*============================================================================*/ + +#define HW_GPMI_TIMING2 (0x00000090) + +#define BP_GPMI_TIMING2_UDMA_TRP 24 +#define BM_GPMI_TIMING2_UDMA_TRP 0xFF000000 +#define BF_GPMI_TIMING2_UDMA_TRP(v) \ + (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP) +#define BP_GPMI_TIMING2_UDMA_ENV 16 +#define BM_GPMI_TIMING2_UDMA_ENV 0x00FF0000 +#define BF_GPMI_TIMING2_UDMA_ENV(v) \ + (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV) +#define BP_GPMI_TIMING2_UDMA_HOLD 8 +#define BM_GPMI_TIMING2_UDMA_HOLD 0x0000FF00 +#define BF_GPMI_TIMING2_UDMA_HOLD(v) \ + (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD) +#define BP_GPMI_TIMING2_UDMA_SETUP 0 +#define BM_GPMI_TIMING2_UDMA_SETUP 0x000000FF +#define BF_GPMI_TIMING2_UDMA_SETUP(v) \ + (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP) + +/*============================================================================*/ + +#define HW_GPMI_DATA (0x000000a0) + +#define BP_GPMI_DATA_DATA 0 +#define BM_GPMI_DATA_DATA 0xFFFFFFFF +#define BF_GPMI_DATA_DATA(v) (v) + +/*============================================================================*/ + +#define HW_GPMI_STAT (0x000000b0) + +#define BM_GPMI_STAT_PRESENT 0x80000000 +#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0 +#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1 +#define BP_GPMI_STAT_RSVD1 12 +#define BM_GPMI_STAT_RSVD1 0x7FFFF000 +#define BF_GPMI_STAT_RSVD1(v) \ + (((v) << 12) & BM_GPMI_STAT_RSVD1) +#define BP_GPMI_STAT_RDY_TIMEOUT 8 +#define BM_GPMI_STAT_RDY_TIMEOUT 0x00000F00 +#define BF_GPMI_STAT_RDY_TIMEOUT(v) \ + (((v) << 8) & BM_GPMI_STAT_RDY_TIMEOUT) +#define BM_GPMI_STAT_ATA_IRQ 0x00000080 +#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000040 +#define BM_GPMI_STAT_FIFO_EMPTY 0x00000020 +#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0 +#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1 +#define BM_GPMI_STAT_FIFO_FULL 0x00000010 +#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0 +#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1 +#define BM_GPMI_STAT_DEV3_ERROR 0x00000008 +#define BM_GPMI_STAT_DEV2_ERROR 0x00000004 +#define BM_GPMI_STAT_DEV1_ERROR 0x00000002 +#define BM_GPMI_STAT_DEERROR 0x00000001 + +/*============================================================================*/ + +#define HW_GPMI_DEBUG (0x000000c0) + +#define BM_GPMI_DEBUG_READY3 0x80000000 +#define BM_GPMI_DEBUG_READY2 0x40000000 +#define BM_GPMI_DEBUG_READY1 0x20000000 +#define BM_GPMI_DEBUG_READY0 0x10000000 +#define BM_GPMI_DEBUG_WAIT_FOR_READY_END3 0x08000000 +#define BM_GPMI_DEBUG_WAIT_FOR_READY_END2 0x04000000 +#define BM_GPMI_DEBUG_WAIT_FOR_READY_END1 0x02000000 +#define BM_GPMI_DEBUG_WAIT_FOR_READY_END0 0x01000000 +#define BM_GPMI_DEBUG_SENSE3 0x00800000 +#define BM_GPMI_DEBUG_SENSE2 0x00400000 +#define BM_GPMI_DEBUG_SENSE1 0x00200000 +#define BM_GPMI_DEBUG_SENSE0 0x00100000 +#define BM_GPMI_DEBUG_DMAREQ3 0x00080000 +#define BM_GPMI_DEBUG_DMAREQ2 0x00040000 +#define BM_GPMI_DEBUG_DMAREQ1 0x00020000 +#define BM_GPMI_DEBUG_DMAREQ0 0x00010000 +#define BP_GPMI_DEBUG_CMD_END 12 +#define BM_GPMI_DEBUG_CMD_END 0x0000F000 +#define BF_GPMI_DEBUG_CMD_END(v) \ + (((v) << 12) & BM_GPMI_DEBUG_CMD_END) +#define BP_GPMI_DEBUG_UDMA_STATE 8 +#define BM_GPMI_DEBUG_UDMA_STATE 0x00000F00 +#define BF_GPMI_DEBUG_UDMA_STATE(v) \ + (((v) << 8) & BM_GPMI_DEBUG_UDMA_STATE) +#define BM_GPMI_DEBUG_BUSY 0x00000080 +#define BV_GPMI_DEBUG_BUSY__DISABLED 0x0 +#define BV_GPMI_DEBUG_BUSY__ENABLED 0x1 +#define BP_GPMI_DEBUG_PIN_STATE 4 +#define BM_GPMI_DEBUG_PIN_STATE 0x00000070 +#define BF_GPMI_DEBUG_PIN_STATE(v) \ + (((v) << 4) & BM_GPMI_DEBUG_PIN_STATE) +#define BV_GPMI_DEBUG_PIN_STATE__PSM_IDLE 0x0 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_BYTCNT 0x1 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_ADDR 0x2 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_STALL 0x3 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_STROBE 0x4 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_ATARDY 0x5 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_DHOLD 0x6 +#define BV_GPMI_DEBUG_PIN_STATE__PSM_DONE 0x7 +#define BP_GPMI_DEBUG_MAIN_STATE 0 +#define BM_GPMI_DEBUG_MAIN_STATE 0x0000000F +#define BF_GPMI_DEBUG_MAIN_STATE(v) \ + (((v) << 0) & BM_GPMI_DEBUG_MAIN_STATE) +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_IDLE 0x0 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_BYTCNT 0x1 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFE 0x2 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFR 0x3 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAREQ 0x4 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAACK 0x5 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFF 0x6 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDFIFO 0x7 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDDMAR 0x8 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_RDCMP 0x9 +#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DONE 0xA + +/*============================================================================*/ + +#define HW_GPMI_VERSION (0x000000d0) + +#define BP_GPMI_VERSION_MAJOR 24 +#define BM_GPMI_VERSION_MAJOR 0xFF000000 +#define BF_GPMI_VERSION_MAJOR(v) (((v) << 24) & BM_GPMI_VERSION_MAJOR) +#define BP_GPMI_VERSION_MINOR 16 +#define BM_GPMI_VERSION_MINOR 0x00FF0000 +#define BF_GPMI_VERSION_MINOR(v) (((v) << 16) & BM_GPMI_VERSION_MINOR) +#define BP_GPMI_VERSION_STEP 0 +#define BM_GPMI_VERSION_STEP 0x0000FFFF +#define BF_GPMI_VERSION_STEP(v) (((v) << 0) & BM_GPMI_VERSION_STEP) + +/*============================================================================*/ + +#define HW_GPMI_DEBUG2 (0x000000e0) + +#define BP_GPMI_DEBUG2_RSVD1 16 +#define BM_GPMI_DEBUG2_RSVD1 0xFFFF0000 +#define BF_GPMI_DEBUG2_RSVD1(v) (((v) << 16) & BM_GPMI_DEBUG2_RSVD1) +#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12 +#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000 +#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \ + (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE) +#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800 +#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400 +#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200 +#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100 +#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080 +#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040 +#define BP_GPMI_DEBUG2_RDN_TAP 0 +#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F +#define BF_GPMI_DEBUG2_RDN_TAP(v) (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP) + +/*============================================================================*/ + +#define HW_GPMI_DEBUG3 (0x000000f0) + +#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16 +#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000 +#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \ + (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR) +#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0 +#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF +#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \ + (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR) + +/*============================================================================*/ +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h new file mode 100644 index 000000000000..dcb3b7d3fc88 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h @@ -0,0 +1,421 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Xml Revision: 2.2 + * Template revision: 26195 + */ + +#ifndef __GPMI_NFC_GPMI_REGS_H +#define __GPMI_NFC_GPMI_REGS_H + +/*============================================================================*/ + +#define HW_GPMI_CTRL0 (0x00000000) +#define HW_GPMI_CTRL0_SET (0x00000004) +#define HW_GPMI_CTRL0_CLR (0x00000008) +#define HW_GPMI_CTRL0_TOG (0x0000000c) + +#define BM_GPMI_CTRL0_SFTRST 0x80000000 +#define BV_GPMI_CTRL0_SFTRST__RUN 0x0 +#define BV_GPMI_CTRL0_SFTRST__RESET 0x1 +#define BM_GPMI_CTRL0_CLKGATE 0x40000000 +#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0 +#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1 +#define BM_GPMI_CTRL0_RUN 0x20000000 +#define BV_GPMI_CTRL0_RUN__IDLE 0x0 +#define BV_GPMI_CTRL0_RUN__BUSY 0x1 +#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000 +#define BM_GPMI_CTRL0_LOCK_CS 0x08000000 +#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0 +#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1 +#define BM_GPMI_CTRL0_UDMA 0x04000000 +#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0 +#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1 +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000 +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 +#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000 +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 +#define BP_GPMI_CTRL0_CS 20 +#define BM_GPMI_CTRL0_CS 0x00700000 +#define BF_GPMI_CTRL0_CS(v) \ + (((v) << 20) & BM_GPMI_CTRL0_CS) +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS 0x000E0000 +#define BF_GPMI_CTRL0_ADDRESS(v) \ + (((v) << 17) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT) + +/*============================================================================*/ + +#define HW_GPMI_COMPARE (0x00000010) + +#define BP_GPMI_COMPARE_MASK 16 +#define BM_GPMI_COMPARE_MASK 0xFFFF0000 +#define BF_GPMI_COMPARE_MASK(v) \ + (((v) << 16) & BM_GPMI_COMPARE_MASK) +#define BP_GPMI_COMPARE_REFERENCE 0 +#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF +#define BF_GPMI_COMPARE_REFERENCE(v) \ + (((v) << 0) & BM_GPMI_COMPARE_REFERENCE) + +/*============================================================================*/ + +#define HW_GPMI_ECCCTRL (0x00000020) +#define HW_GPMI_ECCCTRL_SET (0x00000024) +#define HW_GPMI_ECCCTRL_CLR (0x00000028) +#define HW_GPMI_ECCCTRL_TOG (0x0000002c) + +#define BP_GPMI_ECCCTRL_HANDLE 16 +#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000 +#define BF_GPMI_ECCCTRL_HANDLE(v) \ + (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE) +#define BM_GPMI_ECCCTRL_RSVD2 0x00008000 +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000 +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ + (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE 0x1 +#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE2 0x2 +#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE3 0x3 +#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 +#define BP_GPMI_ECCCTRL_RSVD1 9 +#define BM_GPMI_ECCCTRL_RSVD1 0x00000E00 +#define BF_GPMI_ECCCTRL_RSVD1(v) \ + (((v) << 9) & BM_GPMI_ECCCTRL_RSVD1) +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF + +/*============================================================================*/ + +#define HW_GPMI_ECCCOUNT (0x00000030) + +#define BP_GPMI_ECCCOUNT_RSVD2 16 +#define BM_GPMI_ECCCOUNT_RSVD2 0xFFFF0000 +#define BF_GPMI_ECCCOUNT_RSVD2(v) \ + (((v) << 16) & BM_GPMI_ECCCOUNT_RSVD2) +#define BP_GPMI_ECCCOUNT_COUNT 0 +#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF +#define BF_GPMI_ECCCOUNT_COUNT(v) \ + (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT) + +/*============================================================================*/ + +#define HW_GPMI_PAYLOAD (0x00000040) + +#define BP_GPMI_PAYLOAD_ADDRESS 2 +#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC +#define BF_GPMI_PAYLOAD_ADDRESS(v) \ + (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS) +#define BP_GPMI_PAYLOAD_RSVD0 0 +#define BM_GPMI_PAYLOAD_RSVD0 0x00000003 +#define BF_GPMI_PAYLOAD_RSVD0(v) \ + (((v) << 0) & BM_GPMI_PAYLOAD_RSVD0) + +/*============================================================================*/ + +#define HW_GPMI_AUXILIARY (0x00000050) + +#define BP_GPMI_AUXILIARY_ADDRESS 2 +#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC +#define BF_GPMI_AUXILIARY_ADDRESS(v) \ + (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS) +#define BP_GPMI_AUXILIARY_RSVD0 0 +#define BM_GPMI_AUXILIARY_RSVD0 0x00000003 +#define BF_GPMI_AUXILIARY_RSVD0(v) \ + (((v) << 0) & BM_GPMI_AUXILIARY_RSVD0) + +/*============================================================================*/ + +#define HW_GPMI_CTRL1 (0x00000060) +#define HW_GPMI_CTRL1_SET (0x00000064) +#define HW_GPMI_CTRL1_CLR (0x00000068) +#define HW_GPMI_CTRL1_TOG (0x0000006c) + +#define BP_GPMI_CTRL1_RSVD2 25 +#define BM_GPMI_CTRL1_RSVD2 0xFE000000 +#define BF_GPMI_CTRL1_RSVD2(v) \ + (((v) << 25) & BM_GPMI_CTRL1_RSVD2) +#define BM_GPMI_CTRL1_DECOUPLE_CS 0x01000000 +#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 +#define BM_GPMI_CTRL1_WRN_DLY_SEL 0x00C00000 +#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ + (((v) << 22) & BM_GPMI_CTRL1_WRN_DLY_SEL) +#define BM_GPMI_CTRL1_RSVD1 0x00200000 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN 0x00100000 +#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000 +#define BM_GPMI_CTRL1_BCH_MODE 0x00040000 +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000 +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000 +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000 +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY) +#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800 +#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200 +#define BM_GPMI_CTRL1_BURST_EN 0x00000100 +#define BM_GPMI_CTRL1_ABORT_WAIT_REQUEST 0x00000080 +#define BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 4 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 0x00000070 +#define BF_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL(v) \ + (((v) << 4) & BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL) +#define BM_GPMI_CTRL1_DEV_RESET 0x00000008 +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 +#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002 +#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001 +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +/*============================================================================*/ + +#define HW_GPMI_TIMING0 (0x00000070) + +#define BP_GPMI_TIMING0_RSVD1 24 +#define BM_GPMI_TIMING0_RSVD1 0xFF000000 +#define BF_GPMI_TIMING0_RSVD1(v) \ + (((v) << 24) & BM_GPMI_TIMING0_RSVD1) +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000 +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP) +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00 +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD) +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP) + +/*============================================================================*/ + +#define HW_GPMI_TIMING1 (0x00000080) + +#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16 +#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000 +#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \ + (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT) +#define BP_GPMI_TIMING1_RSVD1 0 +#define BM_GPMI_TIMING1_RSVD1 0x0000FFFF +#define BF_GPMI_TIMING1_RSVD1(v) \ + (((v) << 0) & BM_GPMI_TIMING1_RSVD1) + +/*============================================================================*/ + +#define HW_GPMI_TIMING2 (0x00000090) + +#define BP_GPMI_TIMING2_UDMA_TRP 24 +#define BM_GPMI_TIMING2_UDMA_TRP 0xFF000000 +#define BF_GPMI_TIMING2_UDMA_TRP(v) \ + (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP) +#define BP_GPMI_TIMING2_UDMA_ENV 16 +#define BM_GPMI_TIMING2_UDMA_ENV 0x00FF0000 +#define BF_GPMI_TIMING2_UDMA_ENV(v) \ + (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV) +#define BP_GPMI_TIMING2_UDMA_HOLD 8 +#define BM_GPMI_TIMING2_UDMA_HOLD 0x0000FF00 +#define BF_GPMI_TIMING2_UDMA_HOLD(v) \ + (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD) +#define BP_GPMI_TIMING2_UDMA_SETUP 0 +#define BM_GPMI_TIMING2_UDMA_SETUP 0x000000FF +#define BF_GPMI_TIMING2_UDMA_SETUP(v) \ + (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP) + +/*============================================================================*/ + +#define HW_GPMI_DATA (0x000000a0) + +#define BP_GPMI_DATA_DATA 0 +#define BM_GPMI_DATA_DATA 0xFFFFFFFF +#define BF_GPMI_DATA_DATA(v) (v) + +#define HW_GPMI_STAT (0x000000b0) + +#define BP_GPMI_STAT_READY_BUSY 24 +#define BM_GPMI_STAT_READY_BUSY 0xFF000000 +#define BF_GPMI_STAT_READY_BUSY(v) \ + (((v) << 24) & BM_GPMI_STAT_READY_BUSY) +#define BP_GPMI_STAT_RDY_TIMEOUT 16 +#define BM_GPMI_STAT_RDY_TIMEOUT 0x00FF0000 +#define BF_GPMI_STAT_RDY_TIMEOUT(v) \ + (((v) << 16) & BM_GPMI_STAT_RDY_TIMEOUT) +#define BM_GPMI_STAT_DEV7_ERROR 0x00008000 +#define BM_GPMI_STAT_DEV6_ERROR 0x00004000 +#define BM_GPMI_STAT_DEV5_ERROR 0x00002000 +#define BM_GPMI_STAT_DEV4_ERROR 0x00001000 +#define BM_GPMI_STAT_DEV3_ERROR 0x00000800 +#define BM_GPMI_STAT_DEV2_ERROR 0x00000400 +#define BM_GPMI_STAT_DEERROR 0x00000200 +#define BM_GPMI_STAT_DEV0_ERROR 0x00000100 +#define BP_GPMI_STAT_RSVD1 5 +#define BM_GPMI_STAT_RSVD1 0x000000E0 +#define BF_GPMI_STAT_RSVD1(v) \ + (((v) << 5) & BM_GPMI_STAT_RSVD1) +#define BM_GPMI_STAT_ATA_IRQ 0x00000010 +#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000008 +#define BM_GPMI_STAT_FIFO_EMPTY 0x00000004 +#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0 +#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1 +#define BM_GPMI_STAT_FIFO_FULL 0x00000002 +#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0 +#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1 +#define BM_GPMI_STAT_PRESENT 0x00000001 +#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0 +#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1 + +/*============================================================================*/ + +#define HW_GPMI_DEBUG (0x000000c0) + +#define BP_GPMI_DEBUG_WAIT_FOR_READY_END 24 +#define BM_GPMI_DEBUG_WAIT_FOR_READY_END 0xFF000000 +#define BF_GPMI_DEBUG_WAIT_FOR_READY_END(v) \ + (((v) << 24) & BM_GPMI_DEBUG_WAIT_FOR_READY_END) +#define BP_GPMI_DEBUG_DMA_SENSE 16 +#define BM_GPMI_DEBUG_DMA_SENSE 0x00FF0000 +#define BF_GPMI_DEBUG_DMA_SENSE(v) \ + (((v) << 16) & BM_GPMI_DEBUG_DMA_SENSE) +#define BP_GPMI_DEBUG_DMAREQ 8 +#define BM_GPMI_DEBUG_DMAREQ 0x0000FF00 +#define BF_GPMI_DEBUG_DMAREQ(v) \ + (((v) << 8) & BM_GPMI_DEBUG_DMAREQ) +#define BP_GPMI_DEBUG_CMD_END 0 +#define BM_GPMI_DEBUG_CMD_END 0x000000FF +#define BF_GPMI_DEBUG_CMD_END(v) \ + (((v) << 0) & BM_GPMI_DEBUG_CMD_END) + +/*============================================================================*/ + +#define HW_GPMI_VERSION (0x000000d0) + +#define BP_GPMI_VERSION_MAJOR 24 +#define BM_GPMI_VERSION_MAJOR 0xFF000000 +#define BF_GPMI_VERSION_MAJOR(v) \ + (((v) << 24) & BM_GPMI_VERSION_MAJOR) +#define BP_GPMI_VERSION_MINOR 16 +#define BM_GPMI_VERSION_MINOR 0x00FF0000 +#define BF_GPMI_VERSION_MINOR(v) \ + (((v) << 16) & BM_GPMI_VERSION_MINOR) +#define BP_GPMI_VERSION_STEP 0 +#define BM_GPMI_VERSION_STEP 0x0000FFFF +#define BF_GPMI_VERSION_STEP(v) \ + (((v) << 0) & BM_GPMI_VERSION_STEP) + +/*============================================================================*/ + +#define HW_GPMI_DEBUG2 (0x000000e0) + +#define BP_GPMI_DEBUG2_RSVD1 28 +#define BM_GPMI_DEBUG2_RSVD1 0xF0000000 +#define BF_GPMI_DEBUG2_RSVD1(v) \ + (((v) << 28) & BM_GPMI_DEBUG2_RSVD1) +#define BP_GPMI_DEBUG2_UDMA_STATE 24 +#define BM_GPMI_DEBUG2_UDMA_STATE 0x0F000000 +#define BF_GPMI_DEBUG2_UDMA_STATE(v) \ + (((v) << 24) & BM_GPMI_DEBUG2_UDMA_STATE) +#define BM_GPMI_DEBUG2_BUSY 0x00800000 +#define BV_GPMI_DEBUG2_BUSY__DISABLED 0x0 +#define BV_GPMI_DEBUG2_BUSY__ENABLED 0x1 +#define BP_GPMI_DEBUG2_PIN_STATE 20 +#define BM_GPMI_DEBUG2_PIN_STATE 0x00700000 +#define BF_GPMI_DEBUG2_PIN_STATE(v) \ + (((v) << 20) & BM_GPMI_DEBUG2_PIN_STATE) +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_IDLE 0x0 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_BYTCNT 0x1 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ADDR 0x2 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STALL 0x3 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STROBE 0x4 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ATARDY 0x5 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DHOLD 0x6 +#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DONE 0x7 +#define BP_GPMI_DEBUG2_MAIN_STATE 16 +#define BM_GPMI_DEBUG2_MAIN_STATE 0x000F0000 +#define BF_GPMI_DEBUG2_MAIN_STATE(v) \ + (((v) << 16) & BM_GPMI_DEBUG2_MAIN_STATE) +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_IDLE 0x0 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_BYTCNT 0x1 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFE 0x2 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFR 0x3 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAREQ 0x4 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAACK 0x5 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFF 0x6 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDFIFO 0x7 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDDMAR 0x8 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_RDCMP 0x9 +#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DONE 0xA +#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12 +#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000 +#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \ + (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE) +#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800 +#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400 +#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200 +#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100 +#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080 +#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040 +#define BP_GPMI_DEBUG2_RDN_TAP 0 +#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F +#define BF_GPMI_DEBUG2_RDN_TAP(v) \ + (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP) + +/*============================================================================*/ + +#define HW_GPMI_DEBUG3 (0x000000f0) + +#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16 +#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000 +#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \ + (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR) +#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0 +#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF +#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \ + (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR) + +/*============================================================================*/ + +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c new file mode 100644 index 000000000000..b38d653a21fd --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c @@ -0,0 +1,1037 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +/** + * gpmi_nfc_bch_isr - BCH interrupt service routine. + * + * @interrupt_number: The interrupt number. + * @cookie: A cookie that contains a pointer to the owning device + * data structure. + */ +irqreturn_t gpmi_nfc_bch_isr(int irq, void *cookie) +{ + struct gpmi_nfc_data *this = cookie; + struct nfc_hal *nfc = this->nfc; + + gpmi_nfc_add_event("> gpmi_nfc_bch_isr", 1); + + /* Clear the interrupt. */ + + nfc->clear_bch(this); + + /* Release the base level. */ + + complete(&(nfc->bch_done)); + + /* Return success. */ + + gpmi_nfc_add_event("< gpmi_nfc_bch_isr", -1); + + return IRQ_HANDLED; + +} + +/** + * gpmi_nfc_dma_isr - DMA interrupt service routine. + * + * @interrupt_number: The interrupt number. + * @cookie: A cookie that contains a pointer to the owning device + * data structure. + */ +irqreturn_t gpmi_nfc_dma_isr(int irq, void *cookie) +{ + struct gpmi_nfc_data *this = cookie; + struct nfc_hal *nfc = this->nfc; + + gpmi_nfc_add_event("> gpmi_nfc_dma_isr", 1); + + /* Acknowledge the DMA channel's interrupt. */ + + mxs_dma_ack_irq(nfc->isr_dma_channel); + + /* Release the base level. */ + + complete(&(nfc->dma_done)); + + /* Return success. */ + + gpmi_nfc_add_event("< gpmi_nfc_dma_isr", -1); + + return IRQ_HANDLED; + +} + +/** + * gpmi_nfc_dma_init() - Initializes DMA. + * + * @this: Per-device data. + */ +int gpmi_nfc_dma_init(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct nfc_hal *nfc = this->nfc; + int i; + int error; + + /* Allocate the DMA descriptors. */ + + for (i = 0; i < NFC_DMA_DESCRIPTOR_COUNT; i++) { + nfc->dma_descriptors[i] = mxs_dma_alloc_desc(); + if (!nfc->dma_descriptors[i]) { + dev_err(dev, "Cannot allocate all DMA descriptors.\n"); + error = -ENOMEM; + goto exit_descriptor_allocation; + } + } + + /* If control arrives here, all is well. */ + + return 0; + + /* Control arrives here when something has gone wrong. */ + +exit_descriptor_allocation: + while (--i >= 0) + mxs_dma_free_desc(this->nfc->dma_descriptors[i]); + + return error; + +} + +/** + * gpmi_nfc_dma_exit() - Shuts down DMA. + * + * @this: Per-device data. + */ +void gpmi_nfc_dma_exit(struct gpmi_nfc_data *this) +{ + struct nfc_hal *nfc = this->nfc; + int i; + + /* Free the DMA descriptors. */ + + for (i = 0; i < NFC_DMA_DESCRIPTOR_COUNT; i++) + mxs_dma_free_desc(nfc->dma_descriptors[i]); + +} + +/** + * gpmi_nfc_set_geometry() - Shared NFC geometry configuration. + * + * In principle, computing the NFC geometry is version-specific. However, at + * this writing all, versions share the same page model, so this code can also + * be shared. + * + * @this: Per-device data. + */ +int gpmi_nfc_set_geometry(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct physical_geometry *physical = &this->physical_geometry; + struct nfc_geometry *geometry = &this->nfc_geometry; + struct boot_rom_helper *rom = this->rom; + unsigned int metadata_size; + unsigned int status_size; + unsigned int chunk_data_size_in_bits; + unsigned int chunk_ecc_size_in_bits; + unsigned int chunk_total_size_in_bits; + unsigned int block_mark_chunk_number; + unsigned int block_mark_chunk_bit_offset; + unsigned int block_mark_bit_offset; + + /* At this writing, we support only BCH. */ + + geometry->ecc_algorithm = "BCH"; + + /* + * We always choose a metadata size of 10. Don't try to make sense of + * it -- this is really only for historical compatibility. + */ + + geometry->metadata_size_in_bytes = 10; + + /* + * At this writing, we always use 512-byte ECC chunks. Later hardware + * will be able to support larger chunks, which will cause this + * decision to move into version-specific code. + */ + + geometry->ecc_chunk_size_in_bytes = 512; + + /* Compute the page size based on the physical geometry. */ + + geometry->page_size_in_bytes = + physical->page_data_size_in_bytes + + physical->page_oob_size_in_bytes ; + + /* + * Compute the total number of ECC chunks in a page. This includes the + * slightly larger chunk at the beginning of the page, which contains + * both data and metadata. + */ + + geometry->ecc_chunk_count = + physical->page_data_size_in_bytes / + /*---------------------------------*/ + geometry->ecc_chunk_size_in_bytes; + + /* + * We use the same ECC strength for all chunks, including the first one. + * At this writing, we base our ECC strength choice entirely on the + * the physical page geometry. In the future, this should be changed to + * pay attention to the detailed device information we gathered earlier. + */ + + geometry->ecc_strength = 0; + + switch (physical->page_data_size_in_bytes) { + case 2048: + geometry->ecc_strength = 8; + break; + case 4096: + switch (physical->page_oob_size_in_bytes) { + case 128: + geometry->ecc_strength = 8; + break; + case 218: + geometry->ecc_strength = 16; + break; + } + break; + } + + /* Check if we were able to figure out the ECC strength. */ + + if (!geometry->ecc_strength) { + dev_err(dev, "Unsupported page geometry: %u:%u\n", + physical->page_data_size_in_bytes, + physical->page_oob_size_in_bytes); + return !0; + } + + /* + * The payload buffer contains the data area of a page. The ECC engine + * only needs what's required to hold the data. + */ + + geometry->payload_size_in_bytes = physical->page_data_size_in_bytes; + + /* + * In principle, computing the auxiliary buffer geometry is NFC + * version-specific. However, at this writing, all versions share the + * same model, so this code can also be shared. + * + * The auxiliary buffer contains the metadata and the ECC status. The + * metadata is padded to the nearest 32-bit boundary. The ECC status + * contains one byte for every ECC chunk, and is also padded to the + * nearest 32-bit boundary. + */ + + metadata_size = (geometry->metadata_size_in_bytes + 0x3) & ~0x3; + status_size = (geometry->ecc_chunk_count + 0x3) & ~0x3; + + geometry->auxiliary_size_in_bytes = metadata_size + status_size; + geometry->auxiliary_status_offset = metadata_size; + + /* Check if we're going to do block mark swapping. */ + + if (!rom->swap_block_mark) + return 0; + + /* + * If control arrives here, we're doing block mark swapping, so we need + * to compute the byte and bit offsets of the physical block mark within + * the ECC-based view of the page data. In principle, this isn't a + * difficult computation -- but it's very important and it's easy to get + * it wrong, so we do it carefully. + * + * Note that this calculation is simpler because we use the same ECC + * strength for all chunks, including the zero'th one, which contains + * the metadata. The calculation would be slightly more complicated + * otherwise. + * + * We start by computing the physical bit offset of the block mark. We + * then subtract the number of metadata and ECC bits appearing before + * the mark to arrive at its bit offset within the data alone. + */ + + /* Compute some important facts about chunk geometry. */ + + chunk_data_size_in_bits = geometry->ecc_chunk_size_in_bytes * 8; + chunk_ecc_size_in_bits = geometry->ecc_strength * 13; + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + + block_mark_bit_offset = physical->page_data_size_in_bytes * 8; + + /* Subtract the metadata bits. */ + + block_mark_bit_offset -= geometry->metadata_size_in_bytes * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + + block_mark_chunk_bit_offset = + block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) { + + /* + * If control arrives here, the block mark actually appears in + * the ECC bits of this chunk. This wont' work. + */ + + dev_err(dev, "Unsupported page geometry " + "(block mark in ECC): %u:%u\n", + physical->page_data_size_in_bytes, + physical->page_oob_size_in_bytes); + return !0; + + } + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + /* + * We now know the absolute bit offset of the block mark within the + * ECC-based data. We can now compute the byte offset and the bit + * offset within the byte. + */ + + geometry->block_mark_byte_offset = block_mark_bit_offset / 8; + geometry->block_mark_bit_offset = block_mark_bit_offset % 8; + + /* Return success. */ + + return 0; + +} + +/* + * This code is useful for debugging. + */ + +/*#define DUMP_DMA_CONTEXT*/ + +#if (defined DUMP_DMA_CONTEXT) + +int dump_dma_context_flag; + +void dump_dma_context(struct gpmi_nfc_data *this, char *title) +{ + + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + void *q; + uint32_t *p; + unsigned int i; + unsigned int j; + + if (!dump_dma_context_flag) + return; + + pr_info("%s\n", title); + pr_info("======\n"); + pr_info("\n"); + + /*--------------------------------------------------------------------*/ + + pr_info(" Descriptors\n"); + pr_info(" -----------\n"); + { + + for (i = 0; i < NFC_DMA_DESCRIPTOR_COUNT; i++, d++) { + pr_info(" #%u\n", i); + pr_info(" --\n"); + pr_info(" Physical Address: 0x%08x\n" , (*d)->address); + pr_info(" Next : 0x%08lx\n", (*d)->cmd.next); + pr_info(" Command : 0x%08lx\n", (*d)->cmd.cmd.data); + pr_info(" Buffer : 0x%08x\n" , (*d)->cmd.address); + for (j = 0; j < 6; j++) + pr_info(" PIO[%u] : 0x%08lx\n", + j, (*d)->cmd.pio_words[j]); + } + + } + pr_info("\n"); + + /*--------------------------------------------------------------------*/ + + pr_info(" DMA\n"); + pr_info(" ---\n"); + { + void *DMA = IO_ADDRESS(APBH_DMA_PHYS_ADDR); + + p = q = DMA + 0x200; + + for (i = 0; i < 7; i++) { + pr_info(" [0x%03x] 0x%08x\n", q - DMA, *p); + q += 0x10; + p = q; + } + + } + pr_info("\n"); + + /*--------------------------------------------------------------------*/ + + pr_info(" GPMI\n"); + pr_info(" ----\n"); + { + void *GPMI = resources->gpmi_regs; + + p = q = GPMI; + + for (i = 0; i < 33; i++) { + pr_info(" [0x%03x] 0x%08x\n", q - GPMI, *p); + q += 0x10; + p = q; + } + + } + pr_info("\n"); + + /*--------------------------------------------------------------------*/ + + pr_info(" BCH\n"); + pr_info(" ---\n"); + { + void *BCH = resources->bch_regs; + + p = q = BCH; + + for (i = 0; i < 22; i++) { + pr_info(" [0x%03x] 0x%08x\n", q - BCH, *p); + q += 0x10; + p = q; + } + + } + pr_info("\n"); + +} + +#endif + +/** + * gpmi_nfc_dma_go - Run a DMA channel. + * + * @this: Per-device data structure. + * @dma_channel: The DMA channel we're going to use. + */ +int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + unsigned long timeout; + int error; + LIST_HEAD(tmp_desc_list); + + gpmi_nfc_add_event("> gpmi_nfc_dma_go", 1); + + /* Get ready... */ + + nfc->isr_dma_channel = dma_channel; + init_completion(&nfc->dma_done); + mxs_dma_enable_irq(dma_channel, 1); + + /* Go! */ + + #if defined(DUMP_DMA_CONTEXT) + dump_dma_context(this, "BEFORE"); + #endif + + mxs_dma_enable(dma_channel); + + /* Wait for it to finish. */ + + timeout = wait_for_completion_timeout(&nfc->dma_done, + msecs_to_jiffies(1000)); + + #if defined(DUMP_DMA_CONTEXT) + dump_dma_context(this, "AFTER"); + #endif + + error = (!timeout) ? -ETIMEDOUT : 0; + + if (error) { + dev_err(dev, "[%s] Chip: %u, DMA Channel: %d, Error %d\n", + __func__, dma_channel - resources->dma_low_channel, + dma_channel, error); + gpmi_nfc_add_event("...DMA timed out", 0); + } else + gpmi_nfc_add_event("...Finished DMA successfully", 0); + + /* Clear out the descriptors we just ran. */ + + mxs_dma_cooked(dma_channel, &tmp_desc_list); + + /* Shut the DMA channel down. */ + + mxs_dma_reset(dma_channel); + mxs_dma_enable_irq(dma_channel, 0); + mxs_dma_disable(dma_channel); + + /* Return. */ + + gpmi_nfc_add_event("< gpmi_nfc_dma_go", -1); + + return error; + +} + +/** + * ns_to_cycles - Converts time in nanoseconds to cycles. + * + * @ntime: The time, in nanoseconds. + * @period: The cycle period, in nanoseconds. + * @min: The minimum allowable number of cycles. + */ +static unsigned int ns_to_cycles(unsigned int time, + unsigned int period, unsigned int min) +{ + unsigned int k; + + /* + * Compute the minimum number of cycles that entirely contain the + * given time. + */ + + k = (time + period - 1) / period; + + return max(k, min); + +} + +/** + * gpmi_compute_hardware_timing - Apply timing to current hardware conditions. + * + * @this: Per-device data. + * @hardware_timing: A pointer to a hardware timing structure that will receive + * the results of our calculations. + */ +int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this, + struct gpmi_nfc_hardware_timing *hw) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct physical_geometry *physical = &this->physical_geometry; + struct nfc_hal *nfc = this->nfc; + struct gpmi_nfc_timing target = nfc->timing; + bool improved_timing_is_available; + unsigned long clock_frequency_in_hz; + unsigned int clock_period_in_ns; + bool dll_use_half_periods; + unsigned int dll_delay_shift; + unsigned int max_sample_delay_in_ns; + unsigned int address_setup_in_cycles; + unsigned int data_setup_in_ns; + unsigned int data_setup_in_cycles; + unsigned int data_hold_in_cycles; + int ideal_sample_delay_in_ns; + unsigned int sample_delay_factor; + int tEYE; + unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; + unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; + + /* + * If there are multiple chips, we need to relax the timings to allow + * for signal distortion due to higher capacitance. + */ + + if (physical->chip_count > 2) { + target.data_setup_in_ns += 10; + target.data_hold_in_ns += 10; + target.address_setup_in_ns += 10; + } else if (physical->chip_count > 1) { + target.data_setup_in_ns += 5; + target.data_hold_in_ns += 5; + target.address_setup_in_ns += 5; + } + + /* Check if improved timing information is available. */ + + improved_timing_is_available = + (target.tREA_in_ns >= 0) && + (target.tRLOH_in_ns >= 0) && + (target.tRHOH_in_ns >= 0) ; + + /* Inspect the clock. */ + + clock_frequency_in_hz = nfc->clock_frequency_in_hz; + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; + + /* + * The NFC quantizes setup and hold parameters in terms of clock cycles. + * Here, we quantize the setup and hold timing parameters to the + * next-highest clock period to make sure we apply at least the + * specified times. + * + * For data setup and data hold, the hardware interprets a value of zero + * as the largest possible delay. This is not what's intended by a zero + * in the input parameter, so we impose a minimum of one cycle. + */ + + data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, + clock_period_in_ns, 1); + data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, + clock_period_in_ns, 1); + address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, + clock_period_in_ns, 0); + + /* + * The clock's period affects the sample delay in a number of ways: + * + * (1) The NFC HAL tells us the maximum clock period the sample delay + * DLL can tolerate. If the clock period is greater than half that + * maximum, we must configure the DLL to be driven by half periods. + * + * (2) We need to convert from an ideal sample delay, in ns, to a + * "sample delay factor," which the NFC uses. This factor depends on + * whether we're driving the DLL with full or half periods. + * Paraphrasing the reference manual: + * + * AD = SDF x 0.125 x RP + * + * where: + * + * AD is the applied delay, in ns. + * SDF is the sample delay factor, which is dimensionless. + * RP is the reference period, in ns, which is a full clock period + * if the DLL is being driven by full periods, or half that if + * the DLL is being driven by half periods. + * + * Let's re-arrange this in a way that's more useful to us: + * + * 8 + * SDF = AD x ---- + * RP + * + * The reference period is either the clock period or half that, so this + * is: + * + * 8 AD x DDF + * SDF = AD x ----- = -------- + * f x P P + * + * where: + * + * f is 1 or 1/2, depending on how we're driving the DLL. + * P is the clock period. + * DDF is the DLL Delay Factor, a dimensionless value that + * incorporates all the constants in the conversion. + * + * DDF will be either 8 or 16, both of which are powers of two. We can + * reduce the cost of this conversion by using bit shifts instead of + * multiplication or division. Thus: + * + * AD << DDS + * SDF = --------- + * P + * + * or + * + * AD = (SDF >> DDS) x P + * + * where: + * + * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. + */ + + if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { + dll_use_half_periods = true; + dll_delay_shift = 3 + 1; + } else { + dll_use_half_periods = false; + dll_delay_shift = 3; + } + + /* + * Compute the maximum sample delay the NFC allows, under current + * conditions. If the clock is running too slowly, no sample delay is + * possible. + */ + + if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) + max_sample_delay_in_ns = 0; + else { + + /* + * Compute the delay implied by the largest sample delay factor + * the NFC allows. + */ + + max_sample_delay_in_ns = + (nfc->max_sample_delay_factor * clock_period_in_ns) >> + dll_delay_shift; + + /* + * Check if the implied sample delay larger than the NFC + * actually allows. + */ + + if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) + max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; + + } + + /* + * Check if improved timing information is available. If not, we have to + * use a less-sophisticated algorithm. + */ + + if (!improved_timing_is_available) { + + /* + * Fold the read setup time required by the NFC into the ideal + * sample delay. + */ + + ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + + nfc->internal_data_setup_in_ns; + + /* + * The ideal sample delay may be greater than the maximum + * allowed by the NFC. If so, we can trade off sample delay time + * for more data setup time. + * + * In each iteration of the following loop, we add a cycle to + * the data setup time and subtract a corresponding amount from + * the sample delay until we've satisified the constraints or + * can't do any better. + */ + + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + data_setup_in_cycles++; + ideal_sample_delay_in_ns -= clock_period_in_ns; + + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + } + + /* + * Compute the sample delay factor that corresponds most closely + * to the ideal sample delay. If the result is too large for the + * NFC, use the maximum value. + * + * Notice that we use the ns_to_cycles function to compute the + * sample delay factor. We do this because the form of the + * computation is the same as that for calculating cycles. + */ + + sample_delay_factor = + ns_to_cycles( + ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + + /* Skip to the part where we return our results. */ + + goto return_results; + + } + + /* + * If control arrives here, we have more detailed timing information, + * so we can use a better algorithm. + */ + + /* + * Fold the read setup time required by the NFC into the maximum + * propagation delay. + */ + + max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; + + /* + * Earlier, we computed the number of clock cycles required to satisfy + * the data setup time. Now, we need to know the actual nanoseconds. + */ + + data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; + + /* + * Compute tEYE, the width of the data eye when reading from the NAND + * Flash. The eye width is fundamentally determined by the data setup + * time, perturbed by propagation delays and some characteristics of the + * NAND Flash device. + * + * start of the eye = max_prop_delay + tREA + * end of the eye = min_prop_delay + tRHOH + data_setup + */ + + tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + + (int)data_setup_in_ns; + + tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; + + /* + * The eye must be open. If it's not, we can try to open it by + * increasing its main forcer, the data setup time. + * + * In each iteration of the following loop, we increase the data setup + * time by a single clock cycle. We do this until either the eye is + * open or we run into NFC limits. + */ + + while ((tEYE <= 0) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + } + + /* + * When control arrives here, the eye is open. The ideal time to sample + * the data is in the center of the eye: + * + * end of the eye + start of the eye + * --------------------------------- - data_setup + * 2 + * + * After some algebra, this simplifies to the code immediately below. + */ + + ideal_sample_delay_in_ns = + ((int)max_prop_delay_in_ns + + (int)target.tREA_in_ns + + (int)min_prop_delay_in_ns + + (int)target.tRHOH_in_ns - + (int)data_setup_in_ns) >> 1; + + /* + * The following figure illustrates some aspects of a NAND Flash read: + * + * + * __ _____________________________________ + * RDN \_________________/ + * + * <---- tEYE -----> + * /-----------------\ + * Read Data ----------------------------< >--------- + * \-----------------/ + * ^ ^ ^ ^ + * | | | | + * |<--Data Setup -->|<--Delay Time -->| | + * | | | | + * | | | + * | |<-- Quantized Delay Time -->| + * | | | + * + * + * We have some issues we must now address: + * + * (1) The *ideal* sample delay time must not be negative. If it is, we + * jam it to zero. + * + * (2) The *ideal* sample delay time must not be greater than that + * allowed by the NFC. If it is, we can increase the data setup + * time, which will reduce the delay between the end of the data + * setup and the center of the eye. It will also make the eye + * larger, which might help with the next issue... + * + * (3) The *quantized* sample delay time must not fall either before the + * eye opens or after it closes (the latter is the problem + * illustrated in the above figure). + */ + + /* Jam a negative ideal sample delay to zero. */ + + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * Extend the data setup as needed to reduce the ideal sample delay + * below the maximum permitted by the NFC. + */ + + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + } + + /* + * Compute the sample delay factor that corresponds to the ideal sample + * delay. If the result is too large, then use the maximum allowed + * value. + * + * Notice that we use the ns_to_cycles function to compute the sample + * delay factor. We do this because the form of the computation is the + * same as that for calculating cycles. + */ + + sample_delay_factor = + ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + + /* + * These macros conveniently encapsulate a computation we'll use to + * continuously evaluate whether or not the data sample delay is inside + * the eye. + */ + + #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) + + #define QUANTIZED_DELAY \ + ((int) ((sample_delay_factor * clock_period_in_ns) >> \ + dll_delay_shift)) + + #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) + + #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) + + /* + * While the quantized sample time falls outside the eye, reduce the + * sample delay or extend the data setup to move the sampling point back + * toward the eye. Do not allow the number of data setup cycles to + * exceed the maximum allowed by the NFC. + */ + + while (SAMPLE_IS_NOT_WITHIN_THE_EYE && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + /* + * If control arrives here, the quantized sample delay falls + * outside the eye. Check if it's before the eye opens, or after + * the eye closes. + */ + + if (QUANTIZED_DELAY > IDEAL_DELAY) { + + /* + * If control arrives here, the quantized sample delay + * falls after the eye closes. Decrease the quantized + * delay time and then go back to re-evaluate. + */ + + if (sample_delay_factor != 0) + sample_delay_factor--; + + continue; + + } + + /* + * If control arrives here, the quantized sample delay falls + * before the eye opens. Shift the sample point by increasing + * data setup time. This will also make the eye larger. + */ + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* ...and one less period for the delay time. */ + ideal_sample_delay_in_ns -= clock_period_in_ns; + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * We have a new ideal sample delay, so re-compute the quantized + * delay. + */ + + sample_delay_factor = + ns_to_cycles( + ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + + } + + /* Control arrives here when we're ready to return our results. */ + +return_results: + + hw->data_setup_in_cycles = data_setup_in_cycles; + hw->data_hold_in_cycles = data_hold_in_cycles; + hw->address_setup_in_cycles = address_setup_in_cycles; + hw->use_half_periods = dll_use_half_periods; + hw->sample_delay_factor = sample_delay_factor; + + /* Return success. */ + + return 0; + +} diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c new file mode 100644 index 000000000000..294bb9409581 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c @@ -0,0 +1,924 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +#include "gpmi-nfc-gpmi-regs-v0.h" +#include "gpmi-nfc-bch-regs-v0.h" + +/** + * init() - Initializes the NFC hardware. + * + * @this: Per-device data. + */ +static int init(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + int error; + + /* Initialize DMA. */ + + error = gpmi_nfc_dma_init(this); + + if (error) + return error; + + /* Enable the clock. It will stay on until the end of set_geometry(). */ + + clk_enable(resources->clock); + + /* Reset the GPMI block. */ + + mxs_reset_block(resources->gpmi_regs + HW_GPMI_CTRL0, true); + + /* Choose NAND mode. */ + __raw_writel(BM_GPMI_CTRL1_GPMI_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable write protection. */ + __raw_writel(BM_GPMI_CTRL1_DEV_RESET, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + __raw_writel(BM_GPMI_CTRL1_BCH_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable the clock. */ + + clk_disable(resources->clock); + + /* If control arrives here, all is well. */ + + return 0; + +} + +/** + * set_geometry() - Configures the NFC geometry. + * + * @this: Per-device data. + */ +static int set_geometry(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_geometry *nfc = &this->nfc_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + + /* We make the abstract choices in a common function. */ + + if (gpmi_nfc_set_geometry(this)) + return !0; + + /* Translate the abstract choices into register fields. */ + + block_count = nfc->ecc_chunk_count - 1; + block_size = nfc->ecc_chunk_size_in_bytes; + metadata_size = nfc->metadata_size_in_bytes; + ecc_strength = nfc->ecc_strength >> 1; + page_size = nfc->page_size_in_bytes; + + /* Enable the clock. */ + + clk_enable(resources->clock); + + /* + * Reset the BCH block. Notice that we pass in true for the just_enable + * flag. This is because the soft reset for the version 0 BCH block + * doesn't work. If you try to soft reset the BCH block, it becomes + * unusable until the next hard reset. + */ + + mxs_reset_block(resources->bch_regs, true); + + /* Configure layout 0. */ + + __raw_writel( + BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | + BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) | + BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) | + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size) , + resources->bch_regs + HW_BCH_FLASH0LAYOUT0); + + __raw_writel( + BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | + BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) | + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size) , + resources->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + + __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + resources->bch_regs + HW_BCH_CTRL_SET); + + /* Disable the clock. */ + + clk_disable(resources->clock); + + /* Return success. */ + + return 0; + +} + +/** + * set_timing() - Configures the NFC timing. + * + * @this: Per-device data. + * @timing: The timing of interest. + */ +static int set_timing(struct gpmi_nfc_data *this, + const struct gpmi_nfc_timing *timing) +{ + struct nfc_hal *nfc = this->nfc; + + /* Accept the new timing. */ + + nfc->timing = *timing; + + /* Return success. */ + + return 0; + +} + +/** + * get_timing() - Retrieves the NFC hardware timing. + * + * @this: Per-device data. + * @clock_frequency_in_hz: The clock frequency, in Hz, during the current + * I/O transaction. If no I/O transaction is in + * progress, this is the clock frequency during the + * most recent I/O transaction. + * @hardware_timing: The hardware timing configuration in effect during + * the current I/O transaction. If no I/O transaction + * is in progress, this is the hardware timing + * configuration during the most recent I/O + * transaction. + */ +static void get_timing(struct gpmi_nfc_data *this, + unsigned long *clock_frequency_in_hz, + struct gpmi_nfc_hardware_timing *hardware_timing) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + unsigned char *gpmi_regs = resources->gpmi_regs; + uint32_t register_image; + + /* Return the clock frequency. */ + + *clock_frequency_in_hz = nfc->clock_frequency_in_hz; + + /* We'll be reading the hardware, so let's enable the clock. */ + + clk_enable(resources->clock); + + /* Retrieve the hardware timing. */ + + register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0); + + hardware_timing->data_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_SETUP) >> + BP_GPMI_TIMING0_DATA_SETUP; + + hardware_timing->data_hold_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_HOLD) >> + BP_GPMI_TIMING0_DATA_HOLD; + + hardware_timing->address_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >> + BP_GPMI_TIMING0_ADDRESS_SETUP; + + register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1); + + hardware_timing->use_half_periods = + (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >> + BP_GPMI_CTRL1_HALF_PERIOD; + + hardware_timing->sample_delay_factor = + (register_image & BM_GPMI_CTRL1_RDN_DELAY) >> + BP_GPMI_CTRL1_RDN_DELAY; + + /* We're done reading the hardware, so disable the clock. */ + + clk_disable(resources->clock); + +} + +/** + * exit() - Shuts down the NFC hardware. + * + * @this: Per-device data. + */ +static void exit(struct gpmi_nfc_data *this) +{ + gpmi_nfc_dma_exit(this); +} + +/** + * begin() - Begin NFC I/O. + * + * @this: Per-device data. + */ +static void begin(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct gpmi_nfc_hardware_timing hw; + unsigned char *gpmi_regs = resources->gpmi_regs; + unsigned int clock_period_in_ns; + uint32_t register_image; + unsigned int dll_wait_time_in_us; + + /* Enable the clock. */ + + clk_enable(resources->clock); + + /* Get the timing information we need. */ + + nfc->clock_frequency_in_hz = clk_get_rate(resources->clock); + clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; + + gpmi_nfc_compute_hardware_timing(this, &hw); + + /* Set up all the simple timing parameters. */ + + register_image = + BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ; + + __raw_writel(register_image, gpmi_regs + HW_GPMI_TIMING0); + + /* + * HEY - PAY ATTENTION! + * + * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD. + */ + + __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Clear out the DLL control fields. */ + + __raw_writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR); + __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* If no sample delay is called for, return immediately. */ + + if (!hw.sample_delay_factor) + return; + + /* Configure the HALF_PERIOD flag. */ + + if (hw.use_half_periods) + __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD, + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Set the delay factor. */ + + __raw_writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor), + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Enable the DLL. */ + + __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * After we enable the GPMI DLL, we have to wait 64 clock cycles before + * we can use the GPMI. + * + * Calculate the amount of time we need to wait, in microseconds. + */ + + dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; + + if (!dll_wait_time_in_us) + dll_wait_time_in_us = 1; + + /* Wait for the DLL to settle. */ + + udelay(dll_wait_time_in_us); + +} + +/** + * end() - End NFC I/O. + * + * @this: Per-device data. + */ +static void end(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + /* Disable the clock. */ + + clk_disable(resources->clock); + +} + +/** + * clear_bch() - Clears a BCH interrupt. + * + * @this: Per-device data. + */ +static void clear_bch(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ, + resources->bch_regs + HW_BCH_CTRL_CLR); + +} + +/** + * is_ready() - Returns the ready/busy status of the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + */ +static int is_ready(struct gpmi_nfc_data *this, unsigned chip) +{ + struct resources *resources = &this->resources; + uint32_t mask; + uint32_t register_image; + + /* Extract and return the status. */ + + mask = BM_GPMI_DEBUG_READY0 << chip; + + register_image = __raw_readl(resources->gpmi_regs + HW_GPMI_DEBUG); + + return !!(register_image & mask); + +} + +/** + * send_command() - Sends a command and associated addresses. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that contains the command bytes. + * @length: The number of bytes in the buffer. + */ +static int send_command(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that sends out the command. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_READ; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 3; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BM_GPMI_CTRL0_ADDRESS_INCREMENT | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * send_data() - Sends data to the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that contains the data. + * @length: The number of bytes in the buffer. + */ +static int send_data(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that writes a buffer out. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_READ; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 4; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + (*d)->cmd.pio_words[3] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * read_data() - Receives data from the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that will receive the data. + * @length: The number of bytes to read. + */ +static int read_data(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that reads the data. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_WRITE; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 1; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* + * A DMA descriptor that waits for the command to end and the chip to + * become ready. + * + * I think we actually should *not* be waiting for the chip to become + * ready because, after all, we don't care. I think the original code + * did that and no one has re-thought it yet. + */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 4; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + (*d)->cmd.pio_words[3] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * send_page() - Sends a page, using ECC. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @payload: The physical address of the payload buffer. + * @auxiliary: The physical address of the auxiliary buffer. + */ +static int send_page(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that does an ECC page read. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 6; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + + (*d)->cmd.pio_words[1] = 0; + + (*d)->cmd.pio_words[2] = + BM_GPMI_ECCCTRL_ENABLE_ECC | + BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | + BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ; + + (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes; + (*d)->cmd.pio_words[4] = payload; + (*d)->cmd.pio_words[5] = auxiliary; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Prepare to receive an interrupt from the BCH block. */ + + init_completion(&nfc->bch_done); + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Wait for the interrupt from the BCH block. */ + + wait_for_completion(&nfc->bch_done); + + /* Return success. */ + + return error; + +} + +/** + * read_page() - Reads a page, using ECC. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @payload: The physical address of the payload buffer. + * @auxiliary: The physical address of the auxiliary buffer. + */ +static int read_page(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* Wait for the chip to report ready. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 1; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Enable the BCH block and read. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 6; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = + BM_GPMI_ECCCTRL_ENABLE_ECC | + BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | + BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ; + (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes; + (*d)->cmd.pio_words[4] = payload; + (*d)->cmd.pio_words[5] = auxiliary; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Disable the BCH block */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 3; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Deassert the NAND lock and interrupt. */ + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 0; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 0; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Prepare to receive an interrupt from the BCH block. */ + + init_completion(&nfc->bch_done); + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Wait for the interrupt from the BCH block. */ + + wait_for_completion(&nfc->bch_done); + + /* Return success. */ + + return error; + +} + +/* This structure represents the NFC HAL for this version of the hardware. */ + +struct nfc_hal gpmi_nfc_hal_v0 = { + .version = 0, + .description = "4-chip GPMI and BCH", + .max_chip_count = 4, + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> + BP_GPMI_TIMING0_DATA_SETUP), + .internal_data_setup_in_ns = 0, + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> + BP_GPMI_CTRL1_RDN_DELAY), + .max_dll_clock_period_in_ns = 32, + .max_dll_delay_in_ns = 16, + .init = init, + .set_geometry = set_geometry, + .set_timing = set_timing, + .get_timing = get_timing, + .exit = exit, + .begin = begin, + .end = end, + .clear_bch = clear_bch, + .is_ready = is_ready, + .send_command = send_command, + .send_data = send_data, + .read_data = read_data, + .send_page = send_page, + .read_page = read_page, +}; diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c new file mode 100644 index 000000000000..962efe686853 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c @@ -0,0 +1,866 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +#include "gpmi-nfc-gpmi-regs-v1.h" +#include "gpmi-nfc-bch-regs-v1.h" + +/** + * init() - Initializes the NFC hardware. + * + * @this: Per-device data. + */ +static int init(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + int error; + + /* Initialize DMA. */ + + error = gpmi_nfc_dma_init(this); + + if (error) + return error; + + /* Enable the clock. */ + + clk_enable(resources->clock); + + /* Reset the GPMI block. */ + + mxs_reset_block(resources->gpmi_regs + HW_GPMI_CTRL0, true); + + /* Choose NAND mode. */ + __raw_writel(BM_GPMI_CTRL1_GPMI_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable write protection. */ + __raw_writel(BM_GPMI_CTRL1_DEV_RESET, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + __raw_writel(BM_GPMI_CTRL1_BCH_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable the clock. */ + + clk_disable(resources->clock); + + /* If control arrives here, all is well. */ + + return 0; + +} + +/** + * set_geometry() - Configures the NFC geometry. + * + * @this: Per-device data. + */ +static int set_geometry(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_geometry *nfc = &this->nfc_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + + /* We make the abstract choices in a common function. */ + + if (gpmi_nfc_set_geometry(this)) + return !0; + + /* Translate the abstract choices into register fields. */ + + block_count = nfc->ecc_chunk_count - 1; + block_size = nfc->ecc_chunk_size_in_bytes; + metadata_size = nfc->metadata_size_in_bytes; + ecc_strength = nfc->ecc_strength >> 1; + page_size = nfc->page_size_in_bytes; + + /* Enable the clock. */ + + clk_enable(resources->clock); + + /* + * Reset the BCH block. Notice that we pass in true for the just_enable + * flag. This is because the soft reset for the version 0 BCH block + * doesn't work and the version 1 BCH block is similar enough that we + * suspect the same (though this has not been officially tested). If you + * try to soft reset a version 0 BCH block, it becomes unusable until + * the next hard reset. + */ + + mxs_reset_block(resources->bch_regs, true); + + /* Configure layout 0. */ + + __raw_writel( + BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | + BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) | + BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) | + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size) , + resources->bch_regs + HW_BCH_FLASH0LAYOUT0); + + __raw_writel( + BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | + BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) | + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size) , + resources->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + + __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + resources->bch_regs + HW_BCH_CTRL_SET); + + /* Disable the clock. */ + + clk_disable(resources->clock); + + /* Return success. */ + + return 0; + +} + +/** + * set_timing() - Configures the NFC timing. + * + * @this: Per-device data. + * @timing: The timing of interest. + */ +static int set_timing(struct gpmi_nfc_data *this, + const struct gpmi_nfc_timing *timing) +{ + struct nfc_hal *nfc = this->nfc; + + /* Accept the new timing. */ + + nfc->timing = *timing; + + /* Return success. */ + + return 0; + +} + +/** + * get_timing() - Retrieves the NFC hardware timing. + * + * @this: Per-device data. + * @clock_frequency_in_hz: The clock frequency, in Hz, during the current + * I/O transaction. If no I/O transaction is in + * progress, this is the clock frequency during the + * most recent I/O transaction. + * @hardware_timing: The hardware timing configuration in effect during + * the current I/O transaction. If no I/O transaction + * is in progress, this is the hardware timing + * configuration during the most recent I/O + * transaction. + */ +static void get_timing(struct gpmi_nfc_data *this, + unsigned long *clock_frequency_in_hz, + struct gpmi_nfc_hardware_timing *hardware_timing) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + unsigned char *gpmi_regs = resources->gpmi_regs; + uint32_t register_image; + + /* Return the clock frequency. */ + + *clock_frequency_in_hz = nfc->clock_frequency_in_hz; + + /* We'll be reading the hardware, so let's enable the clock. */ + + clk_enable(resources->clock); + + /* Retrieve the hardware timing. */ + + register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0); + + hardware_timing->data_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_SETUP) >> + BP_GPMI_TIMING0_DATA_SETUP; + + hardware_timing->data_hold_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_HOLD) >> + BP_GPMI_TIMING0_DATA_HOLD; + + hardware_timing->address_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >> + BP_GPMI_TIMING0_ADDRESS_SETUP; + + register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1); + + hardware_timing->use_half_periods = + (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >> + BP_GPMI_CTRL1_HALF_PERIOD; + + hardware_timing->sample_delay_factor = + (register_image & BM_GPMI_CTRL1_RDN_DELAY) >> + BP_GPMI_CTRL1_RDN_DELAY; + + /* We're done reading the hardware, so disable the clock. */ + + clk_disable(resources->clock); + +} + +/** + * exit() - Shuts down the NFC hardware. + * + * @this: Per-device data. + */ +static void exit(struct gpmi_nfc_data *this) +{ + gpmi_nfc_dma_exit(this); +} + +/** + * begin() - Begin NFC I/O. + * + * @this: Per-device data. + */ +static void begin(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct gpmi_nfc_hardware_timing hw; + + /* Enable the clock. */ + + clk_enable(resources->clock); + + /* Get the timing information we need. */ + + nfc->clock_frequency_in_hz = clk_get_rate(resources->clock); + gpmi_nfc_compute_hardware_timing(this, &hw); + + /* Apply the hardware timing. */ + + /* Coming soon - the clock handling code isn't ready yet. */ + +} + +/** + * end() - End NFC I/O. + * + * @this: Per-device data. + */ +static void end(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + /* Disable the clock. */ + + clk_disable(resources->clock); + +} + +/** + * clear_bch() - Clears a BCH interrupt. + * + * @this: Per-device data. + */ +static void clear_bch(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ, + resources->bch_regs + HW_BCH_CTRL_CLR); + +} + +/** + * is_ready() - Returns the ready/busy status of the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + */ +static int is_ready(struct gpmi_nfc_data *this, unsigned chip) +{ + struct resources *resources = &this->resources; + uint32_t mask; + uint32_t register_image; + + /* Extract and return the status. */ + + mask = BF_GPMI_STAT_READY_BUSY(1 << chip); + + register_image = __raw_readl(resources->gpmi_regs + HW_GPMI_STAT); + + return !!(register_image & mask); + +} + +/** + * send_command() - Sends a command and associated addresses. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that contains the command bytes. + * @length: The number of bytes in the buffer. + */ +static int send_command(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that sends out the command. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_READ; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 3; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BM_GPMI_CTRL0_ADDRESS_INCREMENT | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * send_data() - Sends data to the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that contains the data. + * @length: The number of bytes in the buffer. + */ +static int send_data(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that writes a buffer out. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_READ; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 4; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + (*d)->cmd.pio_words[3] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * read_data() - Receives data from the given chip. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @buffer: The physical address of a buffer that will receive the data. + * @length: The number of bytes to read. + */ +static int read_data(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t buffer, unsigned int length) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that reads the data. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = DMA_WRITE; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 1; + (*d)->cmd.cmd.bits.bytes = length; + + (*d)->cmd.address = buffer; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(length) ; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* + * A DMA descriptor that waits for the command to end and the chip to + * become ready. + * + * I think we actually should *not* be waiting for the chip to become + * ready because, after all, we don't care. I think the original code + * did that and no one has re-thought it yet. + */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 4; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + (*d)->cmd.pio_words[3] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Return success. */ + + return error; + +} + +/** + * send_page() - Sends a page, using ECC. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @payload: The physical address of the payload buffer. + * @auxiliary: The physical address of the auxiliary buffer. + */ +static int send_page(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* A DMA descriptor that does an ECC page read. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 6; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + + (*d)->cmd.pio_words[1] = 0; + + (*d)->cmd.pio_words[2] = + BM_GPMI_ECCCTRL_ENABLE_ECC | + BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | + BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ; + + (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes; + (*d)->cmd.pio_words[4] = payload; + (*d)->cmd.pio_words[5] = auxiliary; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Prepare to receive an interrupt from the BCH block. */ + + init_completion(&nfc->bch_done); + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Wait for the interrupt from the BCH block. */ + + wait_for_completion(&nfc->bch_done); + + /* Return success. */ + + return error; + +} + +/** + * read_page() - Reads a page, using ECC. + * + * @this: Per-device data. + * @chip: The chip of interest. + * @payload: The physical address of the payload buffer. + * @auxiliary: The physical address of the auxiliary buffer. + */ +static int read_page(struct gpmi_nfc_data *this, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct device *dev = this->dev; + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mxs_dma_desc **d = nfc->dma_descriptors; + int dma_channel; + int error = 0; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + + /* Compute the DMA channel. */ + + dma_channel = resources->dma_low_channel + chip; + + /* Wait for the chip to report ready. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 1; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(0) ; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Enable the BCH block and read. */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 6; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = + BM_GPMI_ECCCTRL_ENABLE_ECC | + BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | + BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ; + (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes; + (*d)->cmd.pio_words[4] = payload; + (*d)->cmd.pio_words[5] = auxiliary; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Disable the BCH block */ + + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 1; + (*d)->cmd.cmd.bits.irq = 0; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 1; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 1; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 3; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + (*d)->cmd.pio_words[0] = + BF_GPMI_CTRL0_COMMAND_MODE(command_mode) | + BM_GPMI_CTRL0_WORD_LENGTH | + BF_GPMI_CTRL0_CS(chip) | + BF_GPMI_CTRL0_ADDRESS(address) | + BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ; + + (*d)->cmd.pio_words[1] = 0; + (*d)->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Deassert the NAND lock and interrupt. */ + + (*d)->cmd.cmd.data = 0; + (*d)->cmd.cmd.bits.command = NO_DMA_XFER; + (*d)->cmd.cmd.bits.chain = 0; + (*d)->cmd.cmd.bits.irq = 1; + (*d)->cmd.cmd.bits.nand_lock = 0; + (*d)->cmd.cmd.bits.nand_wait_4_ready = 0; + (*d)->cmd.cmd.bits.dec_sem = 1; + (*d)->cmd.cmd.bits.wait4end = 0; + (*d)->cmd.cmd.bits.halt_on_terminate = 0; + (*d)->cmd.cmd.bits.terminate_flush = 0; + (*d)->cmd.cmd.bits.pio_words = 0; + (*d)->cmd.cmd.bits.bytes = 0; + + (*d)->cmd.address = 0; + + mxs_dma_desc_append(dma_channel, (*d)); + d++; + + /* Prepare to receive an interrupt from the BCH block. */ + + init_completion(&nfc->bch_done); + + /* Go! */ + + error = gpmi_nfc_dma_go(this, dma_channel); + + if (error) + dev_err(dev, "[%s] DMA error\n", __func__); + + /* Wait for the interrupt from the BCH block. */ + + wait_for_completion(&nfc->bch_done); + + /* Return success. */ + + return error; + +} + +/* This structure represents the NFC HAL for this version of the hardware. */ + +struct nfc_hal gpmi_nfc_hal_v1 = { + .version = 1, + .description = "8-chip GPMI and BCH", + .max_chip_count = 8, + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> + BP_GPMI_TIMING0_DATA_SETUP), + .internal_data_setup_in_ns = 0, + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> + BP_GPMI_CTRL1_RDN_DELAY), + .max_dll_clock_period_in_ns = 32, + .max_dll_delay_in_ns = 16, + .init = init, + .set_geometry = set_geometry, + .set_timing = set_timing, + .get_timing = get_timing, + .exit = exit, + .begin = begin, + .end = end, + .clear_bch = clear_bch, + .is_ready = is_ready, + .send_command = send_command, + .send_data = send_data, + .read_data = read_data, + .send_page = send_page, + .read_page = read_page, +}; diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c new file mode 100644 index 000000000000..1400deb58e52 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c @@ -0,0 +1,1880 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/slab.h> +#include "gpmi-nfc.h" + +/* + * This structure contains the "safe" GPMI timing that should succeed with any + * NAND Flash device (although, with less-than-optimal performance). + */ + +static struct gpmi_nfc_timing safe_timing = { + .data_setup_in_ns = 80, + .data_hold_in_ns = 60, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, +}; + +/* + * This array has a pointer to every NFC HAL structure. The probing process will + * find and install the one that matches the version given by the platform. + */ + +static struct nfc_hal *(nfc_hals[]) = { + &gpmi_nfc_hal_v0, + &gpmi_nfc_hal_v1, +}; + +/* + * This array has a pointer to every Boot ROM Helper structure. The probing + * process will find and install the one that matches the version given by the + * platform. + */ + +static struct boot_rom_helper *(boot_rom_helpers[]) = { + &gpmi_nfc_boot_rom_helper_v0, + &gpmi_nfc_boot_rom_helper_v1, +}; + +/** + * show_device_report() - Contains a shell script that creates a handy report. + * + * @d: 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_report(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + static const char *script = + "GPMISysDirectory=/sys/bus/platform/devices/gpmi-nfc.0\n" + "\n" + "NodeList='\n" + "physical_geometry\n" + "nfc_info\n" + "nfc_geometry\n" + "timing\n" + "timing_diagram\n" + "rom_geometry\n" + "mtd_nand_info\n" + "mtd_info\n" + "'\n" + "\n" + "cd ${GPMISysDirectory}\n" + "\n" + "printf '\\n'\n" + "\n" + "for NodeName in ${NodeList}\n" + "do\n" + "\n" + " printf '--------------------------------------------\\n'\n" + " printf '%s\\n' ${NodeName}\n" + " printf '--------------------------------------------\\n'\n" + " printf '\\n'\n" + "\n" + " cat ${NodeName}\n" + "\n" + " printf '\\n'\n" + "\n" + "done\n" + ; + + return sprintf(buf, "%s", script); + +} + +/** + * show_device_numchips() - Shows the number of physical chips. + * + * This node is made obsolete by the physical_geometry node, but we keep it for + * backward compatibility (especially for kobs). + * + * @d: 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_numchips(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct physical_geometry *physical = &this->physical_geometry; + + return sprintf(buf, "%d\n", physical->chip_count); + +} + +/** + * 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 gpmi_nfc_data *this = dev_get_drvdata(dev); + struct nand_device_info *info = &this->device_info; + struct physical_geometry *physical = &this->physical_geometry; + + return sprintf(buf, + "Description : %s\n" + "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" + , + info->description, + physical->chip_count, + physical->chip_size_in_bytes, + physical->block_size_in_bytes, + physical->page_data_size_in_bytes, + physical->page_oob_size_in_bytes + ); + +} + +/** + * 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) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct nfc_hal *nfc = this->nfc; + + return sprintf(buf, + "Version : %u\n" + "Description : %s\n" + "Max Chip Count : %u\n" + "Max Data Setup Cycles : 0x%x\n" + "Internal Data Setup in ns : %u\n" + "Max Sample Delay Factor : 0x%x\n" + "Max DLL Clock Period in ns: %u\n" + "Max DLL Delay in ns : %u\n" + , + nfc->version, + nfc->description, + nfc->max_chip_count, + nfc->max_data_setup_cycles, + nfc->internal_data_setup_in_ns, + nfc->max_sample_delay_factor, + nfc->max_dll_clock_period_in_ns, + nfc->max_dll_delay_in_ns + ); + +} + +/** + * 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 gpmi_nfc_data *this = dev_get_drvdata(dev); + struct nfc_geometry *nfc = &this->nfc_geometry; + + return sprintf(buf, + "ECC Algorithm : %s\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n" + , + nfc->ecc_algorithm, + nfc->ecc_strength, + nfc->page_size_in_bytes, + nfc->metadata_size_in_bytes, + nfc->ecc_chunk_size_in_bytes, + nfc->ecc_chunk_count, + nfc->payload_size_in_bytes, + nfc->auxiliary_size_in_bytes, + nfc->auxiliary_status_offset, + nfc->block_mark_byte_offset, + nfc->block_mark_bit_offset + ); + +} + +/** + * show_device_rom_geometry() - Shows the Boot ROM Helper's 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_rom_geometry(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct boot_rom_geometry *rom = &this->rom_geometry; + + return sprintf(buf, + "Boot Area Count : %u\n" + "Boot Area Size in Bytes : %u\n" + "Stride Size in Pages : %u\n" + "Seach Area Stride Exponent: %u\n" + , + rom->boot_area_count, + rom->boot_area_size_in_bytes, + rom->stride_size_in_pages, + rom->search_area_stride_exponent + ); + +} + +/** + * 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 gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + struct nand_chip *nand = &mil->nand; + + o += sprintf(buf + o, + "Options : 0x%08x\n" + "Chip Count : %u\n" + "Chip Size in Bytes : %llu\n" + "Minimum Writable Size in Bytes: %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 gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + + o += sprintf(buf + o, + "Name : %s\n" + "Type : %u\n" + "Flags : 0x%08x\n" + "Size in Bytes : %llu\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_timing_diagram() - Shows a timing diagram. + * + * @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_timing_diagram(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct nfc_hal *nfc = this->nfc; + struct gpmi_nfc_timing timing = nfc->timing; + struct gpmi_nfc_hardware_timing hardware_timing; + unsigned long clock_frequency_in_hz; + unsigned long clock_period_in_ns; + unsigned int data_setup_in_ns; + unsigned int dll_delay_shift; + unsigned int sample_delay_in_ns; + unsigned int tDS_in_ns; + unsigned int tOPEN_in_ns; + unsigned int tCLOSE_in_ns; + unsigned int tEYE_in_ns; + unsigned int tDELAY_in_ns; + unsigned int tDS; + unsigned int tOPEN; + unsigned int tCLOSE; + unsigned int tEYE; + unsigned int tDELAY; + const unsigned int diagram_width_in_chars = 55; + unsigned int diagram_width_in_ns; + int o = 0; + unsigned int i; + + /* + * If there are any timing characteristics we need, but don't know, we + * pretend they're zero. + */ + + if (timing.tREA_in_ns < 0) + timing.tREA_in_ns = 0; + + if (timing.tRHOH_in_ns < 0) + timing.tRHOH_in_ns = 0; + + /* Get information about the current/last I/O transaction. */ + + nfc->get_timing(this, &clock_frequency_in_hz, &hardware_timing); + + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; + + /* Compute basic timing facts. */ + + data_setup_in_ns = + hardware_timing.data_setup_in_cycles * clock_period_in_ns; + + /* Compute data sample delay facts. */ + + dll_delay_shift = 3; + + if (hardware_timing.use_half_periods) + dll_delay_shift++; + + sample_delay_in_ns = + (hardware_timing.sample_delay_factor * clock_period_in_ns) >> + dll_delay_shift; + + /* Compute the basic metrics in the diagram, in nanoseconds. */ + + tDS_in_ns = data_setup_in_ns; + tOPEN_in_ns = pdata->max_prop_delay_in_ns + timing.tREA_in_ns; + tCLOSE_in_ns = pdata->min_prop_delay_in_ns + timing.tRHOH_in_ns; + tEYE_in_ns = tDS_in_ns + tCLOSE_in_ns - tOPEN_in_ns; + tDELAY_in_ns = sample_delay_in_ns; + + /* + * We need to translate nanosecond timings into character widths in the + * diagram. The first step is to discover how "wide" the diagram is in + * nanoseconds. That depends on which happens latest: the sample point + * or the close of the eye. + */ + + if (tCLOSE_in_ns >= tDELAY_in_ns) + diagram_width_in_ns = tDS_in_ns + tCLOSE_in_ns; + else + diagram_width_in_ns = tDS_in_ns + tDELAY_in_ns; + + /* Convert the metrics that appear in the diagram. */ + + tDS = (tDS_in_ns * diagram_width_in_chars) / diagram_width_in_ns; + tOPEN = (tOPEN_in_ns * diagram_width_in_chars) / diagram_width_in_ns; + tCLOSE = (tCLOSE_in_ns * diagram_width_in_chars) / diagram_width_in_ns; + tEYE = (tEYE_in_ns * diagram_width_in_chars) / diagram_width_in_ns; + tDELAY = (tDELAY_in_ns * diagram_width_in_chars) / diagram_width_in_ns; + + /* + * Show the results. + * + * This code is really ugly, but it draws a pretty picture :) + */ + + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, "Sample ______"); + for (i = 0; i < tDS; i++) + o += sprintf(buf + o, "_"); + if (tDELAY > 0) + for (i = 0; i < (tDELAY - 1); i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "|"); + for (i = 0; i < (diagram_width_in_chars - (tDS + tDELAY)); i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, "Strobe "); + for (i = 0; i < tDS; i++) + o += sprintf(buf + o, " "); + o += sprintf(buf + o, "|"); + if (tDELAY > 1) { + for (i = 2; i < tDELAY; i++) + o += sprintf(buf + o, "-"); + o += sprintf(buf + o, "|"); + } + o += sprintf(buf + o, " tDELAY\n"); + + + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " tDS "); + o += sprintf(buf + o, "|"); + if (tDS > 1) { + for (i = 2; i < tDS; i++) + o += sprintf(buf + o, "-"); + o += sprintf(buf + o, "|"); + } + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " ______"); + for (i = 0; i < tDS; i++) + o += sprintf(buf + o, " "); + for (i = 0; i < (diagram_width_in_chars - tDS); i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, "RDN "); + if (tDS > 0) { + if (tDS == 1) + o += sprintf(buf + o, "V"); + else { + o += sprintf(buf + o, "\\"); + for (i = 2; i < tDS; i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "/"); + } + } + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " tOPEN "); + o += sprintf(buf + o, "|"); + if (tOPEN > 1) { + for (i = 2; i < tOPEN; i++) + o += sprintf(buf + o, "-"); + o += sprintf(buf + o, "|"); + } + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " "); + for (i = 0; i < tDS; i++) + o += sprintf(buf + o, " "); + o += sprintf(buf + o, "|"); + if (tCLOSE > 1) { + for (i = 2; i < tCLOSE; i++) + o += sprintf(buf + o, "-"); + o += sprintf(buf + o, "|"); + } + o += sprintf(buf + o, " tCLOSE\n"); + + + o += sprintf(buf + o, " "); + for (i = 0; i < tOPEN; i++) + o += sprintf(buf + o, " "); + if (tEYE > 2) { + o += sprintf(buf + o, " "); + for (i = 2; i < tEYE; i++) + o += sprintf(buf + o, "_"); + } + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, "Data ______"); + for (i = 0; i < tOPEN; i++) + o += sprintf(buf + o, "_"); + if (tEYE > 0) { + if (tEYE == 1) + o += sprintf(buf + o, "|"); + else { + o += sprintf(buf + o, "/"); + for (i = 2; i < tEYE; i++) + o += sprintf(buf + o, " "); + o += sprintf(buf + o, "\\"); + } + } + for (i = 0; i < (diagram_width_in_chars - (tOPEN + tEYE)); i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " "); + for (i = 0; i < tOPEN; i++) + o += sprintf(buf + o, " "); + if (tEYE > 0) { + if (tEYE == 1) + o += sprintf(buf + o, "|"); + else { + o += sprintf(buf + o, "\\"); + for (i = 2; i < tEYE; i++) + o += sprintf(buf + o, "_"); + o += sprintf(buf + o, "/"); + } + } + o += sprintf(buf + o, "\n"); + + + o += sprintf(buf + o, " "); + for (i = 0; i < tOPEN; i++) + o += sprintf(buf + o, " "); + o += sprintf(buf + o, "|"); + if (tEYE > 1) { + for (i = 2; i < tEYE; i++) + o += sprintf(buf + o, "-"); + o += sprintf(buf + o, "|"); + } + o += sprintf(buf + o, " tEYE\n"); + + + o += sprintf(buf + o, "\n"); + o += sprintf(buf + o, "tDS : %u ns\n", tDS_in_ns); + o += sprintf(buf + o, "tOPEN : %u ns\n", tOPEN_in_ns); + o += sprintf(buf + o, "tCLOSE: %u ns\n", tCLOSE_in_ns); + o += sprintf(buf + o, "tEYE : %u ns\n", tEYE_in_ns); + o += sprintf(buf + o, "tDELAY: %u ns\n", tDELAY_in_ns); + o += sprintf(buf + o, "\n"); + + + return o; + +} + +/** + * 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 gpmi_nfc_data *this = dev_get_drvdata(dev); + + /* Invalidate the page cache. */ + + this->mil.nand.pagebuf = -1; + + /* Return success. */ + + return size; + +} + +/** + * store_device_mark_block_bad() - Marks a block as bad. + * + * @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_mark_block_bad(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + struct nand_chip *nand = &mil->nand; + unsigned long block_number; + loff_t byte_address; + int error; + + /* Look for nonsense. */ + + if (!size) + return -EINVAL; + + /* Try to understand the block number. */ + + if (strict_strtoul(buf, 0, &block_number)) + return -EINVAL; + + /* Compute the byte address of this block. */ + + byte_address = block_number << nand->phys_erase_shift; + + /* Attempt to mark the block bad. */ + + error = mtd->block_markbad(mtd, byte_address); + + if (error) + return error; + + /* Return success. */ + + return size; + +} + +/** + * show_device_ignorebad() - Shows the value of the 'ignorebad' flag. + * + * @d: 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_ignorebad(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + + return sprintf(buf, "%d\n", mil->ignore_bad_block_marks); +} + +/** + * store_device_ignorebad() - Sets the value of the 'ignorebad' 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_ignorebad(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + const char *p = buf; + unsigned long v; + + /* Try to make sense of what arrived from user space. */ + + if (strict_strtoul(p, 0, &v) < 0) + return size; + + if (v > 0) + v = 1; + + /* Only do something if the value is changing. */ + + if (v != mil->ignore_bad_block_marks) { + + if (v) { + + /* + * If control arrives here, we want to begin ignoring + * bad block marks. Reach into the NAND Flash MTD data + * structures and set the in-memory BBT pointer to NULL. + * This will cause the NAND Flash MTD code to believe + * that it never created a BBT and force it to call our + * block_bad function. + * + * See mil_block_bad for more details. + */ + + mil->saved_bbt = mil->nand.bbt; + mil->nand.bbt = 0; + + } else { + + /* + * If control arrives here, we want to stop ignoring + * bad block marks. Restore the NAND Flash MTD's pointer + * to its in-memory BBT. + */ + + mil->nand.bbt = mil->saved_bbt; + + } + + mil->ignore_bad_block_marks = v; + + } + + 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 gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + + return sprintf(buf, "%d\n", mil->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) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct mil *mil = &this->mil; + long new_inject_ecc_error; + + /* Look for nonsense. */ + + if (!size) + return -EINVAL; + + /* Try to understand the ECC error count. */ + + if (strict_strtol(buf, 0, &new_inject_ecc_error)) + return -EINVAL; + + /* Store the value. */ + + mil->inject_ecc_error = new_inject_ecc_error; + + /* Return success. */ + + return size; + +} + +/** + * show_device_timing_help() - Show help for setting timing. + * + * @d: 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_timing_help(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + static const char *help = + "<Data Setup>,<Data Hold>,<Address Setup>,<Sample Delay>," + "<tREA>,<tRLOH>,<tRHOH>\n"; + + return sprintf(buf, "%s", help); + +} + +/** + * show_device_timing() - Shows the current timing. + * + * @d: 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_timing(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct nfc_hal *nfc = this->nfc; + struct gpmi_nfc_timing *recorded = &nfc->timing; + unsigned long clock_frequency_in_hz; + unsigned long clock_period_in_ns; + struct gpmi_nfc_hardware_timing hardware; + unsigned int effective_data_setup_in_ns; + unsigned int effective_data_hold_in_ns; + unsigned int effective_address_setup_in_ns; + unsigned int dll_delay_shift; + unsigned int effective_sample_delay_in_ns; + + /* Get information about the current/last I/O transaction. */ + + nfc->get_timing(this, &clock_frequency_in_hz, &hardware); + + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; + + /* Compute basic timing facts. */ + + effective_data_setup_in_ns = + hardware.data_setup_in_cycles * clock_period_in_ns; + effective_data_hold_in_ns = + hardware.data_hold_in_cycles * clock_period_in_ns; + effective_address_setup_in_ns = + hardware.address_setup_in_cycles * clock_period_in_ns; + + /* Compute data sample delay facts. */ + + dll_delay_shift = 3; + + if (hardware.use_half_periods) + dll_delay_shift++; + + effective_sample_delay_in_ns = + (hardware.sample_delay_factor * clock_period_in_ns) >> + dll_delay_shift; + + /* Show the results. */ + + return sprintf(buf, + "Minimum Propagation Delay in ns : %u\n" + "Maximum Propagation Delay in ns : %u\n" + "Clock Frequency in Hz : %lu\n" + "Clock Period in ns : %lu\n" + "Recorded Data Setup in ns : %d\n" + "Hardware Data Setup in cycles : %u\n" + "Effective Data Setup in ns : %u\n" + "Recorded Data Hold in ns : %d\n" + "Hardware Data Hold in cycles : %u\n" + "Effective Data Hold in ns : %u\n" + "Recorded Address Setup in ns : %d\n" + "Hardware Address Setup in cycles: %u\n" + "Effective Address Setup in ns : %u\n" + "Using Half Period : %s\n" + "Recorded Sample Delay in ns : %d\n" + "Hardware Sample Delay Factor : %u\n" + "Effective Sample Delay in ns : %u\n" + "Recorded tREA in ns : %d\n" + "Recorded tRLOH in ns : %d\n" + "Recorded tRHOH in ns : %d\n" + , + pdata->min_prop_delay_in_ns, + pdata->max_prop_delay_in_ns, + clock_frequency_in_hz, + clock_period_in_ns, + recorded->data_setup_in_ns, + hardware .data_setup_in_cycles, + effective_data_setup_in_ns, + recorded->data_hold_in_ns, + hardware .data_hold_in_cycles, + effective_data_hold_in_ns, + recorded->address_setup_in_ns, + hardware .address_setup_in_cycles, + effective_address_setup_in_ns, + hardware .use_half_periods ? "Yes" : "No", + recorded->gpmi_sample_delay_in_ns, + hardware .sample_delay_factor, + effective_sample_delay_in_ns, + recorded->tREA_in_ns, + recorded->tRLOH_in_ns, + recorded->tRHOH_in_ns); + +} + +/** + * store_device_timing() - Sets the current timing. + * + * @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_timing(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpmi_nfc_data *this = dev_get_drvdata(dev); + struct nfc_hal *nfc = this->nfc; + const char *p = buf; + const char *q; + char tmps[20]; + long t; + struct gpmi_nfc_timing new; + + int8_t *field_pointers[] = { + &new.data_setup_in_ns, + &new.data_hold_in_ns, + &new.address_setup_in_ns, + &new.gpmi_sample_delay_in_ns, + &new.tREA_in_ns, + &new.tRLOH_in_ns, + &new.tRHOH_in_ns, + NULL, + }; + + int8_t **field_pointer = field_pointers; + + /* + * Loop over comma-separated timing values in the incoming buffer, + * assigning them to fields in the timing structure as we go along. + */ + + while (*field_pointer != NULL) { + + /* Clear out the temporary buffer. */ + + memset(tmps, 0, sizeof(tmps)); + + /* Copy the timing value into the temporary buffer. */ + + q = strchr(p, ','); + if (q) + strncpy(tmps, p, min_t(int, sizeof(tmps) - 1, q - p)); + else + strncpy(tmps, p, sizeof(tmps) - 1); + + /* Attempt to convert the current timing value. */ + + if (strict_strtol(tmps, 0, &t) < 0) + return -EINVAL; + + if ((t > 127) || (t < -128)) + return -EINVAL; + + /* Assign this value to the current field. */ + + **field_pointer = (int8_t) t; + field_pointer++; + + /* Check if we ran out of input too soon. */ + + if (!q && *field_pointer) + return -EINVAL; + + /* Move past the comma to the next timing value. */ + + p = q + 1; + + } + + /* Hand over the timing to the NFC. */ + + nfc->set_timing(this, &new); + + /* Return success. */ + + return size; + +} + +/* Device attributes that appear in sysfs. */ + +static DEVICE_ATTR(report , 0555, show_device_report , 0); +static DEVICE_ATTR(numchips , 0444, show_device_numchips , 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(rom_geometry , 0444, show_device_rom_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(timing_diagram , 0444, show_device_timing_diagram , 0); +static DEVICE_ATTR(timing_help , 0444, show_device_timing_help , 0); + +static DEVICE_ATTR(invalidate_page_cache, 0644, + 0, store_device_invalidate_page_cache); + +static DEVICE_ATTR(mark_block_bad, 0200, + 0, store_device_mark_block_bad); + +static DEVICE_ATTR(ignorebad, 0644, + show_device_ignorebad, store_device_ignorebad); + +static DEVICE_ATTR(inject_ecc_error, 0644, + show_device_inject_ecc_error, store_device_inject_ecc_error); + +static DEVICE_ATTR(timing, 0644, + show_device_timing, store_device_timing); + +static struct device_attribute *device_attributes[] = { + &dev_attr_report, + &dev_attr_numchips, + &dev_attr_physical_geometry, + &dev_attr_nfc_info, + &dev_attr_nfc_geometry, + &dev_attr_rom_geometry, + &dev_attr_mtd_nand_info, + &dev_attr_mtd_info, + &dev_attr_invalidate_page_cache, + &dev_attr_mark_block_bad, + &dev_attr_ignorebad, + &dev_attr_inject_ecc_error, + &dev_attr_timing, + &dev_attr_timing_help, + &dev_attr_timing_diagram, +}; + +/** + * validate_the_platform() - Validates information about the platform. + * + * @pdev: A pointer to the platform device data structure. + */ +static int validate_the_platform(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpmi_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; + +} + +/** + * acquire_register_block() - Tries to acquire and map a register block. + * + * @this: Per-device data. + * @resource_name: The name of the resource. + * @reg_block_base: A pointer to a variable that will receive the address of + * the mapped register block. + */ +static int acquire_register_block(struct gpmi_nfc_data *this, + const char *resource_name, void **reg_block_base) +{ + struct platform_device *pdev = this->pdev; + struct device *dev = this->dev; + void *p; + struct resource *r; + + /* Attempt to get information about the given resource. */ + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, resource_name); + + if (!r) { + dev_err(dev, "Can't get resource information for '%s'\n", + resource_name); + return -ENXIO; + } + + /* Attempt to remap the register block. */ + + p = ioremap(r->start, r->end - r->start + 1); + + if (!p) { + dev_err(dev, "Can't remap %s\n", resource_name); + return -EIO; + } + + /* If control arrives here, everything went fine. */ + + *reg_block_base = p; + + return 0; + +} + +/** + * release_register_block() - Releases a register block. + * + * @this: Per-device data. + * @reg_block_base: A pointer to the mapped register block. + */ +static void release_register_block(struct gpmi_nfc_data *this, + void *reg_block_base) +{ + iounmap(reg_block_base); +} + +/** + * acquire_interrupt() - Tries to acquire an interrupt. + * + * @this: Per-device data. + * @resource_name: The name of the resource. + * @interrupt_handler: A pointer to the function that will handle interrupts + * from this interrupt number. + * @interrupt_number: A pointer to a variable that will receive the acquired + * interrupt number. + */ +static int acquire_interrupt( + struct gpmi_nfc_data *this, const char *resource_name, + irq_handler_t interrupt_handler, int *interrupt_number) +{ + struct platform_device *pdev = this->pdev; + struct device *dev = this->dev; + int error = 0; + int i; + + /* Attempt to get information about the given resource. */ + + i = platform_get_irq_byname(pdev, resource_name); + + if (i < 0) { + dev_err(dev, "Can't get resource information for '%s'\n", + resource_name); + return -ENXIO; + } + + /* Attempt to own the interrupt. */ + + error = request_irq(i, interrupt_handler, 0, resource_name, this); + + if (error) { + dev_err(dev, "Can't own %s\n", resource_name); + return -EIO; + } + + /* If control arrives here, everything went fine. */ + + *interrupt_number = i; + + return 0; + +} + +/** + * release_interrupt() - Releases an interrupt. + * + * @this: Per-device data. + * @interrupt_number: The interrupt number. + */ +static void release_interrupt(struct gpmi_nfc_data *this, int interrupt_number) +{ + free_irq(interrupt_number, this); +} + +/** + * acquire_dma_channels() - Tries to acquire DMA channels. + * + * @this: Per-device data. + * @resource_name: The name of the resource. + * @low_channel: A pointer to a variable that will receive the acquired + * low DMA channel number. + * @high_channel: A pointer to a variable that will receive the acquired + * high DMA channel number. + */ +static int acquire_dma_channels( + struct gpmi_nfc_data *this, const char *resource_name, + unsigned *low_channel, unsigned *high_channel) +{ + struct platform_device *pdev = this->pdev; + struct device *dev = this->dev; + int error = 0; + struct resource *r; + unsigned int dma_channel; + + /* Attempt to get information about the given resource. */ + + r = platform_get_resource_byname(pdev, IORESOURCE_DMA, resource_name); + + if (!r) { + dev_err(dev, "Can't get resource information for '%s'\n", + resource_name); + return -ENXIO; + } + + /* Loop over DMA channels, attempting to own them. */ + + for (dma_channel = r->start; dma_channel <= r->end; dma_channel++) { + + /* Attempt to own the current channel. */ + + error = mxs_dma_request(dma_channel, dev, resource_name); + + /* Check if we successfully acquired the current channel. */ + + if (error) { + + dev_err(dev, "Can't acquire DMA channel %u\n", + dma_channel); + + /* Free all the channels we've already acquired. */ + + while (--dma_channel >= 0) + mxs_dma_release(dma_channel, dev); + + return error; + + } + + /* + * If control arrives here, we successfully acquired the + * current channel. Continue initializing it. + */ + + mxs_dma_reset(dma_channel); + mxs_dma_ack_irq(dma_channel); + + } + + /* If control arrives here, all went well. */ + + *low_channel = r->start; + *high_channel = r->end; + + return 0; + +} + +/** + * release_dma_channels() - Releases DMA channels. + * + * @this: Per-device data. + * @low_channel: The low DMA channel number. + * @high_channel: The high DMA channel number. + */ +static void release_dma_channels(struct gpmi_nfc_data *this, + unsigned low_channel, unsigned high_channel) +{ + struct device *dev = this->dev; + unsigned int i; + + for (i = low_channel; i <= high_channel; i++) + mxs_dma_release(i, dev); +} + +/** + * acquire_clock() - Tries to acquire a clock. + * + * @this: Per-device data. + * @resource_name: The name of the clock. + * @high_channel: A pointer to a variable that will receive the acquired + * clock address. + */ +static int acquire_clock(struct gpmi_nfc_data *this, + const char *clock_name, struct clk **clock) +{ + struct device *dev = this->dev; + int error = 0; + struct clk *c; + + /* Try to get the clock. */ + + c = clk_get(dev, clock_name); + + if (IS_ERR(c)) { + error = PTR_ERR(c); + dev_err(dev, "Can't own clock %s\n", clock_name); + return error; + } + + /* If control arrives here, everything went fine. */ + + *clock = c; + + return 0; + +} + +/** + * release_clock() - Releases a clock. + * + * @this: Per-device data. + * @clock: A pointer to the clock structure. + */ +static void release_clock(struct gpmi_nfc_data *this, struct clk *clock) +{ + clk_disable(clock); + clk_put(clock); +} + +/** + * acquire_resources() - Tries to acquire resources. + * + * @this: Per-device data. + */ +static int acquire_resources(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct resources *resources = &this->resources; + int error = 0; + + /* Attempt to acquire the GPMI register block. */ + + error = acquire_register_block(this, + GPMI_NFC_GPMI_REGS_ADDR_RES_NAME, &(resources->gpmi_regs)); + + if (error) + goto exit_gpmi_regs; + + /* Attempt to acquire the BCH register block. */ + + error = acquire_register_block(this, + GPMI_NFC_BCH_REGS_ADDR_RES_NAME, &(resources->bch_regs)); + + if (error) + goto exit_bch_regs; + + /* Attempt to acquire the BCH interrupt. */ + + error = acquire_interrupt(this, + GPMI_NFC_BCH_INTERRUPT_RES_NAME, + gpmi_nfc_bch_isr, &(resources->bch_interrupt)); + + if (error) + goto exit_bch_interrupt; + + /* Attempt to acquire the DMA channels. */ + + error = acquire_dma_channels(this, + GPMI_NFC_DMA_CHANNELS_RES_NAME, + &(resources->dma_low_channel), &(resources->dma_high_channel)); + + if (error) + goto exit_dma_channels; + + /* Attempt to acquire the DMA interrupt. */ + + error = acquire_interrupt(this, + GPMI_NFC_DMA_INTERRUPT_RES_NAME, + gpmi_nfc_dma_isr, &(resources->dma_interrupt)); + + if (error) + goto exit_dma_interrupt; + + /* Attempt to acquire our clock. */ + + error = acquire_clock(this, pdata->clock_name, &(resources->clock)); + + if (error) + goto exit_clock; + + /* If control arrives here, all went well. */ + + return 0; + + /* Control arrives here if something went wrong. */ + +exit_clock: + release_interrupt(this, resources->dma_interrupt); +exit_dma_interrupt: + release_dma_channels(this, + resources->dma_low_channel, resources->dma_high_channel); +exit_dma_channels: + release_interrupt(this, resources->bch_interrupt); +exit_bch_interrupt: + release_register_block(this, resources->bch_regs); +exit_bch_regs: + release_register_block(this, resources->gpmi_regs); +exit_gpmi_regs: + + return error; + +} + +/** + * release_resources() - Releases resources. + * + * @this: Per-device data. + */ +static void release_resources(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + release_clock(this, resources->clock); + release_register_block(this, resources->gpmi_regs); + release_register_block(this, resources->bch_regs); + release_interrupt(this, resources->bch_interrupt); + release_dma_channels(this, + resources->dma_low_channel, resources->dma_high_channel); + release_interrupt(this, resources->dma_interrupt); +} + +/** + * set_up_nfc_hal() - Sets up the NFC HAL. + * + * @this: Per-device data. + */ +static int set_up_nfc_hal(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct device *dev = this->dev; + struct nfc_hal *nfc; + int error = 0; + unsigned int i; + + /* Attempt to find an NFC HAL that matches the given version. */ + + for (i = 0; i < ARRAY_SIZE(nfc_hals); i++) { + + nfc = nfc_hals[i]; + + if (nfc->version == pdata->nfc_version) { + this->nfc = nfc; + break; + } + + } + + /* Check if we found a HAL. */ + + if (i >= ARRAY_SIZE(nfc_hals)) { + dev_err(dev, "Unkown NFC version %u\n", pdata->nfc_version); + return -ENXIO; + } + + pr_info("NFC: Version %u, %s\n", nfc->version, nfc->description); + + /* + * Check if we can handle the number of chips called for by the platform + * data. + */ + + if (pdata->max_chip_count > nfc->max_chip_count) { + dev_err(dev, "Platform data calls for %u chips " + "but NFC supports a max of %u.\n", + pdata->max_chip_count, nfc->max_chip_count); + return -ENXIO; + } + + /* Initialize the NFC HAL. */ + + error = nfc->init(this); + + if (error) + return error; + + /* Set up safe timing. */ + + nfc->set_timing(this, &safe_timing); + + /* + * If control arrives here, all is well. + */ + + return 0; + +} + +/** + * set_up_boot_rom_helper() - Sets up the Boot ROM Helper. + * + * @this: Per-device data. + */ +static int set_up_boot_rom_helper(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct device *dev = this->dev; + unsigned int i; + struct boot_rom_helper *rom; + + /* Attempt to find a Boot ROM Helper that matches the given version. */ + + for (i = 0; i < ARRAY_SIZE(boot_rom_helpers); i++) { + + rom = boot_rom_helpers[i]; + + if (rom->version == pdata->boot_rom_version) { + this->rom = rom; + break; + } + + } + + /* Check if we found a Boot ROM Helper. */ + + if (i >= ARRAY_SIZE(boot_rom_helpers)) { + dev_err(dev, "Unkown Boot ROM version %u\n", + pdata->boot_rom_version); + return -ENXIO; + } + + pr_info("Boot ROM: Version %u, %s\n", rom->version, rom->description); + + /* + * If control arrives here, all is well. + */ + + return 0; + +} + +/** + * manage_sysfs_files() - Creates/removes sysfs files for this device. + * + * @this: Per-device data. + */ +static void manage_sysfs_files(struct gpmi_nfc_data *this, int create) +{ + struct device *dev = this->dev; + 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(dev, *attr); + if (error) { + while (--attr >= device_attributes) + device_remove_file(dev, *attr); + return; + } + } else { + device_remove_file(dev, *attr); + } + + } + +} + +/** + * gpmi_nfc_probe() - Probes for a device and, if possible, takes ownership. + * + * @pdev: A pointer to the platform device data structure. + */ +static int gpmi_nfc_probe(struct platform_device *pdev) +{ + int error = 0; + struct device *dev = &pdev->dev; + struct gpmi_nfc_platform_data *pdata = pdev->dev.platform_data; + struct gpmi_nfc_data *this = 0; + + /* 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; + } + + /* Set up our data structures. */ + + platform_set_drvdata(pdev, this); + + this->pdev = pdev; + this->dev = &pdev->dev; + this->pdata = pdata; + + /* Acquire the resources we need. */ + + error = acquire_resources(this); + + if (error) + goto exit_acquire_resources; + + /* Set up the NFC. */ + + error = set_up_nfc_hal(this); + + if (error) + goto exit_nfc_init; + + /* Set up the platform. */ + + if (pdata->platform_init) + error = pdata->platform_init(pdata->max_chip_count); + + if (error) + goto exit_platform_init; + + /* Set up the Boot ROM Helper. */ + + error = set_up_boot_rom_helper(this); + + if (error) + goto exit_boot_rom_helper_init; + + /* Initialize the MTD Interface Layer. */ + + error = gpmi_nfc_mil_init(this); + + if (error) + goto exit_mil_init; + + /* Create sysfs entries for this device. */ + + manage_sysfs_files(this, true); + + /* Return success. */ + + return 0; + + /* Error return paths begin here. */ + +exit_mil_init: +exit_boot_rom_helper_init: + if (pdata->platform_exit) + pdata->platform_exit(pdata->max_chip_count); +exit_platform_init: + this->nfc->exit(this); +exit_nfc_init: + release_resources(this); +exit_acquire_resources: + platform_set_drvdata(pdev, NULL); + kfree(this); +exit_allocate_this: +exit_validate_platform: + return error; + +} + +/** + * gpmi_nfc_remove() - Dissociates this driver from the given device. + * + * @pdev: A pointer to the platform device data structure. + */ +static int __exit gpmi_nfc_remove(struct platform_device *pdev) +{ + struct gpmi_nfc_data *this = platform_get_drvdata(pdev); + struct gpmi_nfc_platform_data *pdata = this->pdata; + + manage_sysfs_files(this, false); + gpmi_nfc_mil_exit(this); + if (pdata->platform_exit) + pdata->platform_exit(pdata->max_chip_count); + this->nfc->exit(this); + release_resources(this); + platform_set_drvdata(pdev, NULL); + kfree(this); + + return 0; +} + +#ifdef CONFIG_PM + +/** + * gpmi_nfc_suspend() - Puts the NFC into a low power state. + * + * @pdev: A pointer to the platform device data structure. + * @state: The new power state. + */ +static int gpmi_nfc_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +/** + * gpmi_nfc_resume() - Brings the NFC back from a low power state. + * + * @pdev: A pointer to the platform device data structure. + */ +static int gpmi_nfc_resume(struct platform_device *pdev) +{ + 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 gpmi_nfc_driver = { + .driver = { + .name = GPMI_NFC_DRIVER_NAME, + }, + .probe = gpmi_nfc_probe, + .remove = __exit_p(gpmi_nfc_remove), + .suspend = gpmi_nfc_suspend, + .resume = gpmi_nfc_resume, +}; + +/** + * gpmi_nfc_init() - Initializes this module. + */ +static int __init gpmi_nfc_init(void) +{ + + pr_info("i.MX GPMI NFC\n"); + + /* Register this driver with the platform management system. */ + + if (platform_driver_register(&gpmi_nfc_driver) != 0) { + pr_err("i.MX GPMI NFC driver registration failed\n"); + return -ENODEV; + } + + /* Return success. */ + + return 0; + +} + +/** + * gpmi_nfc_exit() - Deactivates this module. + */ +static void __exit gpmi_nfc_exit(void) +{ + platform_driver_unregister(&gpmi_nfc_driver); +} + +module_init(gpmi_nfc_init); +module_exit(gpmi_nfc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c new file mode 100644 index 000000000000..cb5dc4d50e87 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c @@ -0,0 +1,2600 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/slab.h> +#include "gpmi-nfc.h" + +/* + * Indicates the driver should register the MTD that represents the entire + * medium, thus making it visible. + */ + +static int register_main_mtd; +module_param(register_main_mtd, int, 0400); + +/* + * Indicates the driver should attempt to perform DMA directly to/from buffers + * passed into this driver. This is true by default. If false, the driver will + * *always* copy incoming/outgoing data to/from its own DMA buffers. + */ + +static int map_io_buffers = true; +module_param(map_io_buffers, int, 0600); + +/** + * mil_outgoing_buffer_dma_begin() - Begins DMA on an outgoing buffer. + * + * @this: Per-device data. + * @source: The source buffer. + * @length: The length of the data in the source buffer. + * @alt_virt: The virtual address of an alternate buffer which is ready to be + * used for DMA. + * @alt_phys: The physical address of an alternate buffer which is ready to be + * used for DMA. + * @alt_size: The size of the alternate buffer. + * @use_virt: A pointer to a variable that will receive the virtual address to + * use. + * @use_phys: A pointer to a variable that will receive the physical address to + * use. + */ +static int mil_outgoing_buffer_dma_begin(struct gpmi_nfc_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + dma_addr_t source_phys = ~0; + + /* + * If we can, we want to use the caller's buffer directly for DMA. Check + * if the system will let us map them. + */ + + if (map_io_buffers && virt_addr_valid(source)) + source_phys = + dma_map_single(dev, + (void *) source, length, DMA_TO_DEVICE); + + if (dma_mapping_error(dev, source_phys)) { + + /* + * If control arrives here, we're not mapping the source buffer. + * Make sure the alternate is large enough. + */ + + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small " + "for outgoing I/O\n"); + return -ENOMEM; + } + + /* + * Copy the contents of the source buffer into the alternate + * buffer and set up the return values accordingly. + */ + + memcpy(alt_virt, source, length); + + *use_virt = alt_virt; + *use_phys = alt_phys; + + } else { + + /* + * If control arrives here, we're mapping the source buffer. Set + * up the return values accordingly. + */ + + *use_virt = source; + *use_phys = source_phys; + + } + + /* If control arrives here, all is well. */ + + return 0; + +} + +/** + * mil_outgoing_buffer_dma_end() - Ends DMA on an outgoing buffer. + * + * @this: Per-device data. + * @source: The source buffer. + * @length: The length of the data in the source buffer. + * @alt_virt: The virtual address of an alternate buffer which was ready to be + * used for DMA. + * @alt_phys: The physical address of an alternate buffer which was ready to + * be used for DMA. + * @alt_size: The size of the alternate buffer. + * @used_virt: The virtual address that was used. + * @used_phys: The physical address that was used. + */ +static void mil_outgoing_buffer_dma_end(struct gpmi_nfc_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void *used_virt, dma_addr_t used_phys) +{ + struct device *dev = this->dev; + + /* + * Check if we used the source buffer, and it's not one of our own DMA + * buffers. If so, we need to unmap it. + */ + + if (used_virt == source) + dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); + +} + +/** + * mil_incoming_buffer_dma_begin() - Begins DMA on an incoming buffer. + * + * @this: Per-device data. + * @destination: The destination buffer. + * @length: The length of the data that will arrive. + * @alt_virt: The virtual address of an alternate buffer which is ready + * to be used for DMA. + * @alt_phys: The physical address of an alternate buffer which is ready + * to be used for DMA. + * @alt_size: The size of the alternate buffer. + * @use_virt: A pointer to a variable that will receive the virtual address + * to use. + * @use_phys: A pointer to a variable that will receive the physical address + * to use. + */ +static int mil_incoming_buffer_dma_begin(struct gpmi_nfc_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + dma_addr_t destination_phys = ~0; + + /* + * If we can, we want to use the caller's buffer directly for DMA. Check + * if the system will let us map them. + */ + + if (map_io_buffers && virt_addr_valid(destination)) + destination_phys = + dma_map_single(dev, + (void *) destination, length, DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, destination_phys)) { + + /* + * If control arrives here, we're not mapping the destination + * buffer. Make sure the alternate is large enough. + */ + + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small " + "for incoming I/O\n"); + return -ENOMEM; + } + + /* Set up the return values to use the alternate. */ + + *use_virt = alt_virt; + *use_phys = alt_phys; + + } else { + + /* + * If control arrives here, we're mapping the destination + * buffer. Set up the return values accordingly. + */ + + *use_virt = destination; + *use_phys = destination_phys; + + } + + /* If control arrives here, all is well. */ + + return 0; + +} + +/** + * mil_incoming_buffer_dma_end() - Ends DMA on an incoming buffer. + * + * @this: Per-device data. + * @destination: The destination buffer. + * @length: The length of the data that arrived. + * @alt_virt: The virtual address of an alternate buffer which was ready to + * be used for DMA. + * @alt_phys: The physical address of an alternate buffer which was ready to + * be used for DMA. + * @alt_size: The size of the alternate buffer. + * @used_virt: The virtual address that was used. + * @used_phys: The physical address that was used. + */ +static void mil_incoming_buffer_dma_end(struct gpmi_nfc_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void *used_virt, dma_addr_t used_phys) +{ + struct device *dev = this->dev; + + /* + * Check if we used the destination buffer, and it's not one of our own + * DMA buffers. If so, we need to unmap it. + */ + + if (used_virt == destination) + dma_unmap_single(dev, used_phys, length, DMA_FROM_DEVICE); + else + memcpy(destination, alt_virt, length); + +} + +/** + * mil_cmd_ctrl - MTD Interface cmd_ctrl() + * + * This is the function that we install in the cmd_ctrl function pointer of the + * owning struct nand_chip. The only functions in the reference implementation + * that use these functions pointers are cmdfunc and select_chip. + * + * In this driver, we implement our own select_chip, so this function will only + * be called by the reference implementation's cmdfunc. For this reason, we can + * ignore the chip enable bit and concentrate only on sending bytes to the + * NAND Flash. + * + * @mtd: The owning MTD. + * @data: The value to push onto the data signals. + * @ctrl: The values to push onto the control signals. + */ +static void mil_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *nand = mtd->priv; + struct gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct mil *mil = &this->mil; + struct nfc_hal *nfc = this->nfc; + int error; +#if defined(CONFIG_MTD_DEBUG) + unsigned int i; + char display[MIL_COMMAND_BUFFER_SIZE * 5]; +#endif + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will deassert + * both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. + */ + + if ((ctrl & (NAND_ALE | NAND_CLE))) { + if (data != NAND_CMD_NONE) + mil->cmd_virt[mil->command_length++] = data; + return; + } + + /* + * If control arrives here, MTD has deasserted both the ALE and CLE, + * which means it's ready to run an operation. Check if we have any + * bytes to send. + */ + + if (!mil->command_length) + return; + + /* Hand the command over to the NFC. */ + + gpmi_nfc_add_event("mil_cmd_ctrl sending command...", 1); + +#if defined(CONFIG_MTD_DEBUG) + display[0] = 0; + for (i = 0; i < mil->command_length; i++) + sprintf(display + strlen(display), " 0x%02x", + mil->cmd_virt[i] & 0xff); + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc cmd_ctrl] command: %s\n", display); +#endif + + error = nfc->send_command(this, + mil->current_chip, mil->cmd_phys, mil->command_length); + + if (error) { + dev_err(dev, "[%s] Chip: %u, Error %d\n", + __func__, mil->current_chip, error); + print_hex_dump(KERN_ERR, + " Command Bytes: ", DUMP_PREFIX_NONE, 16, 1, + mil->cmd_virt, mil->command_length, 0); + } + + gpmi_nfc_add_event("...Finished", -1); + + /* Reset. */ + + mil->command_length = 0; + +} + +/** + * 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 gpmi_nfc_data *this = nand->priv; + struct nfc_hal *nfc = this->nfc; + struct mil *mil = &this->mil; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc dev_ready]\n"); + + gpmi_nfc_add_event("> mil_dev_ready", 1); + + if (nfc->is_ready(this, mil->current_chip)) { + gpmi_nfc_add_event("< mil_dev_ready - Returning ready", -1); + return !0; + } else { + gpmi_nfc_add_event("< 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 gpmi_nfc_data *this = nand->priv; + struct mil *mil = &this->mil; + struct nfc_hal *nfc = this->nfc; + struct clk *clock = this->resources.clock; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc select_chip] chip: %d\n", chip); + + /* Figure out what kind of transition this is. */ + + if ((mil->current_chip < 0) && (chip >= 0)) { + gpmi_nfc_start_event_trace("> mil_select_chip"); + clk_enable(clock); + nfc->begin(this); + gpmi_nfc_add_event("< mil_select_chip", -1); + } else if ((mil->current_chip >= 0) && (chip < 0)) { + gpmi_nfc_add_event("> mil_select_chip", 1); + clk_disable(clock); + nfc->end(this); + gpmi_nfc_stop_event_trace("< mil_select_chip"); + } else { + gpmi_nfc_add_event("> mil_select_chip", 1); + gpmi_nfc_add_event("< mil_select_chip", -1); + } + + mil->current_chip = chip; + +} + +/** + * 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 gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mil *mil = &this->mil; + void *use_virt = 0; + dma_addr_t use_phys = ~0; + int error; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc readbuf] len: %d\n", len); + + gpmi_nfc_add_event("> mil_read_buf", 1); + + /* Set up DMA. */ + + error = mil_incoming_buffer_dma_begin(this, buf, len, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + &use_virt, &use_phys); + + if (error) { + dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__); + goto exit; + } + + /* Ask the NFC. */ + + nfc->read_data(this, mil->current_chip, use_phys, len); + + /* Finish with DMA. */ + + mil_incoming_buffer_dma_end(this, buf, len, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + use_virt, use_phys); + + /* Return. */ + +exit: + + gpmi_nfc_add_event("< mil_read_buf", -1); + +} + +/** + * 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 gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mil *mil = &this->mil; + const void *use_virt = 0; + dma_addr_t use_phys = ~0; + int error; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc writebuf] len: %d\n", len); + + gpmi_nfc_add_event("> mil_write_buf", 1); + + /* Set up DMA. */ + + error = mil_outgoing_buffer_dma_begin(this, buf, len, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + &use_virt, &use_phys); + + if (error) { + dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__); + goto exit; + } + + /* Ask the NFC. */ + + nfc->send_data(this, mil->current_chip, use_phys, len); + + /* Finish with DMA. */ + + mil_outgoing_buffer_dma_end(this, buf, len, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + use_virt, use_phys); + + /* Return. */ + +exit: + + gpmi_nfc_add_event("< mil_write_buf", -1); + +} + +/** + * 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; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc read_byte]\n"); + + gpmi_nfc_add_event("> mil_read_byte", 1); + + mil_read_buf(mtd, (uint8_t *) &byte, 1); + + gpmi_nfc_add_event("< mil_read_byte", -1); + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc read_byte]: 0x%02x\n", byte); + + return byte; + +} + +/** + * mil_handle_block_mark_swapping() - Handles block mark swapping. + * + * Note that, when this function is called, it doesn't know whether it's + * swapping the block mark, or swapping it *back* -- but it doesn't matter + * because the the operation is the same. + * + * @this: Per-device data. + * @payload: A pointer to the payload buffer. + * @auxiliary: A pointer to the auxiliary buffer. + */ +static void mil_handle_block_mark_swapping(struct gpmi_nfc_data *this, + void *payload, void *auxiliary) +{ + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct boot_rom_helper *rom = this->rom; + unsigned char *p; + unsigned char *a; + unsigned int bit; + unsigned char mask; + unsigned char from_data; + unsigned char from_oob; + + /* Check if we're doing block mark swapping. */ + + if (!rom->swap_block_mark) + return; + + /* + * If control arrives here, we're swapping. Make some convenience + * variables. + */ + + bit = nfc_geo->block_mark_bit_offset; + p = ((unsigned char *) payload) + nfc_geo->block_mark_byte_offset; + a = auxiliary; + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + + from_data = (p[0] >> bit) | (p[1] << (8 - bit)); + + /* Get the byte from the OOB. */ + + from_oob = a[0]; + + /* Swap them. */ + + a[0] = from_data; + + mask = (0x1 << bit) - 1; + p[0] = (p[0] & mask) | (from_oob << bit); + + mask = ~0 << bit; + p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); + +} + +/** + * 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) +{ + struct gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mil *mil = &this->mil; + void *payload_virt = 0; + dma_addr_t payload_phys = ~0; + void *auxiliary_virt = 0; + dma_addr_t auxiliary_phys = ~0; + unsigned int i; + unsigned char *status; + unsigned int failed; + unsigned int corrected; + int error = 0; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_page]\n"); + + gpmi_nfc_add_event("> mil_ecc_read_page", 1); + + /* + * Set up DMA. + * + * Notice that we don't try to use the caller's buffer as the auxiliary. + * We need to do a lot of fiddling to deliver the OOB, so there's no + * point. + */ + + error = mil_incoming_buffer_dma_begin(this, buf, mtd->writesize, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + &payload_virt, &payload_phys); + + if (error) { + dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__); + error = -ENOMEM; + goto exit_payload; + } + + auxiliary_virt = mil->auxiliary_virt; + auxiliary_phys = mil->auxiliary_phys; + + /* Ask the NFC. */ + + error = nfc->read_page(this, mil->current_chip, + payload_phys, auxiliary_phys); + + if (error) { + dev_err(dev, "[%s] Error in ECC-based read: %d\n", + __func__, error); + goto exit_nfc; + } + + /* Handle block mark swapping. */ + + mil_handle_block_mark_swapping(this, payload_virt, auxiliary_virt); + + /* Loop over status bytes, accumulating ECC status. */ + + failed = 0; + corrected = 0; + + status = ((unsigned char *) auxiliary_virt) + + nfc_geo->auxiliary_status_offset; + + for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { + + if ((*status == 0x00) || (*status == 0xff)) + continue; + + if (*status == 0xfe) { + failed++; + continue; + } + + corrected += *status; + + } + + /* Propagate ECC status to the owning MTD. */ + + mtd->ecc_stats.failed += failed; + mtd->ecc_stats.corrected += corrected; + + /* + * It's time to deliver the OOB bytes. See mil_ecc_read_oob() for + * details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the block + * mark to th caller's buffer. Note that, if block mark swapping was + * necessary, it has already been done, so we can rely on the first + * byte of the auxiliary buffer to contain the block mark. + */ + + memset(nand->oob_poi, ~0, mtd->oobsize); + + nand->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + + /* Return. */ + +exit_nfc: + mil_incoming_buffer_dma_end(this, buf, mtd->writesize, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + payload_virt, payload_phys); +exit_payload: + + gpmi_nfc_add_event("< mil_ecc_read_page", -1); + + return error; + +} + +/** + * 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 gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct nfc_hal *nfc = this->nfc; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct boot_rom_helper *rom = this->rom; + struct mil *mil = &this->mil; + const void *payload_virt = 0; + dma_addr_t payload_phys = ~0; + const void *auxiliary_virt = 0; + dma_addr_t auxiliary_phys = ~0; + int error; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_write_page]\n"); + + gpmi_nfc_add_event("> mil_ecc_write_page", 1); + + /* Set up DMA. */ + + if (rom->swap_block_mark) { + + /* + * If control arrives here, we're doing block mark swapping. + * Since we can't modify the caller's buffers, we must copy them + * into our own. + */ + + memcpy(mil->payload_virt, buf, mtd->writesize); + payload_virt = mil->payload_virt; + payload_phys = mil->payload_phys; + + memcpy(mil->auxiliary_virt, nand->oob_poi, mtd->oobsize); + auxiliary_virt = mil->auxiliary_virt; + auxiliary_phys = mil->auxiliary_phys; + + /* Handle block mark swapping. */ + + mil_handle_block_mark_swapping(this, + (void *) payload_virt, (void *) auxiliary_virt); + + } else { + + /* + * If control arrives here, we're not doing block mark swapping, + * so we can to try and use the caller's buffers. + */ + + error = mil_outgoing_buffer_dma_begin(this, + buf, mtd->writesize, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + &payload_virt, &payload_phys); + + if (error) { + dev_err(dev, "[%s] Inadequate payload DMA buffer\n", + __func__); + goto exit_payload; + } + + error = mil_outgoing_buffer_dma_begin(this, + nand->oob_poi, mtd->oobsize, + mil->auxiliary_virt, mil->auxiliary_phys, + nfc_geo->auxiliary_size_in_bytes, + &auxiliary_virt, &auxiliary_phys); + + if (error) { + dev_err(dev, "[%s] Inadequate auxiliary DMA buffer\n", + __func__); + goto exit_auxiliary; + } + + } + + /* Ask the NFC. */ + + error = nfc->send_page(this, mil->current_chip, + payload_phys, auxiliary_phys); + + if (error) + dev_err(dev, "[%s] Error in ECC-based write: %d\n", + __func__, error); + + /* Return. */ + + if (!rom->swap_block_mark) + mil_outgoing_buffer_dma_end(this, nand->oob_poi, mtd->oobsize, + mil->auxiliary_virt, mil->auxiliary_phys, + nfc_geo->auxiliary_size_in_bytes, + auxiliary_virt, auxiliary_phys); +exit_auxiliary: + if (!rom->swap_block_mark) + mil_outgoing_buffer_dma_end(this, buf, mtd->writesize, + mil->payload_virt, mil->payload_phys, + nfc_geo->payload_size_in_bytes, + payload_virt, payload_phys); +exit_payload: + + gpmi_nfc_add_event("< mil_ecc_write_page", -1); + +} + +/** + * mil_hook_read_oob() - Hooked MTD Interface read_oob(). + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. See the description of the raw_oob_mode field in + * struct mil for more information about this. + * + * @mtd: A pointer to the MTD. + * @from: The starting address to read. + * @ops: Describes the operation. + */ +static int mil_hook_read_oob(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops) +{ + register struct nand_chip *chip = mtd->priv; + struct gpmi_nfc_data *this = chip->priv; + struct mil *mil = &this->mil; + int ret; + + mil->raw_oob_mode = ops->mode == MTD_OOB_RAW; + ret = mil->hooked_read_oob(mtd, from, ops); + mil->raw_oob_mode = false; + return ret; +} + +/** + * mil_hook_write_oob() - Hooked MTD Interface write_oob(). + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. See the description of the raw_oob_mode field in + * struct mil for more information about this. + * + * @mtd: A pointer to the MTD. + * @to: The starting address to write. + * @ops: Describes the operation. + */ +static int mil_hook_write_oob(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops) +{ + register struct nand_chip *chip = mtd->priv; + struct gpmi_nfc_data *this = chip->priv; + struct mil *mil = &this->mil; + int ret; + + mil->raw_oob_mode = ops->mode == MTD_OOB_RAW; + ret = mil->hooked_write_oob(mtd, to, ops); + mil->raw_oob_mode = false; + return ret; +} + +/** + * mil_hook_block_markbad() - Hooked MTD Interface block_markbad(). + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. See the description of the marking_a_bad_block field + * in struct mil for more information about this. + * + * @mtd: A pointer to the MTD. + * @ofs: Byte address of the block to mark. + */ +static int mil_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + register struct nand_chip *chip = mtd->priv; + struct gpmi_nfc_data *this = chip->priv; + struct mil *mil = &this->mil; + int ret; + + mil->marking_a_bad_block = true; + ret = mil->hooked_block_markbad(mtd, ofs); + mil->marking_a_bad_block = false; + return ret; +} + +/** + * mil_ecc_read_oob() - MTD Interface ecc.read_oob(). + * + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark, so all + * write operations take measures to protect it. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * + * What we do for this specific read operation depends on two questions: + * + * 1) Are we doing a "raw" read, or an ECC-based read? + * + * 2) Are we using block mark swapping or transcription? + * + * There are four cases, illustrated by the following Karnaugh map: + * + * | Raw | ECC-based | + * -------------+-------------------------+-------------------------+ + * | Read the conventional | | + * | OOB at the end of the | | + * Swapping | page and return it. It | | + * | contains exactly what | | + * | we want. | Read the block mark and | + * -------------+-------------------------+ return it in a buffer | + * | Read the conventional | full of set bits. | + * | OOB at the end of the | | + * | page and also the block | | + * Transcribing | mark in the metadata. | | + * | Copy the block mark | | + * | into the first byte of | | + * | the OOB. | | + * -------------+-------------------------+-------------------------+ + * + * Note that we break rule #4 in the Transcribing/Raw case because we're not + * giving an accurate view of the actual, physical bytes in the page (we're + * overwriting the block mark). That's OK because it's more important to follow + * rule #2. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + * + * Since MTD assumes the OOB is not covered by ECC, there is no pair of + * ECC-based/raw functions for reading or or writing the OOB. The fact that the + * caller wants an ECC-based or raw view of the page is not propagated down to + * this driver. + * + * Since our OOB *is* covered by ECC, we need this information. So, we hook the + * ecc.read_oob and ecc.write_oob function pointers in the owning + * struct mtd_info with our own functions. These hook functions set the + * raw_oob_mode field so that, when control finally arrives here, we'll know + * what to do. + * + * @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 gpmi_nfc_data *this = nand->priv; + struct physical_geometry *physical = &this->physical_geometry; + struct mil *mil = &this->mil; + struct boot_rom_helper *rom = this->rom; + int block_mark_column; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_oob] " + "page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No"); + + gpmi_nfc_add_event("> mil_ecc_read_oob", 1); + + /* + * First, fill in the OOB buffer. If we're doing a raw read, we need to + * get the bytes from the physical page. If we're not doing a raw read, + * we need to fill the buffer with set bits. + */ + + if (mil->raw_oob_mode) { + + /* + * If control arrives here, we're doing a "raw" read. Send the + * command to read the conventional OOB. + */ + + nand->cmdfunc(mtd, NAND_CMD_READ0, + physical->page_data_size_in_bytes, page); + + /* Read out the conventional OOB. */ + + nand->read_buf(mtd, nand->oob_poi, mtd->oobsize); + + } else { + + /* + * If control arrives here, we're not doing a "raw" read. Fill + * the OOB buffer with set bits. + */ + + memset(nand->oob_poi, ~0, mtd->oobsize); + + } + + /* + * Now, we want to make sure the block mark is correct. In the + * Swapping/Raw case, we already have it. Otherwise, we need to + * explicitly read it. + */ + + if (!(rom->swap_block_mark && mil->raw_oob_mode)) { + + /* First, figure out where the block mark is. */ + + if (rom->swap_block_mark) + block_mark_column = physical->page_data_size_in_bytes; + else + block_mark_column = 0; + + /* Send the command to read the block mark. */ + + nand->cmdfunc(mtd, NAND_CMD_READ0, block_mark_column, page); + + /* Read the block mark into the first byte of the OOB buffer. */ + + nand->oob_poi[0] = nand->read_byte(mtd); + + } + + /* + * Return true, indicating that the next call to this function must send + * a command. + */ + + gpmi_nfc_add_event("< mil_ecc_read_oob", -1); + + return true; + +} + +/** + * mil_ecc_write_oob() - MTD Interface ecc.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 gpmi_nfc_data *this = nand->priv; + struct device *dev = this->dev; + struct physical_geometry *physical = &this->physical_geometry; + struct mil *mil = &this->mil; + struct boot_rom_helper *rom = this->rom; + uint8_t block_mark = 0; + int block_mark_column; + int status; + int error = 0; + + DEBUG(MTD_DEBUG_LEVEL2, + "[gpmi_nfc ecc_write_oob] page: 0x%06x\n", page); + + gpmi_nfc_add_event("> mil_ecc_write_oob", -1); + + /* + * There are fundamental incompatibilities between the i.MX GPMI NFC and + * the NAND Flash MTD model that make it essentially impossible to write + * the out-of-band bytes. + * + * We permit *ONE* exception. If the *intent* of writing the OOB is to + * mark a block bad, we can do that. + */ + + if (!mil->marking_a_bad_block) { + dev_emerg(dev, "This driver doesn't support writing the OOB\n"); + WARN_ON(1); + error = -EIO; + goto exit; + } + + /* + * If control arrives here, we're marking a block bad. First, figure out + * where the block mark is. + * + * If we're using swapping, the block mark is in the conventional + * location. Otherwise, we're using transcription, and the block mark + * appears in the first byte of the page. + */ + + if (rom->swap_block_mark) + block_mark_column = physical->page_data_size_in_bytes; + else + block_mark_column = 0; + + /* Write the block mark. */ + + nand->cmdfunc(mtd, NAND_CMD_SEQIN, block_mark_column, page); + nand->write_buf(mtd, &block_mark, 1); + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = nand->waitfunc(mtd, nand); + + /* Check if it worked. */ + + if (status & NAND_STATUS_FAIL) + error = -EIO; + + /* Return. */ + +exit: + + gpmi_nfc_add_event("< mil_ecc_write_oob", -1); + + return error; + +} + +/** + * mil_block_bad - Claims all blocks are good. + * + * In principle, this function is *only* called when the NAND Flash MTD system + * isn't allowed to keep an in-memory bad block table, so it is forced to ask + * the driver for bad block information. + * + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so + * this function is *only* called when we take it away. + * + * We take away the in-memory BBT when the user sets the "ignorebad" parameter, + * which indicates that all blocks should be reported good. + * + * Thus, this function is only called when we want *all* blocks to look good, + * so it *always* return success. + * + * @mtd: Ignored. + * @ofs: Ignored. + * @getchip: Ignored. + */ +static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/** + * mil_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 mil_set_physical_geometry(struct gpmi_nfc_data *this) +{ + struct mil *mil = &this->mil; + struct physical_geometry *physical = &this->physical_geometry; + struct nand_chip *nand = &mil->nand; + struct nand_device_info *info = &this->device_info; + unsigned int block_size_in_pages; + unsigned int chip_size_in_blocks; + unsigned int chip_size_in_pages; + uint64_t medium_size_in_bytes; + + /* + * Record the number of physical chips that MTD found. + */ + + physical->chip_count = nand->numchips; + + /* + * We know the total size of a page. We need to break that down into the + * data size and OOB size. The data size is the largest power of two + * that will fit in the given page size. The OOB size is what's left + * over. + */ + + physical->page_data_size_in_bytes = + 1 << (fls(info->page_total_size_in_bytes) - 1); + + physical->page_oob_size_in_bytes = + info->page_total_size_in_bytes - + physical->page_data_size_in_bytes; + + /* + * Now that we know the page data size, we can multiply this by the + * number of pages in a block to compute the block size. + */ + + physical->block_size_in_bytes = + physical->page_data_size_in_bytes * info->block_size_in_pages; + + /* Get the chip size. */ + + physical->chip_size_in_bytes = info->chip_size_in_bytes; + + /* Compute some interesting facts. */ + + block_size_in_pages = + physical->block_size_in_bytes >> + (fls(physical->page_data_size_in_bytes) - 1); + chip_size_in_pages = + physical->chip_size_in_bytes >> + (fls(physical->page_data_size_in_bytes) - 1); + chip_size_in_blocks = + physical->chip_size_in_bytes >> + (fls(physical->block_size_in_bytes) - 1); + medium_size_in_bytes = + physical->chip_size_in_bytes * physical->chip_count; + + /* Report. */ + + #if defined(DETAILED_INFO) + + pr_info("-----------------\n"); + pr_info("Physical Geometry\n"); + pr_info("-----------------\n"); + pr_info("Chip Count : %d\n", physical->chip_count); + pr_info("Page Data Size in Bytes: %u (0x%x)\n", + physical->page_data_size_in_bytes, + physical->page_data_size_in_bytes); + pr_info("Page OOB Size in Bytes : %u\n", + physical->page_oob_size_in_bytes); + pr_info("Block Size in Bytes : %u (0x%x)\n", + physical->block_size_in_bytes, + physical->block_size_in_bytes); + pr_info("Block Size in Pages : %u (0x%x)\n", + block_size_in_pages, + block_size_in_pages); + pr_info("Chip Size in Bytes : %llu (0x%llx)\n", + physical->chip_size_in_bytes, + physical->chip_size_in_bytes); + pr_info("Chip Size in Pages : %u (0x%x)\n", + chip_size_in_pages, chip_size_in_pages); + pr_info("Chip Size in Blocks : %u (0x%x)\n", + chip_size_in_blocks, chip_size_in_blocks); + pr_info("Medium Size in Bytes : %llu (0x%llx)\n", + medium_size_in_bytes, medium_size_in_bytes); + + #endif + + /* Return success. */ + + return 0; + +} + +/** + * mil_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 mil_set_nfc_geometry(struct gpmi_nfc_data *this) +{ + struct nfc_hal *nfc = this->nfc; +#if defined(DETAILED_INFO) + struct nfc_geometry *geo = &this->nfc_geometry; +#endif + /* Set the NFC geometry. */ + + if (nfc->set_geometry(this)) + return !0; + + /* Report. */ + + #if defined(DETAILED_INFO) + + pr_info("------------\n"); + pr_info("NFC Geometry\n"); + pr_info("------------\n"); + pr_info("ECC Algorithm : %s\n", geo->ecc_algorithm); + pr_info("ECC Strength : %u\n", geo->ecc_strength); + pr_info("Page Size in Bytes : %u\n", geo->page_size_in_bytes); + pr_info("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes); + pr_info("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes); + pr_info("ECC Chunk Count : %u\n", geo->ecc_chunk_count); + pr_info("Payload Size in Bytes : %u\n", geo->payload_size_in_bytes); + pr_info("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes); + pr_info("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset); + pr_info("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset); + pr_info("Block Mark Bit Offset : %u\n", geo->block_mark_bit_offset); + + #endif + + /* Return success. */ + + return 0; + +} + +/** + * mil_set_boot_rom_helper_geometry() - Set up the Boot ROM Helper geometry. + * + * @this: Per-device data. + */ +static int mil_set_boot_rom_helper_geometry(struct gpmi_nfc_data *this) +{ + struct boot_rom_helper *rom = this->rom; +#if defined(DETAILED_INFO) + struct boot_rom_geometry *geo = &this->rom_geometry; +#endif + + /* Set the Boot ROM Helper geometry. */ + + if (rom->set_geometry(this)) + return !0; + + /* Report. */ + + #if defined(DETAILED_INFO) + + pr_info("-----------------\n"); + pr_info("Boot ROM Geometry\n"); + pr_info("-----------------\n"); + pr_info("Boot Area Count : %u\n", geo->boot_area_count); + pr_info("Boot Area Size in Bytes : %u (0x%x)\n", + geo->boot_area_size_in_bytes, geo->boot_area_size_in_bytes); + pr_info("Stride Size in Pages : %u\n", geo->stride_size_in_pages); + pr_info("Search Area Stride Exponent: %u\n", + geo->search_area_stride_exponent); + + #endif + + /* Return success. */ + + return 0; + +} + +/** + * mil_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 mil_set_mtd_geometry(struct gpmi_nfc_data *this) +{ + struct physical_geometry *physical = &this->physical_geometry; + struct mil *mil = &this->mil; + struct nand_ecclayout *layout = &mil->oob_layout; + struct nand_chip *nand = &mil->nand; + struct mtd_info *mtd = &mil->mtd; + + /* Configure the struct nand_ecclayout. */ + + layout->eccbytes = 0; + layout->oobavail = physical->page_oob_size_in_bytes; + layout->oobfree[0].offset = 0; + layout->oobfree[0].length = physical->page_oob_size_in_bytes; + + /* Configure the struct mtd_info. */ + + mtd->size = nand->numchips * physical->chip_size_in_bytes; + mtd->erasesize = physical->block_size_in_bytes; + mtd->writesize = physical->page_data_size_in_bytes; + mtd->ecclayout = 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->chipsize = physical->chip_size_in_bytes; + nand->page_shift = ffs(mtd->writesize) - 1; + nand->pagemask = (nand->chipsize >> nand->page_shift) - 1; + nand->subpagesize = mtd->writesize >> mtd->subpage_sft; + nand->phys_erase_shift = ffs(mtd->erasesize) - 1; + nand->bbt_erase_shift = nand->phys_erase_shift; + nand->oob_poi = nand->buffers->databuf + mtd->writesize; + nand->ecc.layout = layout; + if (nand->chipsize & 0xffffffff) + nand->chip_shift = ffs((unsigned) nand->chipsize) - 1; + else + nand->chip_shift = + ffs((unsigned) (nand->chipsize >> 32)) + 32 - 1; + + /* Return success. */ + + return 0; + +} + +/** + * mil_set_geometry() - Set up the medium geometry. + * + * @this: Per-device data. + */ +static int mil_set_geometry(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct nfc_geometry *nfc_geo = &this->nfc_geometry; + struct mil *mil = &this->mil; + + /* Set up the various layers of geometry, in this specific order. */ + + if (mil_set_physical_geometry(this)) + return -ENXIO; + + if (mil_set_nfc_geometry(this)) + return -ENXIO; + + if (mil_set_boot_rom_helper_geometry(this)) + return -ENXIO; + + if (mil_set_mtd_geometry(this)) + return -ENXIO; + + /* + * Allocate the page buffer. + * + * Both the payload buffer and the auxiliary buffer must appear on + * 32-bit boundaries. We presume the size of the payload buffer is a + * power of two and is much larger than four, which guarantees the + * auxiliary buffer will appear on a 32-bit boundary. + */ + + mil->page_buffer_size = nfc_geo->payload_size_in_bytes + + nfc_geo->auxiliary_size_in_bytes; + + mil->page_buffer_virt = + dma_alloc_coherent(dev, mil->page_buffer_size, + &mil->page_buffer_phys, GFP_DMA); + + if (!mil->page_buffer_virt) + return -ENOMEM; + + /* Slice up the page buffer. */ + + mil->payload_virt = mil->page_buffer_virt; + mil->payload_phys = mil->page_buffer_phys; + + mil->auxiliary_virt = ((char *) mil->payload_virt) + + nfc_geo->payload_size_in_bytes; + mil->auxiliary_phys = mil->payload_phys + + nfc_geo->payload_size_in_bytes; + + /* Return success. */ + + return 0; + +} + +/** + * mil_pre_bbt_scan() - Prepare for the BBT scan. + * + * @this: Per-device data. + */ +static int mil_pre_bbt_scan(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct physical_geometry *physical = &this->physical_geometry; + struct boot_rom_helper *rom = this->rom; + struct mil *mil = &this->mil; + struct nand_chip *nand = &mil->nand; + struct mtd_info *mtd = &mil->mtd; + unsigned int block_count; + unsigned int block; + int chip; + int page; + loff_t byte; + uint8_t block_mark; + int error; + + /* + * Check if we can use block mark swapping, which enables us to leave + * the block marks where they are. If so, we don't need to do anything + * at all. + */ + + if (rom->swap_block_mark) + return 0; + + /* + * If control arrives here, we can't use block mark swapping, which + * means we're forced to use transcription. First, scan for the + * transcription stamp. If we find it, then we don't have to do + * anything -- the block marks are already transcribed. + */ + + if (rom->check_transcription_stamp(this)) + return 0; + + /* + * If control arrives here, we couldn't find a transcription stamp, so + * so we presume the block marks are in the conventional location. + */ + + pr_info("Transcribing bad block marks...\n"); + + /* Compute the number of blocks in the entire medium. */ + + block_count = + physical->chip_size_in_bytes >> nand->phys_erase_shift; + + /* + * Loop over all the blocks in the medium, transcribing block marks as + * we go. + */ + + for (block = 0; block < block_count; block++) { + + /* + * Compute the chip, page and byte addresses for this block's + * conventional mark. + */ + + chip = block >> (nand->chip_shift - nand->phys_erase_shift); + page = block << (nand->phys_erase_shift - nand->page_shift); + byte = block << nand->phys_erase_shift; + + /* Select the chip. */ + + nand->select_chip(mtd, chip); + + /* Send the command to read the conventional block mark. */ + + nand->cmdfunc(mtd, NAND_CMD_READ0, + physical->page_data_size_in_bytes, page); + + /* Read the conventional block mark. */ + + block_mark = nand->read_byte(mtd); + + /* + * Check if the block is marked bad. If so, we need to mark it + * again, but this time the result will be a mark in the + * location where we transcribe block marks. + * + * Notice that we have to explicitly set the marking_a_bad_block + * member before we call through the block_markbad function + * pointer in the owning struct nand_chip. If we could call + * though the block_markbad function pointer in the owning + * struct mtd_info, which we have hooked, then this would be + * taken care of for us. Unfortunately, we can't because that + * higher-level code path will do things like consulting the + * in-memory bad block table -- which doesn't even exist yet! + * So, we have to call at a lower level and handle some details + * ourselves. + */ + + if (block_mark != 0xff) { + pr_info("Transcribing mark in block %u\n", block); + mil->marking_a_bad_block = true; + error = nand->block_markbad(mtd, byte); + mil->marking_a_bad_block = false; + if (error) + dev_err(dev, "Failed to mark block bad with " + "error %d\n", error); + } + + /* Deselect the chip. */ + + nand->select_chip(mtd, -1); + + } + + /* Write the stamp that indicates we've transcribed the block marks. */ + + rom->write_transcription_stamp(this); + + /* Return success. */ + + return 0; + +} + +/** + * 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 gpmi_nfc_data *this = nand->priv; + struct nfc_hal *nfc = this->nfc; + struct mil *mil = &this->mil; + int saved_chip_number; + uint8_t id_bytes[NAND_DEVICE_ID_BYTE_COUNT]; + struct nand_device_info *info; + struct gpmi_nfc_timing timing; + int error; + + DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc scan_bbt] \n"); + + /* + * 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; + + /* + * MTD identified the attached NAND Flash devices, but we have a much + * better database that we want to consult. First, we need to gather all + * the ID bytes from the first chip (MTD only read the first two). + */ + + saved_chip_number = mil->current_chip; + nand->select_chip(mtd, 0); + + nand->cmdfunc(mtd, NAND_CMD_READID, 0, -1); + nand->read_buf(mtd, id_bytes, NAND_DEVICE_ID_BYTE_COUNT); + + nand->select_chip(mtd, saved_chip_number); + + /* Look up this device in our database. */ + + info = nand_device_get_info(id_bytes); + + /* Check if we understand this device. */ + + if (!info) { + pr_err("Unrecognized NAND Flash device.\n"); + return !0; + } + + /* Display the information we discovered. */ + + #if defined(DETAILED_INFO) + pr_info("-----------------------------\n"); + pr_info("NAND Flash Device Information\n"); + pr_info("-----------------------------\n"); + nand_device_print_info(info); + #endif + + /* + * Copy the device info into the per-device data. We can't just keep + * the pointer because that storage is reclaimed after initialization. + */ + + this->device_info = *info; + this->device_info.description = kstrdup(info->description, GFP_KERNEL); + + /* Set up geometry. */ + + error = mil_set_geometry(this); + + if (error) + return error; + + /* Set up timing. */ + + timing.data_setup_in_ns = info->data_setup_in_ns; + timing.data_hold_in_ns = info->data_hold_in_ns; + timing.address_setup_in_ns = info->address_setup_in_ns; + timing.gpmi_sample_delay_in_ns = info->gpmi_sample_delay_in_ns; + timing.tREA_in_ns = info->tREA_in_ns; + timing.tRLOH_in_ns = info->tRLOH_in_ns; + timing.tRHOH_in_ns = info->tRHOH_in_ns; + + error = nfc->set_timing(this, &timing); + + if (error) + return error; + + /* Prepare for the BBT scan. */ + + error = mil_pre_bbt_scan(this); + + if (error) + return error; + + /* We use the reference implementation for bad block management. */ + + error = nand_default_bbt(mtd); + + if (error) + return error; + + /* Return success. */ + + return 0; + +} + +/** + * mil_boot_areas_init() - Initializes boot areas. + * + * @this: Per-device data. + */ +static int mil_boot_areas_init(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct physical_geometry *physical = &this->physical_geometry; + struct boot_rom_geometry *rom = &this->rom_geometry; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + struct nand_chip *nand = &mil->nand; + int mtd_support_is_adequate; + unsigned int i; + struct mtd_partition partitions[4]; + struct mtd_info *search_mtd; + struct mtd_info *chip_0_remainder_mtd = 0; + struct mtd_info *medium_remainder_mtd = 0; + struct mtd_info *concatenate[2]; + + /* + * Here we declare the static strings we use to name partitions. We use + * static strings because, as of 2.6.31, the partitioning code *always* + * registers the partition MTDs it creates and leaves behind *no* other + * trace of its work. So, once we've created a partition, we must search + * the master MTD table to find the MTDs we created. Since we're using + * static strings, we can simply search the master table for an MTD with + * a name field pointing to a known address. + */ + + static char *chip_0_boot_name = "gpmi-nfc-0-boot"; + static char *chip_0_remainder_name = "gpmi-nfc-0-remainder"; + static char *chip_1_boot_name = "gpmi-nfc-1-boot"; + static char *medium_remainder_name = "gpmi-nfc-remainder"; + static char *general_use_name = "gpmi-nfc-general-use"; + + /* Check if we're protecting the boot areas.*/ + + if (!rom->boot_area_count) { + + /* + * If control arrives here, we're not protecting the boot areas. + * In this case, there are not boot area partitons, and the main + * MTD is the general use MTD. + */ + + mil->general_use_mtd = &mil->mtd; + + return 0; + + } + + /* + * If control arrives here, we're protecting the boot areas. Check if we + * have the MTD support we need. + */ + + pr_info("Boot area protection is enabled.\n"); + + if (rom->boot_area_count > 1) { + + /* + * If the Boot ROM wants more than one boot area, then we'll + * need to create partitions *and* concatenate them. + */ + + #if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + mtd_support_is_adequate = true; + #else + mtd_support_is_adequate = false; + #endif + + } else if (rom->boot_area_count == 1) { + + /* + * If the Boot ROM wants only one boot area, then we only need + * to create partitions -- we don't need to concatenate them. + */ + + #if defined(CONFIG_MTD_PARTITIONS) + mtd_support_is_adequate = true; + #else + mtd_support_is_adequate = false; + #endif + + } else { + + /* + * If control arrives here, we're protecting the boot area, but + * somehow the boot area count was set to zero. This doesn't + * make any sense. + */ + + dev_err(dev, "Internal error: boot area count is " + "incorrectly set to zero."); + return -ENXIO; + + } + + if (!mtd_support_is_adequate) { + dev_err(dev, "Configured MTD support is inadequate to " + "protect the boot area(s)."); + return -ENXIO; + } + + /* + * If control arrives here, we're protecting boot areas and we have + * everything we need to do so. + * + * We have special code to handle the case for one boot area. + * + * The code that handles "more than one" boot area actually only handles + * two. We *could* write the general case, but that would take a lot of + * time to both write and test -- and, right now, we don't have a chip + * that cares. + */ + + /* Check if a boot area is larger than a single chip. */ + + if (rom->boot_area_size_in_bytes > physical->chip_size_in_bytes) { + dev_emerg(dev, "Boot area size is larger than a chip"); + return -ENXIO; + } + + if (rom->boot_area_count == 1) { + +#if defined(CONFIG_MTD_PARTITIONS) + + /* + * We partition the medium like so: + * + * +------+----------------------------------------------------+ + * | Boot | General Use | + * +------+----------------------------------------------------+ + */ + + /* Chip 0 Boot */ + + partitions[0].name = chip_0_boot_name; + partitions[0].offset = 0; + partitions[0].size = rom->boot_area_size_in_bytes; + partitions[0].mask_flags = 0; + + /* General Use */ + + partitions[1].name = general_use_name; + partitions[1].offset = rom->boot_area_size_in_bytes; + partitions[1].size = MTDPART_SIZ_FULL; + partitions[1].mask_flags = 0; + + /* Construct and register the partitions. */ + + add_mtd_partitions(mtd, partitions, 2); + + /* Find the general use MTD. */ + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + + /* Get the current MTD so we can examine it. */ + + search_mtd = get_mtd_device(0, i); + + /* Check if we got nonsense. */ + + if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV))) + continue; + + /* Check if the current MTD is one of our remainders. */ + + if (search_mtd->name == general_use_name) + mil->general_use_mtd = search_mtd; + + /* Put the MTD back. We only wanted a quick look. */ + + put_mtd_device(search_mtd); + + } + + if (!mil->general_use_mtd) { + dev_emerg(dev, "Can't find general use MTD"); + BUG(); + } + +#endif + + } else if (rom->boot_area_count == 2) { + +#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + + /* + * If control arrives here, there is more than one boot area. + * We partition the medium and concatenate the remainders like + * so: + * + * --- Chip 0 --- --- Chip 1 --- ... ------- Chip N ------- + * / \ / \ + * +----+----------+----+--------------- ... ------------------+ + * |Boot|Remainder |Boot| Remainder | + * +----+----------+----+--------------- ... ------------------+ + * | | / / + * | | / / + * | | / / + * | |/ / + * +----------+----------- ... ----------------------+ + * | General Use | + * +---------------------- ... ----------------------+ + * + * Notice that the results we leave in the master MTD table + * look like this: + * + * * Chip 0 Boot Area + * * Chip 1 Boot Area + * * General Use + * + * Some user space programs expect the boot partitions to + * appear first. This is naive, but let's try not to cause + * any trouble, where we can avoid it. + */ + + /* Chip 0 Boot */ + + partitions[0].name = chip_0_boot_name; + partitions[0].offset = 0; + partitions[0].size = rom->boot_area_size_in_bytes; + partitions[0].mask_flags = 0; + + /* Chip 1 Boot */ + + partitions[1].name = chip_1_boot_name; + partitions[1].offset = nand->chipsize; + partitions[1].size = rom->boot_area_size_in_bytes; + partitions[1].mask_flags = 0; + + /* Chip 0 Remainder */ + + partitions[2].name = chip_0_remainder_name; + partitions[2].offset = rom->boot_area_size_in_bytes; + partitions[2].size = nand->chipsize - + rom->boot_area_size_in_bytes; + partitions[2].mask_flags = 0; + + /* Medium Remainder */ + + partitions[3].name = medium_remainder_name; + partitions[3].offset = nand->chipsize + + rom->boot_area_size_in_bytes; + partitions[3].size = MTDPART_SIZ_FULL; + partitions[3].mask_flags = 0; + + /* Construct and register the partitions. */ + + add_mtd_partitions(mtd, partitions, 4); + + /* Find the remainder partitions. */ + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + + /* Get the current MTD so we can examine it. */ + + search_mtd = get_mtd_device(0, i); + + /* Check if we got nonsense. */ + + if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV))) + continue; + + /* Check if the current MTD is one of our remainders. */ + + if (search_mtd->name == chip_0_remainder_name) + chip_0_remainder_mtd = search_mtd; + + if (search_mtd->name == medium_remainder_name) + medium_remainder_mtd = search_mtd; + + /* Put the MTD back. We only wanted a quick look. */ + + put_mtd_device(search_mtd); + + } + + if (!chip_0_remainder_mtd || !medium_remainder_mtd) { + dev_emerg(dev, "Can't find remainder partitions"); + BUG(); + } + + /* + * Unregister the remainder MTDs. Note that we are *not* + * destroying these MTDs -- we're just removing from the + * globally-visible list. There's no need for anyone to see + * these. + */ + + del_mtd_device(chip_0_remainder_mtd); + del_mtd_device(medium_remainder_mtd); + + /* Concatenate the remainders and register the result. */ + + concatenate[0] = chip_0_remainder_mtd; + concatenate[1] = medium_remainder_mtd; + + mil->general_use_mtd = mtd_concat_create(concatenate, + 2, general_use_name); + + add_mtd_device(mil->general_use_mtd); + +#endif + + } else { + dev_err(dev, "Boot area count greater than two is " + "unimplemented.\n"); + return -ENXIO; + } + + /* Return success. */ + + return 0; + +} + +/** + * mil_boot_areas_exit() - Shuts down boot areas. + * + * @this: Per-device data. + */ +static void mil_boot_areas_exit(struct gpmi_nfc_data *this) +{ + struct boot_rom_geometry *rom = &this->rom_geometry; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + + /* Check if we're protecting the boot areas.*/ + + if (!rom->boot_area_count) { + + /* + * If control arrives here, we're not protecting the boot areas. + * That means we never created any boot area partitions, and the + * general use MTD is just the main MTD. + */ + + mil->general_use_mtd = 0; + + return; + + } + + /* + * If control arrives here, we're protecting the boot areas. + * + * Start by checking if there is more than one boot area. If so, then + * we both partitioned the medium and then concatenated some of the + * partitions to form the general use MTD. The first step is to get rid + * of the concatenation. + */ + + #if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + if (rom->boot_area_count > 1) { + del_mtd_device(mil->general_use_mtd); + mtd_concat_destroy(mil->general_use_mtd); + } + #endif + + /* + * At this point, we're left only with the partitions of the main MTD. + * Delete them. + */ + + #if defined(CONFIG_MTD_PARTITIONS) + del_mtd_partitions(mtd); + #endif + + /* The general use MTD no longer exists. */ + + mil->general_use_mtd = 0; + +} + +/** + * mil_construct_ubi_partitions() - Constructs partitions for UBI. + * + * MTD uses a 64-bit value to express the size of MTDs, but UBI is still using + * a 32-bit value. For this reason, UBI can't work on top of an MTD with size + * greater than 2GiB. In this function, we examine the general use MTD and, if + * it's larger than 2GiB, we construct a set of partitions for that MTD such + * that none are too large for UBI to comprehend. + * + * @this: Per-device data. + */ +static void mil_construct_ubi_partitions(struct gpmi_nfc_data *this) +{ +#if defined(CONFIG_MTD_PARTITIONS) + struct device *dev = this->dev; + struct mil *mil = &this->mil; + unsigned int partition_count; + struct mtd_partition *partitions; + unsigned int name_size; + char *names; + unsigned int memory_block_size; + unsigned int i; + + static const char *name_prefix = "gpmi-nfc-ubi-"; + + /* + * If the general use MTD isn't larger than 2GiB, we have nothing to do. + */ + + if (mil->general_use_mtd->size <= SZ_2G) + return; + + /* + * If control arrives here, the general use MTD is larger than 2GiB. We + * need to split it up into some number of partitions. Find out how many + * 2GiB partitions we'll be creating. + */ + + partition_count = mil->general_use_mtd->size >> 31; + + /* + * If the MTD size doesn't evenly divide by 2GiB, we'll need another + * partition to hold the extra. + */ + + if (mil->general_use_mtd->size & ((1 << 30) - 1)) + partition_count++; + + /* + * We're going to allocate a single memory block to contain all the + * partition structures and their names. Calculate how large it must be. + */ + + name_size = strlen(name_prefix) + 4; + + memory_block_size = (sizeof(*partitions) + name_size) * partition_count; + + /* + * Attempt to allocate the block. + */ + + partitions = kzalloc(memory_block_size, GFP_KERNEL); + + if (!partitions) { + dev_err(dev, "Could not allocate memory for UBI partitions.\n"); + return; + } + + names = (char *)(partitions + partition_count); + + /* Loop over partitions, filling in the details. */ + + for (i = 0; i < partition_count; i++) { + + partitions[i].name = names; + partitions[i].size = SZ_2G; + partitions[i].offset = MTDPART_OFS_NXTBLK; + + sprintf(names, "%s%u", name_prefix, i); + names += name_size; + + } + + /* Adjust the last partition to take up the remainder. */ + + partitions[i - 1].size = MTDPART_SIZ_FULL; + + /* Record everything in the device data structure. */ + + mil->partitions = partitions; + mil->partition_count = partition_count; + mil->ubi_partition_memory = partitions; + +#endif +} + +/** + * mil_partitions_init() - Initializes partitions. + * + * @this: Per-device data. + */ +static int mil_partitions_init(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + int error; + + /* + * Set up the boot areas. When this function returns, if there has been + * no error, the boot area partitions (if any) will have been created + * and registered. Also, the general_use_mtd field will point to an MTD + * we can use. + */ + + error = mil_boot_areas_init(this); + + if (error) + return error; + + /* + * If we've been told to, register the MTD that represents the entire + * medium. Normally, we don't register the main MTD because we only want + * to expose the medium through the boot area partitions and the general + * use partition. + * + * We do this *after* setting up the boot areas because, for historical + * reasons, we like the lowest-numbered MTDs to be the boot areas. + */ + + if (register_main_mtd) { + pr_info("Registering the main MTD.\n"); + add_mtd_device(mtd); + } + +#if defined(CONFIG_MTD_PARTITIONS) + + /* + * If control arrives here, partitioning is available. + * + * There are three possible sets of partitions we might apply, in order + * of decreasing priority: + * + * 1) Partitions dynamically discovered from sources defined by the + * platform. These can come from, for example, the command line or + * a partition table. + * + * 2) Partitions attached to the platform data. + * + * 3) Partitions we generate to deal with limitations in UBI. + * + * Recall that the pointer to the general use MTD *may* just point to + * the main MTD. + */ + + /* + * First, try to get partition information from the sources defined by + * the platform. + */ + + if (pdata->partition_source_types) + mil->partition_count = + parse_mtd_partitions(mil->general_use_mtd, + pdata->partition_source_types, + &mil->partitions, 0); + + /* + * Check if we got anything. If not, then accept whatever partitions are + * attached to the platform data. + */ + + if ((mil->partition_count <= 0) && (pdata->partitions)) { + mil->partition_count = mil->partition_count; + mil->partitions = mil->partitions; + } + + /* + * If we still don't have any partitions to apply, then we might want to + * apply some of our own, to account for UBI's limitations. + */ + + if (!mil->partition_count) + mil_construct_ubi_partitions(this); + + /* If we came up with any partitions, apply them. */ + + if (mil->partition_count) + add_mtd_partitions(mil->general_use_mtd, + mil->partitions, + mil->partition_count); + +#endif + + /* Return success. */ + + return 0; + +} + +/** + * mil_partitions_exit() - Shuts down partitions. + * + * @this: Per-device data. + */ +static void mil_partitions_exit(struct gpmi_nfc_data *this) +{ + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + + /* Check if we applied any partitions to the general use MTD. */ + + #if defined(CONFIG_MTD_PARTITIONS) + + if (mil->partition_count) + del_mtd_partitions(mil->general_use_mtd); + + kfree(mil->ubi_partition_memory); + + #endif + + /* + * If we were told to register the MTD that represents the entire + * medium, unregister it now. Note that this does *not* "destroy" the + * MTD - it merely unregisters it. That's important because all our + * other MTDs depend on this one. + */ + + if (register_main_mtd) + del_mtd_device(mtd); + + /* Tear down the boot areas. */ + + mil_boot_areas_exit(this); + +} + +/** + * gpmi_nfc_mil_init() - Initializes the MTD Interface Layer. + * + * @this: Per-device data. + */ +int gpmi_nfc_mil_init(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + struct nand_chip *nand = &mil->nand; + static struct nand_ecclayout fake_ecc_layout; + int error = 0; + + /* Initialize MIL data. */ + + mil->current_chip = -1; + mil->command_length = 0; + + mil->page_buffer_virt = 0; + mil->page_buffer_phys = ~0; + mil->page_buffer_size = 0; + + /* Initialize the MTD data structures. */ + + mtd->priv = nand; + mtd->name = "gpmi-nfc-main"; + mtd->owner = THIS_MODULE; + nand->priv = this; + + /* + * Signal Control + */ + + nand->cmd_ctrl = mil_cmd_ctrl; + + /* + * Chip Control + * + * We rely on the reference implementations of: + * - cmdfunc + * - waitfunc + */ + + nand->dev_ready = mil_dev_ready; + nand->select_chip = mil_select_chip; + + /* + * Low-level I/O + * + * We don't support a 16-bit NAND Flash bus, so we don't implement + * read_word. + * + * We rely on the reference implentation of verify_buf. + */ + + nand->read_byte = mil_read_byte; + nand->read_buf = mil_read_buf; + nand->write_buf = mil_write_buf; + + /* + * ECC Control + * + * None of these functions are necessary for us: + * - ecc.hwctl + * - ecc.calculate + * - ecc.correct + */ + + /* + * ECC-aware I/O + * + * We rely on the reference implementations of: + * - ecc.read_page_raw + * - ecc.write_page_raw + */ + + nand->ecc.read_page = mil_ecc_read_page; + nand->ecc.write_page = mil_ecc_write_page; + + /* + * High-level I/O + * + * We rely on the reference implementations of: + * - write_page + * - erase_cmd + */ + + nand->ecc.read_oob = mil_ecc_read_oob; + nand->ecc.write_oob = mil_ecc_write_oob; + + /* + * Bad Block Management + * + * We rely on the reference implementations of: + * - block_bad + * - block_markbad + */ + + nand->block_bad = mil_block_bad; + 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. + */ + + /* + * Set up NAND Flash options. Specifically: + * + * - Disallow partial page writes. + */ + + nand->options |= NAND_NO_SUBPAGE_WRITE; + + /* + * Tell the NAND Flash MTD system that we'll be handling ECC with our + * own hardware. It turns out that we still have to fill in the ECC size + * because the MTD code will divide by it -- even though it doesn't + * actually care. + */ + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 1; + + /* + * 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 one for itself. + * Later, when we know the medium geometry, we'll install the correct + * one. + * + * Of course, this tactic depends critically on the MTD code not doing + * an I/O operation that depends on the ECC layout being sensible. This + * is in fact the case. + */ + + memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); + + nand->ecc.layout = &fake_ecc_layout; + + /* Allocate a command buffer. */ + + mil->cmd_virt = + dma_alloc_coherent(dev, + MIL_COMMAND_BUFFER_SIZE, &mil->cmd_phys, GFP_DMA); + + if (!mil->cmd_virt) + goto exit_cmd_allocation; + + /* + * 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. + */ + + pr_info("Scanning for NAND Flash chips...\n"); + + error = nand_scan(mtd, pdata->max_chip_count); + + if (error) { + dev_err(dev, "Chip scan failed\n"); + goto exit_nand_scan; + } + + /* + * Hook some operations at the MTD level. See the descriptions of the + * saved function pointer fields for details about why we hook these. + */ + + mil->hooked_read_oob = mtd->read_oob; + mtd->read_oob = mil_hook_read_oob; + + mil->hooked_write_oob = mtd->write_oob; + mtd->write_oob = mil_hook_write_oob; + + mil->hooked_block_markbad = mtd->block_markbad; + mtd->block_markbad = mil_hook_block_markbad; + + /* Construct partitions as necessary. */ + + error = mil_partitions_init(this); + + if (error) + goto exit_partitions; + + /* Return success. */ + + return 0; + + /* Control arrives here if something went wrong. */ + +exit_partitions: + nand_release(&mil->mtd); +exit_nand_scan: + dma_free_coherent(dev, MIL_COMMAND_BUFFER_SIZE, + mil->cmd_virt, mil->cmd_phys); + mil->cmd_virt = 0; + mil->cmd_phys = ~0; +exit_cmd_allocation: + + return error; + +} + +/** + * gpmi_nfc_mil_exit() - Shuts down the MTD Interface Layer. + * + * @this: Per-device data. + */ +void gpmi_nfc_mil_exit(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct mil *mil = &this->mil; + + /* Shut down partitions as necessary. */ + + mil_partitions_exit(this); + + /* Get MTD to let go of our MTD. */ + + nand_release(&mil->mtd); + + /* Free the page buffer, if it's been allocated. */ + + if (mil->page_buffer_virt) + dma_free_coherent(dev, mil->page_buffer_size, + mil->page_buffer_virt, mil->page_buffer_phys); + + mil->page_buffer_size = 0; + mil->page_buffer_virt = 0; + mil->page_buffer_phys = ~0; + + /* Free the command buffer, if it's been allocated. */ + + if (mil->cmd_virt) + dma_free_coherent(dev, MIL_COMMAND_BUFFER_SIZE, + mil->cmd_virt, mil->cmd_phys); + + mil->cmd_virt = 0; + mil->cmd_phys = ~0; + +} diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c new file mode 100644 index 000000000000..0cd0b39141fd --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c @@ -0,0 +1,59 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +/** + * gpmi_nfc_rom_helper_set_geometry() - Sets geometry for the Boot ROM Helper. + * + * @this: Per-device data. + */ +int gpmi_nfc_rom_helper_set_geometry(struct gpmi_nfc_data *this) +{ + struct boot_rom_geometry *geometry = &this->rom_geometry; + + /* + * Set the boot block stride size. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + + geometry->stride_size_in_pages = 64; + + /* + * Set the search area stride exponent. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + + geometry->search_area_stride_exponent = 2; + + /* Return success. */ + + return 0; + +} diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c new file mode 100644 index 000000000000..35321cc25546 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c @@ -0,0 +1,297 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +/* + * Useful variables for Boot ROM Helper version 0. + */ + +static const char *fingerprint = "STMP"; + +/** + * set_geometry() - Sets geometry for the Boot ROM Helper. + * + * @this: Per-device data. + */ +static int set_geometry(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct physical_geometry *physical = &this->physical_geometry; + struct boot_rom_geometry *geometry = &this->rom_geometry; + int error; + + /* Version-independent geometry. */ + + error = gpmi_nfc_rom_helper_set_geometry(this); + + if (error) + return error; + + /* + * Check if the platform data indicates we are to protect the boot area. + */ + + if (!pdata->boot_area_size_in_bytes) { + geometry->boot_area_count = 0; + geometry->boot_area_size_in_bytes = 0; + return 0; + } + + /* + * If control arrives here, we are supposed to set up partitions to + * protect the boot areas. In this version of the ROM, the number of + * boot areas and their size depends on the number of chips. + */ + + if (physical->chip_count == 1) { + geometry->boot_area_count = 1; + geometry->boot_area_size_in_bytes = + pdata->boot_area_size_in_bytes * 2; + } else { + geometry->boot_area_count = 2; + geometry->boot_area_size_in_bytes = + pdata->boot_area_size_in_bytes; + } + + /* Return success. */ + + return 0; + +} + +/** + * check_transcription_stamp() - Checks for a transcription stamp. + * + * Returns 0 if a stamp is not found. + * + * @this: Per-device data. + */ +static int check_transcription_stamp(struct gpmi_nfc_data *this) +{ + struct physical_geometry *physical = &this->physical_geometry; + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + struct nand_chip *nand = &mil->nand; + unsigned int search_area_size_in_strides; + unsigned int stride; + unsigned int page; + loff_t byte; + uint8_t *buffer = nand->buffers->databuf; + int saved_chip_number; + int found_an_ncb_fingerprint = false; + + /* Compute the number of strides in a search area. */ + + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + + /* Select chip 0. */ + + saved_chip_number = mil->current_chip; + nand->select_chip(mtd, 0); + + /* + * Loop through the first search area, looking for the NCB fingerprint. + */ + + pr_info("Scanning for an NCB fingerprint...\n"); + + for (stride = 0; stride < search_area_size_in_strides; stride++) { + + /* Compute the page and byte addresses. */ + + page = stride * rom_geo->stride_size_in_pages; + byte = page * physical->page_data_size_in_bytes; + + pr_info(" Looking for a fingerprint in page 0x%x\n", page); + + /* + * Read the NCB fingerprint. The fingerprint is four bytes long + * and starts in the 12th byte of the page. + */ + + nand->cmdfunc(mtd, NAND_CMD_READ0, 12, page); + nand->read_buf(mtd, buffer, strlen(fingerprint)); + + /* Look for the fingerprint. */ + + if (!memcmp(buffer, fingerprint, + strlen(fingerprint))) { + found_an_ncb_fingerprint = true; + break; + } + + } + + /* Deselect chip 0. */ + + nand->select_chip(mtd, saved_chip_number); + + /* Return. */ + + if (found_an_ncb_fingerprint) + pr_info(" Found a fingerprint\n"); + else + pr_info(" No fingerprint found\n"); + + return found_an_ncb_fingerprint; + +} + +/** + * write_transcription_stamp() - Writes a transcription stamp. + * + * @this: Per-device data. + */ +static int write_transcription_stamp(struct gpmi_nfc_data *this) +{ + struct device *dev = this->dev; + struct physical_geometry *physical = &this->physical_geometry; + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct mil *mil = &this->mil; + struct mtd_info *mtd = &mil->mtd; + struct nand_chip *nand = &mil->nand; + unsigned int block_size_in_pages; + unsigned int search_area_size_in_strides; + unsigned int search_area_size_in_pages; + unsigned int search_area_size_in_blocks; + unsigned int block; + unsigned int stride; + unsigned int page; + loff_t byte; + uint8_t *buffer = nand->buffers->databuf; + int saved_chip_number; + int status; + + /* Compute the search area geometry. */ + + block_size_in_pages = physical->block_size_in_bytes >> + (ffs(physical->page_data_size_in_bytes) - 1); + + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + + search_area_size_in_pages = search_area_size_in_strides * + rom_geo->stride_size_in_pages; + + search_area_size_in_blocks = + (search_area_size_in_pages + (block_size_in_pages - 1)) / + /*-------------------------------------------------------*/ + block_size_in_pages; + + #if defined(DETAILED_INFO) + + pr_info("--------------------\n"); + pr_info("Search Area Geometry\n"); + pr_info("--------------------\n"); + pr_info("Search Area Size in Blocks : %u", search_area_size_in_blocks); + pr_info("Search Area Size in Strides: %u", search_area_size_in_strides); + pr_info("Search Area Size in Pages : %u", search_area_size_in_pages); + + #endif + + /* Select chip 0. */ + + saved_chip_number = mil->current_chip; + nand->select_chip(mtd, 0); + + /* Loop over blocks in the first search area, erasing them. */ + + pr_info("Erasing the search area...\n"); + + for (block = 0; block < search_area_size_in_blocks; block++) { + + /* Compute the page address. */ + + page = block * block_size_in_pages; + + /* Erase this block. */ + + pr_info(" Erasing block 0x%x\n", block); + + nand->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + nand->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + /* Wait for the erase to finish. */ + + status = nand->waitfunc(mtd, nand); + + if (status & NAND_STATUS_FAIL) + dev_err(dev, "[%s] Erase failed.\n", __func__); + + } + + /* Write the NCB fingerprint into the page buffer. */ + + memset(buffer, ~0, mtd->writesize); + memset(nand->oob_poi, ~0, mtd->oobsize); + + memcpy(buffer + 12, fingerprint, strlen(fingerprint)); + + /* Loop through the first search area, writing NCB fingerprints. */ + + pr_info("Writing NCB fingerprints...\n"); + + for (stride = 0; stride < search_area_size_in_strides; stride++) { + + /* Compute the page and byte addresses. */ + + page = stride * rom_geo->stride_size_in_pages; + byte = page * physical->page_data_size_in_bytes; + + /* Write the first page of the current stride. */ + + pr_info(" Writing an NCB fingerprint in page 0x%x\n", page); + + nand->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + nand->ecc.write_page_raw(mtd, nand, buffer); + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* Wait for the write to finish. */ + + status = nand->waitfunc(mtd, nand); + + if (status & NAND_STATUS_FAIL) + dev_err(dev, "[%s] Write failed.\n", __func__); + + } + + /* Deselect chip 0. */ + + nand->select_chip(mtd, saved_chip_number); + + /* Return success. */ + + return 0; + +} + +/* This structure represents the Boot ROM Helper for this version. */ + +struct boot_rom_helper gpmi_nfc_boot_rom_helper_v0 = { + .version = 0, + .description = "Single/dual-chip boot area, " + "no block mark swapping", + .swap_block_mark = false, + .set_geometry = set_geometry, + .check_transcription_stamp = check_transcription_stamp, + .write_transcription_stamp = write_transcription_stamp, +}; diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c new file mode 100644 index 000000000000..49cb329ccdd4 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c @@ -0,0 +1,82 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gpmi-nfc.h" + +/** + * set_geometry() - Sets geometry for the Boot ROM Helper. + * + * @this: Per-device data. + */ +static int set_geometry(struct gpmi_nfc_data *this) +{ + struct gpmi_nfc_platform_data *pdata = this->pdata; + struct boot_rom_geometry *geometry = &this->rom_geometry; + int error; + + /* Version-independent geometry. */ + + error = gpmi_nfc_rom_helper_set_geometry(this); + + if (error) + return error; + + /* + * Check if the platform data indicates we are to protect the boot area. + */ + + if (!pdata->boot_area_size_in_bytes) { + geometry->boot_area_count = 0; + geometry->boot_area_size_in_bytes = 0; + return 0; + } + + /* + * If control arrives here, we are supposed to set up partitions to + * protect the boot areas. In this version of the ROM, we support only + * one boot area. + */ + + geometry->boot_area_count = 1; + + /* + * Use the platform's boot area size. + */ + + geometry->boot_area_size_in_bytes = pdata->boot_area_size_in_bytes; + + /* Return success. */ + + return 0; + +} + +/* This structure represents the Boot ROM Helper for this version. */ + +struct boot_rom_helper gpmi_nfc_boot_rom_helper_v1 = { + .version = 1, + .description = "Single-chip boot area, " + "block mark swapping supported", + .swap_block_mark = true, + .set_geometry = set_geometry, + .check_transcription_stamp = 0, + .write_transcription_stamp = 0, +}; diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h new file mode 100644 index 000000000000..6f14b73dd93d --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h @@ -0,0 +1,643 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __DRIVERS_MTD_NAND_GPMI_NFC_H +#define __DRIVERS_MTD_NAND_GPMI_NFC_H + +/* Linux header files. */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> +#include <linux/gpmi-nfc.h> +#include <asm/sizes.h> + +/* Platform header files. */ + +#include <mach/system.h> +#include <mach/dmaengine.h> +#include <mach/device.h> +#include <mach/clock.h> + +/* Driver header files. */ + +#include "../nand_device_info.h" + +/* + *------------------------------------------------------------------------------ + * Fundamental Macros + *------------------------------------------------------------------------------ + */ + +/* Define this macro to enable detailed information messages. */ + +#define DETAILED_INFO + +/* Define this macro to enable event reporting. */ + +/*#define EVENT_REPORTING*/ + +/* + *------------------------------------------------------------------------------ + * Fundamental Data Structures + *------------------------------------------------------------------------------ + */ + +/** + * struct resources - The collection of resources the driver needs. + * + * @gpmi_regs: A pointer to the GPMI registers. + * @bch_regs: A pointer to the BCH registers. + * @bch_interrupt: The BCH interrupt number. + * @dma_low_channel: The low DMA channel. + * @dma_high_channel: The high DMA channel. + * @dma_interrupt: The DMA interrupt number. + * @clock: A pointer to the struct clk for the NFC's clock. + */ + +struct resources { + void *gpmi_regs; + void *bch_regs; + unsigned int bch_interrupt; + unsigned int dma_low_channel; + unsigned int dma_high_channel; + unsigned int dma_interrupt; + struct clk *clock; +}; + +/** + * struct mil - State for the MTD Interface Layer. + * + * @nand: The NAND Flash MTD data structure that represents + * the NAND Flash medium. + * @mtd: The MTD data structure that represents the NAND + * Flash medium. + * @oob_layout: A structure that describes how bytes are laid out + * in the OOB. + * @general_use_mtd: A pointer to an MTD we export for general use. + * This *may* simply be a pointer to the mtd field, if + * we've been instructed NOT to protect the boot + * areas. + * @partitions: A pointer to a set of partitions applied to the + * general use MTD. + * @partition_count: The number of partitions. + * @ubi_partition_memory: If not NULL, a block of memory used to create a set + * of partitions that help with the problem that UBI + * can't handle an MTD larger than 2GiB. + * @current_chip: The chip currently selected by the NAND Fash MTD + * code. A negative value indicates that no chip is + * selected. + * @command_length: The length of the command that appears in the + * command buffer (see cmd_virt, below). + * @inject_ecc_error: Indicates the driver should inject a "fake" ECC + * error into 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 by the maximum possible number of + * errors that could appear in a single read. + * @ignore_bad_block_marks: Indicates we are ignoring bad block marks. + * @saved_bbt: A saved pointer to the in-memory NAND Flash MTD bad + * block table. See show_device_ignorebad() for more + * details. + * @raw_oob_mode: Indicates the OOB is to be read/written in "raw" + * mode. See mil_ecc_read_oob() for details. + * @hooked_read_oob: A pointer to the ecc.read_oob() function we + * "hooked." See mil_ecc_read_oob() for details. + * @hooked_write_oob: A pointer to the ecc.write_oob() function pointer + * we "hooked." See mil_ecc_read_oob() for details. + * @marking_a_bad_block: Indicates the caller is marking a bad block. See + * mil_ecc_write_oob() for details. + * @hooked_block_markbad: A pointer to the block_markbad() function we + * we "hooked." See mil_ecc_write_oob() for details. + * @cmd_virt: A pointer to a DMA-coherent buffer in which we + * accumulate command bytes before we give them to the + * NFC layer. See mil_cmd_ctrl() for more details. + * @cmd_phys: The physical address for the cmd_virt buffer. + * @page_buffer_virt: A pointer to a DMA-coherent buffer we use for + * reading and writing pages. This buffer includes + * space for both the payload data and the auxiliary + * data (including status bytes, but not syndrome + * bytes). + * @page_buffer_phys: The physical address for the page_buffer_virt + * buffer. + * @page_buffer_size: The size of the page buffer. + * @payload_virt: A pointer to a location in the page buffer used + * for payload bytes. The size of this buffer is + * determined by struct nfc_geometry. + * @payload_phys: The physical address for payload_virt. + * @payload_size: The size of the payload area in the page buffer. + * @auxiliary_virt: A pointer to a location in the page buffer used + * for auxiliary bytes. The size of this buffer is + * determined by struct nfc_geometry. + * @auxiliary_phys: The physical address for auxiliary_virt. + * @auxiliary_size: The size of the auxiliary area in the page buffer. + */ + +#define MIL_COMMAND_BUFFER_SIZE (10) + +struct mil { + + /* MTD Data Structures */ + + struct nand_chip nand; + struct mtd_info mtd; + struct nand_ecclayout oob_layout; + + /* Partitioning and Boot Area Protection */ + + struct mtd_info *general_use_mtd; + struct mtd_partition *partitions; + unsigned int partition_count; + void *ubi_partition_memory; + + /* General-use Variables */ + + int current_chip; + unsigned int command_length; + int inject_ecc_error; + int ignore_bad_block_marks; + void *saved_bbt; + + /* MTD Function Pointer Hooks */ + + int raw_oob_mode; + int (*hooked_read_oob)(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops); + int (*hooked_write_oob)(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops); + + int marking_a_bad_block; + int (*hooked_block_markbad)(struct mtd_info *mtd, + loff_t ofs); + + /* DMA Buffers */ + + char *cmd_virt; + dma_addr_t cmd_phys; + + void *page_buffer_virt; + dma_addr_t page_buffer_phys; + unsigned int page_buffer_size; + + void *payload_virt; + dma_addr_t payload_phys; + + void *auxiliary_virt; + dma_addr_t auxiliary_phys; + +}; + +/** + * struct 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_in_bytes: The size, in bytes, of a single chip + * (excluding the out-of-band bytes). + * @block_size_in_bytes: The size, in bytes, of a single block + * (excluding the out-of-band bytes). + * @page_data_size_in_bytes: The size, in bytes, of the data area in a + * page (excluding the out-of-band bytes). + * @page_oob_size_in_bytes: The size, in bytes, of the out-of-band area + * in a page. + */ + +struct physical_geometry { + unsigned int chip_count; + uint64_t chip_size_in_bytes; + unsigned int block_size_in_bytes; + unsigned int page_data_size_in_bytes; + unsigned int page_oob_size_in_bytes; +}; + +/** + * struct nfc_geometry - NFC geometry description. + * + * This structure describes the NFC's view of the medium geometry. + * + * @ecc_algorithm: The human-readable name of the ECC algorithm + * (e.g., "Reed-Solomon" or "BCH"). + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @page_size_in_bytes: The size, in bytes, of a physical page, including + * both data and OOB. + * @metadata_size_in_bytes: The size, in bytes, of the metadata. + * @ecc_chunk_size_in_bytes: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @payload_size_in_bytes: The size, in bytes, of the payload buffer. + * @auxiliary_size_in_bytes: The size, in bytes, of the auxiliary buffer. + * @auxiliary_status_offset: The offset into the auxiliary buffer at which + * the ECC status appears. + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ + +struct nfc_geometry { + char *ecc_algorithm; + unsigned int ecc_strength; + unsigned int page_size_in_bytes; + unsigned int metadata_size_in_bytes; + unsigned int ecc_chunk_size_in_bytes; + unsigned int ecc_chunk_count; + unsigned int payload_size_in_bytes; + unsigned int auxiliary_size_in_bytes; + unsigned int auxiliary_status_offset; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + +/** + * struct boot_rom_geometry - Boot ROM geometry description. + * + * This structure encapsulates decisions made by the Boot ROM Helper. + * + * @boot_area_count: The number of boot areas. The first boot area + * appears at the beginning of chip 0, the next + * at the beginning of chip 1, etc. + * @boot_area_size_in_bytes: The size, in bytes, of each boot area. + * @stride_size_in_pages: The size of a boot block stride, in pages. + * @search_area_stride_exponent: The logarithm to base 2 of the size of a + * search area in boot block strides. + */ + +struct boot_rom_geometry { + unsigned int boot_area_count; + unsigned int boot_area_size_in_bytes; + unsigned int stride_size_in_pages; + unsigned int search_area_stride_exponent; +}; + +/** + * struct gpmi_nfc_data - i.MX NFC per-device data. + * + * Note that the "device" managed by this driver represents the NAND Flash + * controller *and* the NAND Flash medium behind it. Thus, the per-device data + * structure has information about the controller, the chips to which it is + * connected, and properties of the medium as a whole. + * + * @dev: A pointer to the owning struct device. + * @pdev: A pointer to the owning struct platform_device. + * @pdata: A pointer to the device's platform data. + * @resources: Information about system resources used by this driver. + * @device_info: A structure that contains detailed information about + * the NAND Flash device. + * @physical_geometry: A description of the medium's physical geometry. + * @nfc: A pointer to a structure that represents the underlying + * NFC hardware. + * @nfc_geometry: A description of the medium geometry as viewed by the + * NFC. + * @rom: A pointer to a structure that represents the underlying + * Boot ROM. + * @rom_geometry: A description of the medium geometry as viewed by the + * Boot ROM. + * @mil: A collection of information used by the MTD Interface + * Layer. + */ + +struct gpmi_nfc_data { + + /* System Interface */ + struct device *dev; + struct platform_device *pdev; + struct gpmi_nfc_platform_data *pdata; + + /* Resources */ + struct resources resources; + + /* Flash Hardware */ + struct nand_device_info device_info; + struct physical_geometry physical_geometry; + + /* NFC HAL */ + struct nfc_hal *nfc; + struct nfc_geometry nfc_geometry; + + /* Boot ROM Helper */ + struct boot_rom_helper *rom; + struct boot_rom_geometry rom_geometry; + + /* MTD Interface Layer */ + struct mil mil; + +}; + +/** + * struct gpmi_nfc_timing - GPMI NFC timing parameters. + * + * This structure contains the fundamental timing attributes for the NAND Flash + * bus and the GPMI NFC hardware. + * + * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the + * maximum of tDS and tWP. A negative value + * indicates this characteristic isn't known. + * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the + * maximum of tDH, tWH and tREH. A negative value + * indicates this characteristic isn't known. + * @address_setup_in_ns: The address setup time, in nanoseconds. Usually + * the maximum of tCLS, tCS and tALS. A negative + * value indicates this characteristic isn't known. + * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value + * indicates this characteristic isn't known. + * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + */ + +struct gpmi_nfc_timing { + int8_t data_setup_in_ns; + int8_t data_hold_in_ns; + int8_t address_setup_in_ns; + int8_t gpmi_sample_delay_in_ns; + int8_t tREA_in_ns; + int8_t tRLOH_in_ns; + int8_t tRHOH_in_ns; +}; + +/** + * struct gpmi_nfc_hardware_timing - GPMI NFC hardware timing parameters. + * + * This structure contains timing information expressed in a form directly + * usable by the GPMI NFC hardware. + * + * @data_setup_in_cycles: The data setup time, in cycles. + * @data_hold_in_cycles: The data hold time, in cycles. + * @address_setup_in_cycles: The address setup time, in cycles. + * @use_half_periods: Indicates the clock is running slowly, so the + * NFC DLL should use half-periods. + * @sample_delay_factor: The sample delay factor. + */ + +struct gpmi_nfc_hardware_timing { + uint8_t data_setup_in_cycles; + uint8_t data_hold_in_cycles; + uint8_t address_setup_in_cycles; + bool use_half_periods; + uint8_t sample_delay_factor; +}; + +/** + * struct nfc_hal - GPMI NFC HAL + * + * This structure embodies an abstract interface to the underlying NFC hardware. + * + * @version: The NFC hardware version. + * @description: A pointer to a human-readable description of + * the NFC hardware. + * @max_chip_count: The maximum number of chips the NFC can + * possibly support (this value is a constant for + * each NFC version). This may *not* be the actual + * number of chips connected. + * @max_data_setup_cycles: The maximum number of data setup cycles that + * can be expressed in the hardware. + * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires + * for data read internal setup. In the Reference + * Manual, see the chapter "High-Speed NAND + * Timing" for more details. + * @max_sample_delay_factor: The maximum sample delay factor that can be + * expressed in the hardware. + * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the + * sample delay DLL hardware can possibly work + * with (the DLL is unusable with longer periods). + * If the full-cycle period is greater than HALF + * this value, the DLL must be configured to use + * half-periods. + * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the + * DLL can implement. + * @dma_descriptors: A pool of DMA descriptors. + * @isr_dma_channel: The DMA channel with which the NFC HAL is + * working. We record this here so the ISR knows + * which DMA channel to acknowledge. + * @dma_done: The completion structure used for DMA + * interrupts. + * @bch_done: The completion structure used for BCH + * interrupts. + * @timing: The current timing configuration. + * @clock_frequency_in_hz: The clock frequency, in Hz, during the current + * I/O transaction. If no I/O transaction is in + * progress, this is the clock frequency during + * the most recent I/O transaction. + * @hardware_timing: The hardware timing configuration in effect + * during the current I/O transaction. If no I/O + * transaction is in progress, this is the + * hardware timing configuration during the most + * recent I/O transaction. + * @init: Initializes the NFC hardware and data + * structures. This function will be called after + * everything has been set up for communication + * with the NFC itself, but before the platform + * has set up off-chip communication. Thus, this + * function must not attempt to communicate with + * the NAND Flash hardware. + * @set_geometry: Configures the NFC hardware and data structures + * to match the physical NAND Flash geometry. + * @set_geometry: Configures the NFC hardware and data structures + * to match the physical NAND Flash geometry. + * @set_timing: Configures the NFC hardware and data structures + * to match the given NAND Flash bus timing. + * @get_timing: Returns the the clock frequency, in Hz, and + * the hardware timing configuration during the + * current I/O transaction. If no I/O transaction + * is in progress, this is the timing state during + * the most recent I/O transaction. + * @exit: Shuts down the NFC hardware and data + * structures. This function will be called after + * the platform has shut down off-chip + * communication but while communication with the + * NFC itself still works. + * @clear_bch: Clears a BCH interrupt (intended to be called + * by a more general interrupt handler to do + * device-specific clearing). + * @is_ready: Returns true if the given chip is ready. + * @begin: Begins an interaction with the NFC. This + * function must be called before *any* of the + * following functions so the NFC can prepare + * itself. + * @end: Ends interaction with the NFC. This function + * should be called to give the NFC a chance to, + * among other things, enter a lower-power state. + * @send_command: Sends the given buffer of command bytes. + * @send_data: Sends the given buffer of data bytes. + * @read_data: Reads data bytes into the given buffer. + * @send_page: Sends the given given data and OOB bytes, + * using the ECC engine. + * @read_page: Reads a page through the ECC engine and + * delivers the data and OOB bytes to the given + * buffers. + */ + +#define NFC_DMA_DESCRIPTOR_COUNT (4) + +struct nfc_hal { + + /* Hardware attributes. */ + + const unsigned int version; + const char *description; + const unsigned int max_chip_count; + const unsigned int max_data_setup_cycles; + const unsigned int internal_data_setup_in_ns; + const unsigned int max_sample_delay_factor; + const unsigned int max_dll_clock_period_in_ns; + const unsigned int max_dll_delay_in_ns; + + /* Working variables. */ + + struct mxs_dma_desc *dma_descriptors[NFC_DMA_DESCRIPTOR_COUNT]; + int isr_dma_channel; + struct completion dma_done; + struct completion bch_done; + struct gpmi_nfc_timing timing; + unsigned long clock_frequency_in_hz; + + /* Configuration functions. */ + + int (*init) (struct gpmi_nfc_data *); + int (*set_geometry)(struct gpmi_nfc_data *); + int (*set_timing) (struct gpmi_nfc_data *, + const struct gpmi_nfc_timing *); + void (*get_timing) (struct gpmi_nfc_data *, + unsigned long *clock_frequency_in_hz, + struct gpmi_nfc_hardware_timing *); + void (*exit) (struct gpmi_nfc_data *); + + /* Call these functions to begin and end I/O. */ + + void (*begin) (struct gpmi_nfc_data *); + void (*end) (struct gpmi_nfc_data *); + + /* Call these I/O functions only between begin() and end(). */ + + void (*clear_bch) (struct gpmi_nfc_data *); + int (*is_ready) (struct gpmi_nfc_data *, unsigned chip); + int (*send_command)(struct gpmi_nfc_data *, unsigned chip, + dma_addr_t buffer, unsigned length); + int (*send_data) (struct gpmi_nfc_data *, unsigned chip, + dma_addr_t buffer, unsigned length); + int (*read_data) (struct gpmi_nfc_data *, unsigned chip, + dma_addr_t buffer, unsigned length); + int (*send_page) (struct gpmi_nfc_data *, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary); + int (*read_page) (struct gpmi_nfc_data *, unsigned chip, + dma_addr_t payload, dma_addr_t auxiliary); +}; + +/** + * struct boot_rom_helper - Boot ROM Helper + * + * This structure embodies the interface to an object that assists the driver + * in making decisions that relate to the Boot ROM. + * + * @version: The Boot ROM version. + * @description: A pointer to a human-readable description of the + * Boot ROM. + * @swap_block_mark: Indicates that the Boot ROM will swap the block + * mark with the first byte of the OOB. + * @set_geometry: Configures the Boot ROM geometry. + * @check_transcription_stamp: Checks for a transcription stamp. This pointer + * is ignored if swap_block_mark is set. + * @write_transcription_stamp: Writes a transcription stamp. This pointer + * is ignored if swap_block_mark is set. + */ + +struct boot_rom_helper { + const unsigned int version; + const char *description; + const int swap_block_mark; + int (*set_geometry) (struct gpmi_nfc_data *); + int (*check_transcription_stamp)(struct gpmi_nfc_data *); + int (*write_transcription_stamp)(struct gpmi_nfc_data *); +}; + +/* + *------------------------------------------------------------------------------ + * External Symbols + *------------------------------------------------------------------------------ + */ + +/* Event Reporting */ + +#if defined(EVENT_REPORTING) + extern void gpmi_nfc_start_event_trace(char *description); + extern void gpmi_nfc_add_event(char *description, int delta); + extern void gpmi_nfc_stop_event_trace(char *description); + extern void gpmi_nfc_dump_event_trace(void); +#else + #define gpmi_nfc_start_event_trace(description) do {} while (0) + #define gpmi_nfc_add_event(description, delta) do {} while (0) + #define gpmi_nfc_stop_event_trace(description) do {} while (0) + #define gpmi_nfc_dump_event_trace() do {} while (0) +#endif + +/* NFC HAL Common Services */ + +extern irqreturn_t gpmi_nfc_bch_isr(int irq, void *cookie); +extern irqreturn_t gpmi_nfc_dma_isr(int irq, void *cookie); +extern int gpmi_nfc_dma_init(struct gpmi_nfc_data *this); +extern void gpmi_nfc_dma_exit(struct gpmi_nfc_data *this); +extern int gpmi_nfc_set_geometry(struct gpmi_nfc_data *this); +extern int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel); +extern int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this, + struct gpmi_nfc_hardware_timing *hw); + +/* NFC HAL Structures */ + +extern struct nfc_hal gpmi_nfc_hal_v0; +extern struct nfc_hal gpmi_nfc_hal_v1; + +/* Boot ROM Helper Common Services */ + +extern int gpmi_nfc_rom_helper_set_geometry(struct gpmi_nfc_data *this); + +/* Boot ROM Helper Structures */ + +extern struct boot_rom_helper gpmi_nfc_boot_rom_helper_v0; +extern struct boot_rom_helper gpmi_nfc_boot_rom_helper_v1; + +/* MTD Interface Layer */ + +extern int gpmi_nfc_mil_init(struct gpmi_nfc_data *this); +extern void gpmi_nfc_mil_exit(struct gpmi_nfc_data *this); + +#endif diff --git a/drivers/mtd/nand/imx_nfc.c b/drivers/mtd/nand/imx_nfc.c new file mode 100644 index 000000000000..065ff338b06d --- /dev/null +++ b/drivers/mtd/nand/imx_nfc.c @@ -0,0 +1,8286 @@ +/* + * Copyright 2009-2010 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. + */ + +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. + */ + +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. + */ + +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. + */ + +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. + */ + +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. + */ + +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"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 88785032322a..66f38d598e13 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Sascha Hauer, kernel@pengutronix.de * * This program is free software; you can redistribute it and/or @@ -30,6 +30,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/fsl_devices.h> #include <asm/mach/flash.h> #include <mach/mxc_nand.h> diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c new file mode 100644 index 000000000000..7344bafa77fa --- /dev/null +++ b/drivers/mtd/nand/mxc_nd.c @@ -0,0 +1,1413 @@ +/* + * Copyright 2004-2010 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/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/mach/flash.h> + +#include "mxc_nd.h" + +#define DVR_VER "2.1" + +struct mxc_mtd_s { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; +}; + +static struct mxc_mtd_s *mxc_nand_data; + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 +/* + * Macros to get half word and bit positions of ECC + */ +#define COLPOS(x) ((x) >> 4) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +struct nand_info { + bool bSpareOnly; + bool bStatusRequest; + u16 colAddr; +}; + +static struct nand_info g_nandfc_info; + +#ifdef CONFIG_MTD_NAND_MXC_SWECC +static int hardware_ecc; +#else +static int hardware_ecc = 1; +#endif + +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 +static int Ecc_disabled; +#endif + +static int is2k_Pagesize; + +static struct clk *nfc_clk; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_8 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 5}, {11, 5} } +}; + +static struct nand_ecclayout nand_hw_eccoob_16 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 6}, {12, 4} } +}; + +static struct nand_ecclayout nand_hw_eccoob_2k = { + .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = { + {.offset = 0, + .length = 5}, + + {.offset = 11, + .length = 10}, + + {.offset = 27, + .length = 10}, + + {.offset = 43, + .length = 10}, + + {.offset = 59, + .length = 5} + } +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif + +static wait_queue_head_t irq_waitq; + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + NFC_CONFIG1 |= NFC_INT_MSK; /* Disable interrupt */ + wake_up(&irq_waitq); + + return IRQ_RETVAL(1); +} + +/*! + * This function polls the NANDFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param param parameter for debug + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int maxRetries, u16 param, bool useirq) +{ + if (useirq) { + if ((NFC_CONFIG2 & NFC_INT) == 0) { + NFC_CONFIG1 &= ~NFC_INT_MSK; /* Enable interrupt */ + wait_event(irq_waitq, NFC_CONFIG2 & NFC_INT); + } + NFC_CONFIG2 &= ~NFC_INT; + } else { + while (maxRetries-- > 0) { + if (NFC_CONFIG2 & NFC_INT) { + NFC_CONFIG2 &= ~NFC_INT; + break; + } + udelay(1); + } + if (maxRetries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __FUNCTION__, param); + } +} + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(u16 cmd, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq); + + NFC_FLASH_CMD = (u16) cmd; + NFC_CONFIG2 = NFC_CMD; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, cmd, useirq); +} + +/*! + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param islast True if this is the last address cycle for command + */ +static void send_addr(u16 addr, bool islast) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast); + + NFC_FLASH_ADDR = addr; + NFC_CONFIG2 = NFC_ADDR; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, addr, islast); +} + +/*! + * This function requests the NANDFC to initate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param bSpareOnly set true if only the spare area is transferred + */ +static void send_prog_page(u8 buf_id, bool bSpareOnly) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", bSpareOnly); + + /* NANDFC buffer 0 is used for page read/write */ + + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_Pagesize) { + if (bSpareOnly) { + NFC_CONFIG1 |= NFC_SP_EN; + } else { + NFC_CONFIG1 &= ~(NFC_SP_EN); + } + } + NFC_CONFIG2 = NFC_INPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, bSpareOnly, true); +} + +/*! + * This function will correct the single bit ECC error + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param eccpos Ecc byte and bit position + * @param bSpareOnly set to true if only spare area needs correction + */ + +static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, bool bSpareOnly) +{ + u16 col; + u8 pos; + volatile u16 *buf; + + /* Get col & bit position of error + these macros works for both 8 & 16 bits */ + col = COLPOS(eccpos); /* Get half-word position */ + pos = BITPOS(eccpos); /* Get bit position */ + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos); + + /* Set the pointer for main / spare area */ + if (!bSpareOnly) { + buf = (volatile u16 *)(MAIN_AREA0 + col + (256 * buf_id)); + } else { + buf = (volatile u16 *)(SPARE_AREA0 + col + (8 * buf_id)); + } + + /* Fix the data */ + *buf ^= (1 << pos); +} + +/*! + * This function will maintains state of single bit Error + * in Main & spare area + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param spare set to true if only spare area needs correction + */ +static void mxc_nd_correct_ecc(u8 buf_id, bool spare) +{ +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + static int lastErrMain, lastErrSpare; /* To maintain single bit + error in previous page */ +#endif + u16 value, ecc_status; + /* Read the ECC result */ + ecc_status = NFC_ECC_STATUS_RESULT; + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status); + +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + /* Check for Error in Mainarea */ + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (lastErrMain && !spare) { + value = NFC_RSLTMAIN_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, false); + } else { + /* Set if single bit error in current page */ + lastErrMain = 1; + } + } else { + /* Reset if no single bit error in current page */ + lastErrMain = 0; + } + + /* Check for Error in Sparearea */ + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (lastErrSpare) { + value = NFC_RSLTSPARE_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, true); + } else { + /* Set if single bit error in current page */ + lastErrSpare = 1; + } + } else { + /* Reset if no single bit error in current page */ + lastErrSpare = 0; + } +#else + if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) + || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) { + if (Ecc_disabled) { + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + value = NFC_RSLTMAIN_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, false); + } + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + value = NFC_RSLTSPARE_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, true); + } + + } else { + /* Disable ECC */ + NFC_CONFIG1 &= ~(NFC_ECC_EN); + Ecc_disabled = 1; + } + } else if (ecc_status == 0) { + if (Ecc_disabled) { + /* Enable ECC */ + NFC_CONFIG1 |= NFC_ECC_EN; + Ecc_disabled = 0; + } + } else { + /* 2-bit Error Do nothing */ + } +#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */ + +} + +/*! + * This function requests the NANDFC to initated the transfer + * of data from the NAND device into in the NANDFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param bSpareOnly set true if only the spare area is transferred + */ +static void send_read_page(u8 buf_id, bool bSpareOnly) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", bSpareOnly); + + /* NANDFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_Pagesize) { + if (bSpareOnly) { + NFC_CONFIG1 |= NFC_SP_EN; + } else { + NFC_CONFIG1 &= ~(NFC_SP_EN); + } + } + + NFC_CONFIG2 = NFC_OUTPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, bSpareOnly, true); + + /* If there are single bit errors in + two consecutive page reads then + the error is not corrected by the + NFC for the second page. + Correct single bit error in driver */ + + mxc_nd_correct_ecc(buf_id, bSpareOnly); +} + +/*! + * This function requests the NANDFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + struct nand_chip *this = &mxc_nand_data->nand; + + /* NANDFC buffer 0 is used for device ID output */ + NFC_BUF_ADDR = 0x0; + + /* Read ID into main buffer */ + NFC_CONFIG1 &= (~(NFC_SP_EN)); + NFC_CONFIG2 = NFC_ID; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, 0, true); + + if (this->options & NAND_BUSWIDTH_16) { + volatile u16 *mainBuf = MAIN_AREA0; + + /* + * Pack the every-other-byte result for 16-bit ID reads + * into every-byte as the generic code expects and various + * chips implement. + */ + + mainBuf[0] = (mainBuf[0] & 0xff) | ((mainBuf[1] & 0xff) << 8); + mainBuf[1] = (mainBuf[2] & 0xff) | ((mainBuf[3] & 0xff) << 8); + mainBuf[2] = (mainBuf[4] & 0xff) | ((mainBuf[5] & 0xff) << 8); + } +} + +/*! + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(void) +{ + volatile u16 *mainBuf = MAIN_AREA1; + u32 store; + u16 ret; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = *((u32 *) mainBuf); + /* + * NANDFC buffer 1 is used for device status to prevent + * corruption of read/write buffer on status requests. + */ + NFC_BUF_ADDR = 1; + + /* Read status into main buffer */ + NFC_CONFIG1 &= (~(NFC_SP_EN)); + NFC_CONFIG2 = NFC_STATUS; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, 0, true); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = mainBuf[0]; + *((u32 *) mainBuf) = store; + + return ret; +} + +/*! + * This functions is used by upper layer to checks if device is ready + * + * @param mtd MTD structure for the NAND Flash + * + * @return 0 if device is busy else 1 + */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally.Therefore,this function + * always returns status as ready. + */ + return 1; +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + u16 ecc_status = NFC_ECC_STATUS_RESULT; + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + /* + * Just return success. HW ECC does not read/write the NFC spare + * buffer. Only the FLASH spare area contains the calcuated ECC. + */ + return 0; +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + u_char retVal = 0; + u16 col, rdWord; + volatile u16 *mainBuf = MAIN_AREA0; + volatile u16 *spareBuf = SPARE_AREA0; + + /* Check for status request */ + if (g_nandfc_info.bStatusRequest) { + return get_dev_status() & 0xFF; + } + + /* Get column for 16-bit access */ + col = g_nandfc_info.colAddr >> 1; + + /* If we are accessing the spare region */ + if (g_nandfc_info.bSpareOnly) { + rdWord = spareBuf[col]; + } else { + rdWord = mainBuf[col]; + } + + /* Pick upper/lower byte of word from RAM buffer */ + if (g_nandfc_info.colAddr & 0x1) { + retVal = (rdWord >> 8) & 0xFF; + } else { + retVal = rdWord & 0xFF; + } + + /* Update saved column address */ + g_nandfc_info.colAddr++; + + return retVal; +} + +/*! + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 col; + u16 rdWord, retVal; + volatile u16 *p; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", g_nandfc_info.colAddr); + + col = g_nandfc_info.colAddr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + if (col < mtd->writesize) + p = (MAIN_AREA0) + (col >> 1); + else + p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1); + + if (col & 1) { + rdWord = *p; + retVal = (rdWord >> 8) & 0xff; + rdWord = *(p + 1); + retVal |= (rdWord << 8) & 0xff00; + + } else { + retVal = *p; + + } + + /* Update saved column address */ + g_nandfc_info.colAddr = col + 2; + + return retVal; +} + +/*! + * This function writes data of length \b len to buffer \b buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr, + len); + + col = g_nandfc_info.colAddr; + + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n); + + while (n) { + volatile u32 *p; + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (col & ~3)); + + DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __FUNCTION__, + __LINE__, p); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data = 0; + + if (col & 3 || n < 4) + data = *p; + + switch (col & 3) { + case 0: + if (n) { + data = (data & 0xffffff00) | + (buf[i++] << 0); + n--; + col++; + } + case 1: + if (n) { + data = (data & 0xffff00ff) | + (buf[i++] << 8); + n--; + col++; + } + case 2: + if (n) { + data = (data & 0xff00ffff) | + (buf[i++] << 16); + n--; + col++; + } + case 3: + if (n) { + data = (data & 0x00ffffff) | + (buf[i++] << 24); + n--; + col++; + } + } + + *p = data; + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __FUNCTION__, __LINE__, n, m, i, col); + + memcpy((void *)(p), &buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + g_nandfc_info.colAddr = col; + +} + +/*! + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr, + len); + + col = g_nandfc_info.colAddr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n) { + volatile u32 *p; + + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (col & ~3)); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data; + + data = *p; + switch (col & 3) { + case 0: + if (n) { + buf[i++] = (u8) (data); + n--; + col++; + } + case 1: + if (n) { + buf[i++] = (u8) (data >> 8); + n--; + col++; + } + case 2: + if (n) { + buf[i++] = (u8) (data >> 16); + n--; + col++; + } + case 3: + if (n) { + buf[i++] = (u8) (data >> 24); + n--; + col++; + } + } + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + memcpy(&buf[i], (void *)(p), m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + g_nandfc_info.colAddr = col; + +} + +/*! + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int +mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) +{ + return -EFAULT; +} + +/*! + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE + if (chip > 0) { + DEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", chip); + return; + } + + if (chip == -1) { + NFC_CONFIG1 &= (~(NFC_CE)); + return; + } + + NFC_CONFIG1 |= NFC_CE; +#endif + + switch (chip) { + case -1: + /* Disable the NFC clock */ + clk_disable(nfc_clk); + break; + case 0: + /* Enable the NFC clock */ + clk_enable(nfc_clk); + break; + + default: + break; + } +} + +/*! + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + bool useirq = true; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* + * Reset command state information + */ + g_nandfc_info.bStatusRequest = false; + + /* Reset column address to 0 */ + g_nandfc_info.colAddr = 0; + + /* + * Command pre-processing step + */ + switch (command) { + + case NAND_CMD_STATUS: + g_nandfc_info.bStatusRequest = true; + break; + + case NAND_CMD_READ0: + g_nandfc_info.colAddr = column; + g_nandfc_info.bSpareOnly = false; + useirq = false; + break; + + case NAND_CMD_READOOB: + g_nandfc_info.colAddr = column; + g_nandfc_info.bSpareOnly = true; + useirq = false; + if (is2k_Pagesize) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + if (is2k_Pagesize) { + /** + * FIXME: before send SEQIN command for write OOB, + * We must read one page out. + * For K9F1GXX has no READ1 command to set current HW + * pointer to spare area, we must write the whole page including OOB together. + */ + /* call itself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + g_nandfc_info.colAddr = column - mtd->writesize; + g_nandfc_info.bSpareOnly = true; + /* Set program pointer to spare region */ + if (!is2k_Pagesize) + send_cmd(NAND_CMD_READOOB, false); + } else { + g_nandfc_info.bSpareOnly = false; + g_nandfc_info.colAddr = column; + /* Set program pointer to page start */ + if (!is2k_Pagesize) + send_cmd(NAND_CMD_READ0, false); + } + useirq = false; + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (Ecc_disabled) { + /* Enable Ecc for page writes */ + NFC_CONFIG1 |= NFC_ECC_EN; + } +#endif + + send_prog_page(0, g_nandfc_info.bSpareOnly); + + if (is2k_Pagesize) { + /* data in 4 areas datas */ + send_prog_page(1, g_nandfc_info.bSpareOnly); + send_prog_page(2, g_nandfc_info.bSpareOnly); + send_prog_page(3, g_nandfc_info.bSpareOnly); + } + + break; + + case NAND_CMD_ERASE1: + useirq = false; + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(command, useirq); + + /* + * Write out column address, if necessary + */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(0, page_addr == -1); + if (is2k_Pagesize) + /* another col addr cycle for 2k page */ + send_addr(0, false); + } + + /* + * Write out page address, if necessary + */ + if (page_addr != -1) { + send_addr((page_addr & 0xff), false); /* paddr_0 - p_addr_7 */ + + if (is2k_Pagesize) { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, false); + send_addr((page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, true); + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, false); + send_addr((page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, true); + } + } + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (is2k_Pagesize) { + /* send read confirm command */ + send_cmd(NAND_CMD_READSTART, true); + /* read for each AREA */ + send_read_page(0, g_nandfc_info.bSpareOnly); + send_read_page(1, g_nandfc_info.bSpareOnly); + send_read_page(2, g_nandfc_info.bSpareOnly); + send_read_page(3, g_nandfc_info.bSpareOnly); + } else { + send_read_page(0, g_nandfc_info.bSpareOnly); + } + break; + + case NAND_CMD_READID: + send_read_id(); + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (Ecc_disabled) { + /* Disble Ecc after page writes */ + NFC_CONFIG1 &= ~(NFC_ECC_EN); + } +#endif + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .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_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .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 = mirror_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Config before scanning */ + /* Do not rely on NFMS_BIT, set/clear NFMS bit based on mtd->writesize */ + if (mtd->writesize == 2048) { + NFMS |= (1 << NFMS_BIT); + is2k_Pagesize = 1; + } else { + if ((NFMS >> NFMS_BIT) & 0x1) { /* This case strangly happened on MXC91321 P1.2.2 */ + printk(KERN_INFO + "Oops... NFMS Bit set for 512B Page, resetting it. [RCSR: 0x%08x]\n", + NFMS); + NFMS &= ~(1 << NFMS_BIT); + } + is2k_Pagesize = 0; + } + + if (is2k_Pagesize) + this->ecc.layout = &nand_hw_eccoob_2k; + + /* jffs2 not write oob */ + mtd->flags &= ~MTD_OOB_WRITEABLE; + + /* use flash based bbt */ + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + + if (!this->badblock_pattern) { + if (mtd->writesize == 2048) + this->badblock_pattern = &smallpage_memorybased; + else + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE +static void mxc_low_erase(struct mtd_info *mtd) +{ + + struct nand_chip *this = mtd->priv; + unsigned int page_addr, addr; + u_char status; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n"); + for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) { + page_addr = addr / mtd->writesize; + mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr); + mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1); + mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + status = mxc_nand_read_byte(mtd); + if (status & NAND_STATUS_FAIL) { + printk(KERN_ERR + "ERASE FAILED(block = %d,status = 0x%x)\n", + addr / mtd->erasesize, status); + } + } + +} +#endif +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +static int __init mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct flash_platform_data *flash = pdev->dev.platform_data; + int nr_parts = 0; + + int err = 0; + /* Allocate memory for MTD device structure and private data */ + mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL); + if (!mxc_nand_data) { + printk(KERN_ERR "%s: failed to allocate mtd_info\n", + __FUNCTION__); + err = -ENOMEM; + goto out; + } + memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info)); + + mxc_nand_data->dev = &pdev->dev; + /* structures must be linked */ + this = &mxc_nand_data->nand; + mtd = &mxc_nand_data->mtd; + mtd->priv = this; + mtd->owner = THIS_MODULE; + + /* 50 us command delay time */ + this->chip_delay = 5; + + this->priv = mxc_nand_data; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + + nfc_clk = clk_get(&pdev->dev, "nfc_clk"); + clk_enable(nfc_clk); + + NFC_CONFIG1 |= NFC_INT_MSK; + init_waitqueue_head(&irq_waitq); + err = request_irq(MXC_INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL); + if (err) { + goto out_1; + } + + if (hardware_ecc) { + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + NFC_CONFIG1 |= NFC_ECC_EN; + } else { + this->ecc.mode = NAND_ECC_SOFT; + } + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + NFC_CONFIG = 0x2; + + /* Blocks to be unlocked */ + NFC_UNLOCKSTART_BLKADDR = 0x0; + NFC_UNLOCKEND_BLKADDR = 0x4000; + + /* Unlock Block Command for given address range */ + NFC_WRPROT = 0x4; + + /* NAND bus width determines access funtions used by upper layer */ + if (flash->width == 2) { + this->options |= NAND_BUSWIDTH_16; + this->ecc.layout = &nand_hw_eccoob_16; + } else { + this->options |= 0; + } + + is2k_Pagesize = 0; + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND: Unable to find any NAND device.\n"); + err = -ENXIO; + goto out_1; + } + + /* Register the partitions */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts); + else if (flash->parts) + add_mtd_partitions(mtd, flash->parts, flash->nr_parts); + else +#endif + { + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); + } +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE + /* Erase all the blocks of a NAND */ + mxc_low_erase(mtd); +#endif + + platform_set_drvdata(pdev, mtd); + return 0; + + out_1: + kfree(mxc_nand_data); + out: + return err; + +} + + /*! + * Dissociates the driver from the device. + * + * @param pdev the device structure used to give information on which + * + * @return The function always returns 0. + */ + +static int __exit mxcnd_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + clk_put(nfc_clk); + platform_set_drvdata(pdev, NULL); + + if (mxc_nand_data) { + nand_release(mtd); + free_irq(MXC_INT_NANDFC, NULL); + kfree(mxc_nand_data); + } + + return 0; +} + +#ifdef CONFIG_PM +/*! + * This function is called to put the NAND in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure + */ + +static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); + if (info) + ret = info->suspend(info); + + /* Disable the NFC clock */ + clk_disable(nfc_clk); + + return ret; +} + +/*! + * This function is called to bring the NAND back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxcnd_resume(struct platform_device *pdev) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); + /* Enable the NFC clock */ + clk_enable(nfc_clk); + + if (info) { + info->resume(info); + } + + return ret; +} + +#else +#define mxcnd_suspend NULL +#define mxcnd_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxcnd_driver = { + .driver = { + .name = "mxc_nand_flash", + }, + .probe = mxcnd_probe, + .remove = __exit_p(mxcnd_remove), + .suspend = mxcnd_suspend, + .resume = mxcnd_resume, +}; + +/*! + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init mxc_nd_init(void) +{ + /* Register the device driver structure. */ + pr_info("MXC MTD nand Driver %s\n", DVR_VER); + if (platform_driver_register(&mxcnd_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); + return -ENODEV; + } + return 0; +} + +/*! + * Clean up routine + */ +static void __exit mxc_nd_cleanup(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&mxcnd_driver); +} + +module_init(mxc_nd_init); +module_exit(mxc_nd_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mxc_nd.h b/drivers/mtd/nand/mxc_nd.h new file mode 100644 index 000000000000..a2a257c3897c --- /dev/null +++ b/drivers/mtd/nand/mxc_nd.h @@ -0,0 +1,112 @@ +/* + * Copyright 2004-2010 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 + */ + +/*! + * @file mxc_nd.h + * + * @brief This file contains the NAND Flash Controller register information. + * + * + * @ingroup NAND_MTD + */ + +#ifndef __MXC_ND_H__ +#define __MXC_ND_H__ + +#include <mach/hardware.h> + +/* + * Addresses for NFC registers + */ +#define NFC_BUF_SIZE (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE00))) +#define NFC_BUF_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE04))) +#define NFC_FLASH_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE06))) +#define NFC_FLASH_CMD (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE08))) +#define NFC_CONFIG (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0A))) +#define NFC_ECC_STATUS_RESULT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0C))) +#define NFC_RSLTMAIN_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0E))) +#define NFC_RSLTSPARE_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE10))) +#define NFC_WRPROT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE12))) +#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE14))) +#define NFC_UNLOCKEND_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE16))) +#define NFC_NF_WRPRST (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE18))) +#define NFC_CONFIG1 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1A))) +#define NFC_CONFIG2 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1C))) + +/*! + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x000) +#define MAIN_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x200) +#define MAIN_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x400) +#define MAIN_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x600) + +/*! + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#define SPARE_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x800) +#define SPARE_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x810) +#define SPARE_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x820) +#define SPARE_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x830) + +/*! + * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command + * operation + */ +#define NFC_CMD 0x1 + +/*! + * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address + * operation + */ +#define NFC_ADDR 0x2 + +/*! + * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input + * operation + */ +#define NFC_INPUT 0x4 + +/*! + * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data Output + * operation + */ +#define NFC_OUTPUT 0x8 + +/*! + * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID + * operation + */ +#define NFC_ID 0x10 + +/*! + * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_STATUS 0x20 + +/*! + * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_INT 0x8000 + +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + +#endif /* MXCND_H */ diff --git a/drivers/mtd/nand/mxc_nd2.c b/drivers/mtd/nand/mxc_nd2.c new file mode 100644 index 000000000000..2f7bebff6a61 --- /dev/null +++ b/drivers/mtd/nand/mxc_nd2.c @@ -0,0 +1,1628 @@ +/* + * Copyright 2004-2010 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/io.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 <asm/mach/flash.h> +#include "mxc_nd2.h" +#include "nand_device_info.h" + +#define DVR_VER "3.0" + +/* Global address Variables */ +static void __iomem *nfc_axi_base, *nfc_ip_base; +static int nfc_irq; + +struct mxc_mtd_s { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; + int disable_bi_swap; /* disable bi swap */ +}; + +static struct mxc_mtd_s *mxc_nand_data; + +/* + * Define delay timeout value + */ +#define TROP_US_DELAY (1000 * 1000) + +struct nand_info { + bool bStatusRequest; + u16 colAddr; +}; + +static struct nand_info g_nandfc_info; + +#ifdef CONFIG_MTD_NAND_MXC_SWECC +static int hardware_ecc; +#else +static int hardware_ecc = 1; +#endif + +static u8 num_of_interleave = 1; + +static u8 *data_buf; +static u8 *oob_buf; + +static int g_page_mask; + +static struct clk *nfc_clk; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_512 = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{0, 4} } +}; + +static struct nand_ecclayout nand_hw_eccoob_2k = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{2, 4} } +}; + +static struct nand_ecclayout nand_hw_eccoob_4k = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{2, 4} } +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd2.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif + +static wait_queue_head_t irq_waitq; + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + /* Disable Interuupt */ + raw_write(raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK, REG_NFC_INTRRUPT); + wake_up(&irq_waitq); + + return IRQ_HANDLED; +} + +static void mxc_nand_bi_swap(struct mtd_info *mtd) +{ + u16 ma, sa, nma, nsa; + + if (!IS_LARGE_PAGE_NAND) + return; + + /* Disable bi swap if the user set disable_bi_swap at sys entry */ + if (mxc_nand_data->disable_bi_swap) + return; + + ma = __raw_readw(BAD_BLK_MARKER_MAIN); + sa = __raw_readw(BAD_BLK_MARKER_SP); + + nma = (ma & 0xFF00) | (sa >> 8); + nsa = (sa & 0x00FF) | (ma << 8); + + __raw_writew(nma, BAD_BLK_MARKER_MAIN); + __raw_writew(nsa, BAD_BLK_MARKER_SP); +} + +static void nfc_memcpy(void *dest, void *src, int len) +{ + u8 *d = dest; + u8 *s = src; + + while (len > 0) { + if (len >= 4) { + *(u32 *)d = *(u32 *)s; + d += 4; + s += 4; + len -= 4; + } else { + *(u16 *)d = *(u16 *)s; + len -= 2; + break; + } + } + + if (len) + BUG(); +} + +/* + * Functions to transfer data to/from spare erea. + */ +static void +copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, bool bfrom) +{ + u16 i, j; + u16 m = mtd->oobsize; + u16 n = mtd->writesize >> 9; + u8 *d = (u8 *) pbuf; + u8 *s = (u8 *) pspare; + u16 t = SPARE_LEN; + + m /= num_of_interleave; + n /= num_of_interleave; + + j = (m / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&d[i * j], &s[i * t], j); + + /* the last section */ + nfc_memcpy(&d[i * j], &s[i * t], len - i * j); + } else { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&s[i * t], &d[i * j], j); + + /* the last section */ + nfc_memcpy(&s[i * t], &d[i * j], len - i * j); + } +} + +/*! + * This function polls the NFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int maxRetries, bool useirq) +{ + if (useirq) { + if ((raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) == 0) { + /* enable interrupt */ + raw_write(raw_read(REG_NFC_INTRRUPT) & ~NFC_INT_MSK, + REG_NFC_INTRRUPT); + if (!wait_event_timeout(irq_waitq, + (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT), + msecs_to_jiffies(TROP_US_DELAY / 1000)) > 0) { + /* disable interrupt */ + raw_write(raw_read(REG_NFC_INTRRUPT) + | NFC_INT_MSK, REG_NFC_INTRRUPT); + + printk(KERN_WARNING "%s(%d): INT not set\n", + __func__, __LINE__); + return; + } + } + WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) & + ~NFC_OPS_STAT), REG_NFC_OPS_STAT); + } else { + while (1) { + maxRetries--; + if (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) { + WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) & + ~NFC_OPS_STAT), + REG_NFC_OPS_STAT); + break; + } + udelay(1); + if (maxRetries <= 0) { + printk(KERN_WARNING "%s(%d): INT not set\n", + __func__, __LINE__); + break; + } + } + } +} + +static inline void send_atomic_cmd(u16 cmd, bool useirq) +{ + /* fill command */ + raw_write(cmd, REG_NFC_FLASH_CMD); + + /* send out command */ + raw_write(NFC_CMD, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, useirq); +} + +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr); +static int mxc_check_ecc_status(struct mtd_info *mtd); + +#ifdef NFC_AUTO_MODE_ENABLE +/*! + * This function handle the interleave related work + * @param mtd mtd info + * @param cmd command + */ +static void auto_cmd_interleave(struct mtd_info *mtd, u16 cmd) +{ + u32 i, page_addr, ncs; + u32 j = num_of_interleave; + struct nand_chip *this = mtd->priv; + u32 addr_low = raw_read(NFC_FLASH_ADDR0); + u32 addr_high = raw_read(NFC_FLASH_ADDR8); + u8 *dbuf = data_buf; + u8 *obuf = oob_buf; + u32 dlen = mtd->writesize / j; + u32 olen = mtd->oobsize / j; + + /* adjust the addr value + * since ADD_OP mode is 01 + */ + if (cmd == NAND_CMD_ERASE2) + page_addr = addr_low; + else + page_addr = addr_low >> 16 | addr_high << 16; + + ncs = page_addr >> (this->chip_shift - this->page_shift); + + if (j > 1) { + page_addr *= j; + } else { + page_addr *= this->numchips; + page_addr += ncs; + } + + switch (cmd) { + case NAND_CMD_PAGEPROG: + for (i = 0; i < j; i++) { + /* reset addr cycle */ + mxc_do_addr_cycle(mtd, 0, page_addr++); + + /* data transfer */ + memcpy(MAIN_AREA0, dbuf, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, false); + mxc_nand_bi_swap(mtd); + + /* update the value */ + dbuf += dlen; + obuf += olen; + + NFC_SET_RBA(0); + raw_write(NFC_AUTO_PROG, REG_NFC_OPS); + + /* wait auto_prog_done bit set */ + while (!(raw_read(REG_NFC_OPS_STAT) & NFC_OP_DONE)) + ; + } + + wait_op_done(TROP_US_DELAY, true); + while (!(raw_read(REG_NFC_OPS_STAT) & NFC_RB)); + + break; + case NAND_CMD_READSTART: + for (i = 0; i < j; i++) { + /* reset addr cycle */ + mxc_do_addr_cycle(mtd, 0, page_addr++); + + NFC_SET_RBA(0); + raw_write(NFC_AUTO_READ, REG_NFC_OPS); + wait_op_done(TROP_US_DELAY, true); + + /* check ecc error */ + mxc_check_ecc_status(mtd); + + /* data transfer */ + mxc_nand_bi_swap(mtd); + memcpy(dbuf, MAIN_AREA0, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, true); + + /* update the value */ + dbuf += dlen; + obuf += olen; + } + break; + case NAND_CMD_ERASE2: + for (i = 0; i < j; i++) { + mxc_do_addr_cycle(mtd, -1, page_addr++); + raw_write(NFC_AUTO_ERASE, REG_NFC_OPS); + wait_op_done(TROP_US_DELAY, true); + } + break; + case NAND_CMD_RESET: + for (i = 0; i < j; i++) { + if (j > 1) + NFC_SET_NFC_ACTIVE_CS(i); + send_atomic_cmd(cmd, false); + } + break; + default: + break; + } +} +#endif + +static void send_addr(u16 addr, bool useirq); + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(struct mtd_info *mtd, u16 cmd, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq); + +#ifdef NFC_AUTO_MODE_ENABLE + switch (cmd) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_SEQIN: + case NAND_CMD_ERASE1: + raw_write(cmd, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE2: + case NAND_CMD_READSTART: + raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT, + REG_NFC_FLASH_CMD); + auto_cmd_interleave(mtd, cmd); + break; + case NAND_CMD_READID: + send_atomic_cmd(cmd, useirq); + send_addr(0, false); + break; + case NAND_CMD_RESET: + auto_cmd_interleave(mtd, cmd); + break; + case NAND_CMD_STATUS: + send_atomic_cmd(cmd, useirq); + break; + default: + break; + } +#else + send_atomic_cmd(cmd, useirq); +#endif +} + +/*! + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param useirq True if IRQ should be used rather than polling + */ +static void send_addr(u16 addr, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, useirq); + + /* fill address */ + raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR); + + /* send out address */ + raw_write(NFC_ADDR, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, useirq); +} + +/*! + * This function requests the NFC to initate the transfer + * of data currently in the NFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_prog_page(u8 buf_id) +{ +#ifndef NFC_AUTO_MODE_ENABLE + DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__); + + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* transfer data from NFC ram to nand */ + raw_write(NFC_INPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); +#endif +} + +/*! + * This function requests the NFC to initated the transfer + * of data from the NAND device into in the NFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_read_page(u8 buf_id) +{ +#ifndef NFC_AUTO_MODE_ENABLE + DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __FUNCTION__, buf_id); + + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* transfer data from nand to NFC ram */ + raw_write(NFC_OUTPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); +#endif +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + /* Set RBA bits for BUFFER0 */ + NFC_SET_RBA(0); + + /* Read ID into main buffer */ + raw_write(NFC_ID, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, false); + +} + +#ifdef NFC_AUTO_MODE_ENABLE +static inline void read_dev_status(u16 *status) +{ + u32 mask = 0xFF << 16; + + /* use atomic mode to read status instead + of using auto mode,auto-mode has issues + and the status is not correct. + */ + raw_write(NFC_STATUS, REG_NFC_OPS); + + wait_op_done(TROP_US_DELAY, true); + + *status = (raw_read(NFC_CONFIG1) & mask) >> 16; + +} +#endif + +/*! + * This function requests the NFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(void) +{ +#ifdef NFC_AUTO_MODE_ENABLE + int i; + u16 status = 0; + for (i = 0; i < num_of_interleave; i++) { + + /* set ative cs */ + NFC_SET_NFC_ACTIVE_CS(i); + + /* FIXME, NFC Auto erase may have + * problem, have to pollingit until + * the nand get idle, otherwise + * it may get error + */ + read_dev_status(&status); + if (status & NAND_STATUS_FAIL) + break; + } + + return status; +#else + volatile u16 *mainBuf = MAIN_AREA1; + u8 val = 1; + u16 ret; + + /* Set ram buffer id */ + NFC_SET_RBA(val); + + /* Read status into main buffer */ + raw_write(NFC_STATUS, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = *mainBuf; + + return ret; +#endif +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN); + return; +} + +/* + * Function to record the ECC corrected/uncorrected errors resulted + * after a page read. This NFC detects and corrects upto to 4 symbols + * of 9-bits each. + */ +static int mxc_check_ecc_status(struct mtd_info *mtd) +{ + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask = 0xf; + + no_subpages = mtd->writesize >> 9; + + no_subpages /= num_of_interleave; + + ecc_stat = GET_NFC_ECC_STATUS(); + do { + err = ecc_stat & ecc_bit_mask; + if (err == ecc_bit_mask) { + mtd->ecc_stats.failed++; + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + pr_debug("Correctable ECC Error(%d)\n", ret); + + return ret; +} + +/* + * Function to correct the detected errors. This NFC corrects all the errors + * detected. So this function just return 0. + */ +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + return 0; +} + +/* + * Function to calculate the ECC for the data to be stored in the Nand device. + * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC + * CONTROL blocks are responsible for detection and correction of up to + * 8 symbols of 9 bits each in 528 byte page. + * So this function is just return 0. + */ + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/*! + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + u16 col = g_nandfc_info.colAddr; + + if (mtd->writesize) { + + int j = mtd->writesize - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(buf, &data_buf[col], j); + memcpy(buf + j, &oob_buf[0], n - j); + } else { + memcpy(buf, &data_buf[col], n); + } + } else { + col -= mtd->writesize; + memcpy(buf, &oob_buf[col], len); + } + + /* update */ + g_nandfc_info.colAddr += n; + + } else { + /* At flash identify phase, + * mtd->writesize has not been + * set correctly, it should + * be zero.And len will less 2 + */ + memcpy(buf, &data_buf[col], len); + + /* update */ + g_nandfc_info.colAddr += len; + } + +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static uint8_t mxc_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t ret; + + /* Check for status request */ + if (g_nandfc_info.bStatusRequest) { + return get_dev_status() & 0xFF; + } + + mxc_nand_read_buf(mtd, &ret, 1); + + return ret; +} + +/*! + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 ret; + + mxc_nand_read_buf(mtd, (uint8_t *) &ret, sizeof(u16)); + + return ret; +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte16(struct mtd_info *mtd) +{ + /* Check for status request */ + if (g_nandfc_info.bStatusRequest) { + return get_dev_status() & 0xFF; + } + + return mxc_nand_read_word(mtd) & 0xFF; +} + +/*! + * This function writes data of length \b len from buffer \b buf to the NAND + * internal RAM buffer's MAIN area 0. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u16 col = g_nandfc_info.colAddr; + int j = mtd->writesize - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(&data_buf[col], buf, j); + memcpy(&oob_buf[0], buf + j, n - j); + } else { + memcpy(&data_buf[col], buf, n); + } + } else { + col -= mtd->writesize; + memcpy(&oob_buf[col], buf, len); + } + + /* update */ + g_nandfc_info.colAddr += n; +} + +/*! + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + u_char *s = data_buf; + + const u_char *p = buf; + + for (; len > 0; len--) { + if (*p++ != *s++) + return -EFAULT; + } + + return 0; +} + +/*! + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + + switch (chip) { + case -1: + /* Disable the NFC clock */ + clk_disable(nfc_clk); + break; + case 0 ... 7: + /* Enable the NFC clock */ + clk_enable(nfc_clk); + + NFC_SET_NFC_ACTIVE_CS(chip); + break; + + default: + break; + } +} + +/* + * Function to perform the address cycles. + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ +#ifdef NFC_AUTO_MODE_ENABLE + + if (page_addr != -1 && column != -1) { + u32 mask = 0xFFFF; + /* the column address */ + raw_write(column & mask, NFC_FLASH_ADDR0); + raw_write((raw_read(NFC_FLASH_ADDR0) | + ((page_addr & mask) << 16)), NFC_FLASH_ADDR0); + /* the row address */ + raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) | + ((page_addr & (mask << 16)) >> 16)), + NFC_FLASH_ADDR8); + } else if (page_addr != -1) { + raw_write(page_addr, NFC_FLASH_ADDR0); + raw_write(0, NFC_FLASH_ADDR8); + } + + DEBUG(MTD_DEBUG_LEVEL3, + "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n", + raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8)); +#else + + u32 page_mask = g_page_mask; + + if (column != -1) { + send_addr(column & 0xFF, true); + if (IS_2K_PAGE_NAND) { + /* another col addr cycle for 2k page */ + send_addr((column >> 8) & 0xF, true); + } else if (IS_4K_PAGE_NAND) { + /* another col addr cycle for 4k page */ + send_addr((column >> 8) & 0x1F, true); + } + } + if (page_addr != -1) { + do { + send_addr((page_addr & 0xff), true); + page_mask >>= 8; + page_addr >>= 8; + } while (page_mask != 0); + } +#endif +} + +/*! + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + bool useirq = true; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + /* + * Reset command state information + */ + g_nandfc_info.bStatusRequest = false; + + /* + * Command pre-processing step + */ + switch (command) { + case NAND_CMD_STATUS: + g_nandfc_info.colAddr = 0; + g_nandfc_info.bStatusRequest = true; + break; + + case NAND_CMD_READ0: + g_nandfc_info.colAddr = column; + break; + + case NAND_CMD_READOOB: + g_nandfc_info.colAddr = column; + command = NAND_CMD_READ0; + break; + + case NAND_CMD_SEQIN: + if (column != 0) { + + /* FIXME: before send SEQIN command for + * partial write,We need read one page out. + * FSL NFC does not support partial write + * It alway send out 512+ecc+512+ecc ... + * for large page nand flash. But for small + * page nand flash, it did support SPARE + * ONLY operation. But to make driver + * simple. We take the same as large page,read + * whole page out and update. As for MLC nand + * NOP(num of operation) = 1. Partial written + * on one programed page is not allowed! We + * can't limit it on the driver, it need the + * upper layer applicaiton take care it + */ + + mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); + } + + g_nandfc_info.colAddr = column; + column = 0; + + break; + + case NAND_CMD_PAGEPROG: +#ifndef NFC_AUTO_MODE_ENABLE + /* FIXME:the NFC interal buffer + * access has some limitation, it + * does not allow byte access. To + * make the code simple and ease use + * not every time check the address + * alignment.Use the temp buffer + * to accomadate the data.since We + * know data_buf will be at leat 4 + * byte alignment, so we can use + * memcpy safely + */ + nfc_memcpy(MAIN_AREA0, data_buf, mtd->writesize); + copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, false); + mxc_nand_bi_swap(mtd); +#endif + + if (IS_LARGE_PAGE_NAND) + PROG_PAGE(); + else + send_prog_page(0); + + break; + + case NAND_CMD_ERASE1: + break; + case NAND_CMD_ERASE2: + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(mtd, command, useirq); + + mxc_do_addr_cycle(mtd, column, page_addr); + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (IS_LARGE_PAGE_NAND) { + /* send read confirm command */ + send_cmd(mtd, NAND_CMD_READSTART, true); + /* read for each AREA */ + READ_PAGE(); + } else { + send_read_page(0); + } + +#ifndef NFC_AUTO_MODE_ENABLE + /* FIXME, the NFC interal buffer + * access has some limitation, it + * does not allow byte access. To + * make the code simple and ease use + * not every time check the address + * alignment.Use the temp buffer + * to accomadate the data.since We + * know data_buf will be at leat 4 + * byte alignment, so we can use + * memcpy safely + */ + mxc_nand_bi_swap(mtd); + nfc_memcpy(data_buf, MAIN_AREA0, mtd->writesize); + copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, true); +#endif + + break; + + case NAND_CMD_READID: + send_read_id(); + g_nandfc_info.colAddr = column; + nfc_memcpy(data_buf, MAIN_AREA0, 2048); + + break; + } +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page, int sndcmd) +{ + if (sndcmd) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + memcpy(chip->oob_poi, oob_buf, mtd->oobsize); + + return sndcmd; +} + +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + +#ifndef NFC_AUTO_MODE_ENABLE + mxc_check_ecc_status(mtd); +#endif + + memcpy(buf, data_buf, mtd->writesize); + memcpy(chip->oob_poi, oob_buf, mtd->oobsize); + + return 0; +} + +static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + memcpy(data_buf, buf, mtd->writesize); + memcpy(oob_buf, chip->oob_poi, mtd->oobsize); + +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + g_page_mask = this->pagemask; + + if (IS_2K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->ecc.layout = &nand_hw_eccoob_2k; + } else if (IS_4K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->ecc.layout = &nand_hw_eccoob_4k; + } else { + this->ecc.layout = &nand_hw_eccoob_512; + } + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = this->ecc.layout; + + /* jffs2 not write oob */ + mtd->flags &= ~MTD_OOB_WRITEABLE; + + /* fix up the offset */ + largepage_memorybased.offs = BAD_BLK_MARKER_OOB_OFFS; + + /* keep compatible for bbt table with old soc */ + if (cpu_is_mx53()) { + bbt_mirror_descr.offs = BAD_BLK_MARKER_OOB_OFFS + 2; + bbt_main_descr.offs = BAD_BLK_MARKER_OOB_OFFS + 2; + } + + /* use flash based bbt */ + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +static int mxc_get_resources(struct platform_device *pdev) +{ + struct resource *r; + int error = 0; + +#define MXC_NFC_NO_IP_REG \ + (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx32() || cpu_is_mx35()) + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + error = -ENXIO; + goto out_0; + } + nfc_axi_base = ioremap(r->start, resource_size(r)); + + if (!MXC_NFC_NO_IP_REG) { + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + error = -ENXIO; + goto out_1; + } + } + nfc_ip_base = ioremap(r->start, resource_size(r)); + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r) { + error = -ENXIO; + goto out_2; + } + nfc_irq = r->start; + + init_waitqueue_head(&irq_waitq); + error = request_irq(nfc_irq, mxc_nfc_irq, 0, "mxc_nd", NULL); + if (error) + goto out_3; + + return 0; +out_3: +out_2: + if (!MXC_NFC_NO_IP_REG) + iounmap(nfc_ip_base); +out_1: + iounmap(nfc_axi_base); +out_0: + return error; +} + +static void mxc_nfc_init(void) +{ + /* Disable interrupt */ + raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT); + + /* disable spare enable */ + raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN); + + /* Unlock the internal RAM Buffer */ + raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS); + + if (!(cpu_is_mx53())) { + /* Blocks to be unlocked */ + UNLOCK_ADDR(0x0, 0xFFFF); + + /* Unlock Block Command for given address range */ + raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC); + } + + /* Enable symetric mode by default except mx37TO1.0 */ + if (!(cpu_is_mx37_rev(CHIP_REV_1_0) == 1)) + raw_write(raw_read(REG_NFC_ONE_CYCLE) | + NFC_ONE_CYCLE, REG_NFC_ONE_CYCLE); +} + +static int mxc_alloc_buf(void) +{ + int err = 0; + + data_buf = kzalloc(NAND_MAX_PAGESIZE, GFP_KERNEL); + if (!data_buf) { + printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__); + err = -ENOMEM; + goto out; + } + oob_buf = kzalloc(NAND_MAX_OOBSIZE, GFP_KERNEL); + if (!oob_buf) { + printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__); + err = -ENOMEM; + goto out; + } + + out: + return err; +} + +static void mxc_free_buf(void) +{ + kfree(data_buf); + kfree(oob_buf); +} + +int nand_scan_mid(struct mtd_info *mtd) +{ + int i; + uint8_t id_bytes[NAND_DEVICE_ID_BYTE_COUNT]; + struct nand_chip *this = mtd->priv; + struct nand_device_info *dev_info; + + if (!IS_LARGE_PAGE_NAND) + return 0; + + /* Read ID bytes from the first NAND Flash chip. */ + this->select_chip(mtd, 0); + + this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + for (i = 0; i < NAND_DEVICE_ID_BYTE_COUNT; i++) + id_bytes[i] = this->read_byte(mtd); + + /* Get information about this device, based on the ID bytes. */ + dev_info = nand_device_get_info(id_bytes); + + /* Check if we understand this device. */ + if (!dev_info) { + printk(KERN_ERR "Unrecognized NAND Flash device.\n"); + return !0; + } + + /* Correct mtd setting */ + this->chipsize = dev_info->chip_size_in_bytes; + mtd->size = dev_info->chip_size_in_bytes * this->numchips; + mtd->writesize = dev_info->page_total_size_in_bytes & ~0x3ff; + mtd->oobsize = dev_info->page_total_size_in_bytes & 0x3ff; + mtd->erasesize = dev_info->block_size_in_pages * mtd->writesize; + + /* limit to 2G size due to Kernel + * larger 4G space support,need fix + * it later + */ + if ((u32)mtd->size == 0) { + mtd->size = (u32)(1 << 31); + this->numchips = 1; + this->chipsize = mtd->size; + } + + /* Calculate the address shift from the page size */ + this->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1. */ + this->pagemask = (this->chipsize >> this->page_shift) - 1; + + this->bbt_erase_shift = this->phys_erase_shift = + ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + + return 0; +} + +/*! + * show_device_disable_bi_swap() + * Shows the value of the 'disable_bi_swap' 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_disable_bi_swap(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", mxc_nand_data->disable_bi_swap); +} + +/*! + * store_device_disable_bi_swap() + * Sets the value of the 'disable_bi_swap' 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_disable_bi_swap(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const char *p = buf; + unsigned long v; + + /* Try to make sense of what arrived from user space. */ + + if (strict_strtoul(p, 0, &v) < 0) + return size; + + if (v > 0) + v = 1; + mxc_nand_data->disable_bi_swap = v; + return size; + +} + +static DEVICE_ATTR(disable_bi_swap, 0644, + show_device_disable_bi_swap, store_device_disable_bi_swap); +static struct device_attribute *device_attributes[] = { + &dev_attr_disable_bi_swap, +}; +/*! + * manage_sysfs_files() - Creates/removes sysfs files for this device. + * + * @create: create/remove the sys entry. + */ +static void manage_sysfs_files(int create) +{ + struct device *dev = mxc_nand_data->dev; + 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(dev, *attr); + if (error) { + while (--attr >= device_attributes) + device_remove_file(dev, *attr); + return; + } + } else { + device_remove_file(dev, *attr); + } + } + +} + + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +static int __init mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct flash_platform_data *flash = pdev->dev.platform_data; + int nr_parts = 0, err = 0; + + /* get the resource */ + err = mxc_get_resources(pdev); + if (err) + goto out; + + /* init the nfc */ + mxc_nfc_init(); + + /* init data buf */ + if (mxc_alloc_buf()) + goto out; + + /* Allocate memory for MTD device structure and private data */ + mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL); + if (!mxc_nand_data) { + printk(KERN_ERR "%s: failed to allocate mtd_info\n", + __FUNCTION__); + err = -ENOMEM; + goto out; + } + + memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info)); + + mxc_nand_data->dev = &pdev->dev; + /* structures must be linked */ + this = &mxc_nand_data->nand; + mtd = &mxc_nand_data->mtd; + mtd->priv = this; + mtd->owner = THIS_MODULE; + + this->priv = mxc_nand_data; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + + /* NAND bus width determines access funtions used by upper layer */ + if (flash->width == 2) { + this->read_byte = mxc_nand_read_byte16; + this->options |= NAND_BUSWIDTH_16; + NFC_SET_NFMS(1 << NFMS_NF_DWIDTH); + } else { + NFC_SET_NFMS(0); + } + + nfc_clk = clk_get(&pdev->dev, "nfc_clk"); + clk_enable(nfc_clk); + + if (hardware_ecc) { + this->ecc.read_page = mxc_nand_read_page; + this->ecc.write_page = mxc_nand_write_page; + this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.layout = &nand_hw_eccoob_512; + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 9; + raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), + REG_NFC_ECC_EN); + } else { + this->ecc.mode = NAND_ECC_SOFT; + raw_write((raw_read(REG_NFC_ECC_EN) & ~NFC_ECC_EN), + REG_NFC_ECC_EN); + } + + /* config the gpio */ + if (flash->init) + flash->init(); + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Scan to find existence of the device */ + if (nand_scan_ident(mtd, NFC_GET_MAXCHIP_SP(), NULL) + || nand_scan_mid(mtd) + || nand_scan_tail(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND2: Unable to find any NAND device.\n"); + err = -ENXIO; + goto out_1; + } + + /* Register the partitions */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts); + else if (flash->parts) + add_mtd_partitions(mtd, flash->parts, flash->nr_parts); + else +#endif + { + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); + } + + /* Create sysfs entries for this device. */ + manage_sysfs_files(true); + + platform_set_drvdata(pdev, mtd); + + return 0; + + out_1: + kfree(mxc_nand_data); + out: + return err; + +} + + /*! + * Dissociates the driver from the device. + * + * @param pdev the device structure used to give information on which + * + * @return The function always returns 0. + */ + +static int __exit mxcnd_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct flash_platform_data *flash = pdev->dev.platform_data; + + if (flash->exit) + flash->exit(); + + manage_sysfs_files(false); + mxc_free_buf(); + + clk_disable(nfc_clk); + clk_put(nfc_clk); + platform_set_drvdata(pdev, NULL); + + if (mxc_nand_data) { + nand_release(mtd); + free_irq(nfc_irq, NULL); + kfree(mxc_nand_data); + } + + return 0; +} + +#ifdef CONFIG_PM +/*! + * This function is called to put the NAND in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure + */ + +static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) +{ + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND suspend\n"); + + /* Disable the NFC clock */ + clk_disable(nfc_clk); + + return 0; +} + +/*! + * This function is called to bring the NAND back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxcnd_resume(struct platform_device *pdev) +{ + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND resume\n"); + + /* Enable the NFC clock */ + clk_enable(nfc_clk); + + return 0; +} + +#else +#define mxcnd_suspend NULL +#define mxcnd_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxcnd_driver = { + .driver = { + .name = "mxc_nandv2_flash", + }, + .probe = mxcnd_probe, + .remove = __exit_p(mxcnd_remove), + .suspend = mxcnd_suspend, + .resume = mxcnd_resume, +}; + +/*! + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init mxc_nd_init(void) +{ + /* Register the device driver structure. */ + pr_info("MXC MTD nand Driver %s\n", DVR_VER); + if (platform_driver_register(&mxcnd_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); + return -ENODEV; + } + return 0; +} + +/*! + * Clean up routine + */ +static void __exit mxc_nd_cleanup(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&mxcnd_driver); +} + +module_init(mxc_nd_init); +module_exit(mxc_nd_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver Version 2-5"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mxc_nd2.h b/drivers/mtd/nand/mxc_nd2.h new file mode 100644 index 000000000000..99b8bfc828df --- /dev/null +++ b/drivers/mtd/nand/mxc_nd2.h @@ -0,0 +1,712 @@ +/* + * Copyright 2004-2010 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 + */ + +/*! + * @file mxc_nd2.h + * + * @brief This file contains the NAND Flash Controller register information. + * + * + * @ingroup NAND_MTD + */ + +#ifndef __MXC_ND2_H__ +#define __MXC_ND2_H__ + +#include <mach/hardware.h> + +#define IS_2K_PAGE_NAND ((mtd->writesize / num_of_interleave) \ + == NAND_PAGESIZE_2KB) +#define IS_4K_PAGE_NAND ((mtd->writesize / num_of_interleave) \ + == NAND_PAGESIZE_4KB) +#define IS_LARGE_PAGE_NAND ((mtd->writesize / num_of_interleave) > 512) + +#define GET_NAND_OOB_SIZE (mtd->oobsize / num_of_interleave) +#define GET_NAND_PAGE_SIZE (mtd->writesize / num_of_interleave) + +#define NAND_PAGESIZE_2KB 2048 +#define NAND_PAGESIZE_4KB 4096 + +/* + * main area for bad block marker is in the last data section + * the spare area for swapped bad block marker is the second + * byte of last spare section + */ +#define NAND_SECTIONS (GET_NAND_PAGE_SIZE >> 9) +#define NAND_OOB_PER_SECTION (((GET_NAND_OOB_SIZE / NAND_SECTIONS) >> 1) << 1) +#define NAND_CHUNKS (GET_NAND_PAGE_SIZE / (512 + NAND_OOB_PER_SECTION)) + +#define BAD_BLK_MARKER_MAIN_OFFS \ + (GET_NAND_PAGE_SIZE - NAND_CHUNKS * NAND_OOB_PER_SECTION) + +#define BAD_BLK_MARKER_SP_OFFS (NAND_CHUNKS * SPARE_LEN) + +#define BAD_BLK_MARKER_OOB_OFFS (NAND_CHUNKS * NAND_OOB_PER_SECTION) + +#define BAD_BLK_MARKER_MAIN \ + ((u32)MAIN_AREA0 + BAD_BLK_MARKER_MAIN_OFFS) + +#define BAD_BLK_MARKER_SP \ + ((u32)SPARE_AREA0 + BAD_BLK_MARKER_SP_OFFS) + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3 +/* + * For V3 NFC registers Definition + */ + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) /* mx37 */ +#define MXC_INT_NANDFC MXC_INT_EMI +#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0x1E00) +#define NFC_CONFIG1 (nfc_axi_base + 0x1E04) +#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E08) +#define LAUNCH_NFC (nfc_axi_base + 0x1E0c) +#define NFC_WRPROT (nfc_ip_base + 0x00) +#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04) +#define NFC_CONFIG2 (nfc_ip_base + 0x14) +#define NFC_IPC (nfc_ip_base + 0x18) +#elif defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */ +#define MXC_INT_NANDFC MXC_INT_NFC +#define NFC_AUTO_MODE_ENABLE +#define NFC_FLASH_CMD (nfc_axi_base + 0x1E00) +#define NFC_FLASH_ADDR0 (nfc_axi_base + 0x1E04) +#define NFC_FLASH_ADDR8 (nfc_axi_base + 0x1E24) +#define NFC_CONFIG1 (nfc_axi_base + 0x1E34) +#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E38) +#define NFC_ECC_STATUS_SUM (nfc_axi_base + 0x1E3C) +#define LAUNCH_NFC (nfc_axi_base + 0x1E40) +#define NFC_WRPROT (nfc_ip_base + 0x00) +#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04) +#define NFC_CONFIG2 (nfc_ip_base + 0x24) +#define NFC_CONFIG3 (nfc_ip_base + 0x28) +#define NFC_IPC (nfc_ip_base + 0x2C) +#define NFC_DELAY_LINE (nfc_ip_base + 0x34) +#else /* skye */ +#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0xE00) +#define NFC_CONFIG1 (nfc_axi_base + 0xE04) +#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0xE08) +#define LAUNCH_NFC (nfc_axi_base + 0xE0C) +#define NFC_WRPROT (nfc_ip_base + 0x00) +#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04) +#define NFC_CONFIG2 (nfc_ip_base + 0x14) +#define NFC_IPC (nfc_ip_base + 0x18) +#endif +/*! + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 ((u16 *)(nfc_axi_base + 0x000)) +#define MAIN_AREA1 ((u16 *)(nfc_axi_base + 0x200)) + +/*! + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \ + defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) +#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x1000)) +#define SPARE_LEN 64 +#define SPARE_COUNT 8 +#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT) +#else +#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x800)) +#define SPARE_LEN 16 +#define SPARE_COUNT 4 +#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT) +#endif + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \ + defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) +#define NFC_SPAS_WIDTH 8 +#define NFC_SPAS_SHIFT 16 + +#define NFC_SET_SPAS(v) \ + raw_write((((raw_read(NFC_CONFIG2) & \ + NFC_FIELD_RESET(NFC_SPAS_WIDTH, NFC_SPAS_SHIFT)) | ((v) << 16))), \ + NFC_CONFIG2) + +#define NFC_SET_ECC_MODE(v) \ +do { \ + if (cpu_is_mx53() > 0) { \ + if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(3 << 6)) | \ + NFC_ECC_MODE_16), NFC_CONFIG2); \ + else \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(3 << 6)) & \ + NFC_ECC_MODE_4), NFC_CONFIG2); \ + } else if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0) { \ + if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(1 << 6)) | \ + NFC_ECC_MODE_8), NFC_CONFIG2); \ + else \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(1 << 6)) & \ + NFC_ECC_MODE_4), NFC_CONFIG2); \ + } else { \ + if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(1 << 6))), NFC_CONFIG2); \ + else \ + raw_write(((raw_read(NFC_CONFIG2) & \ + ~(1 << 6)) | \ + NFC_ECC_MODE_4), NFC_CONFIG2); \ + } \ +} while (0) + +#define WRITE_NFC_IP_REG(val, reg) \ + do { \ + raw_write(raw_read(NFC_IPC) | NFC_IPC_CREQ, NFC_IPC); \ + while (!(raw_read(NFC_IPC) & NFC_IPC_ACK)) \ + ; \ + raw_write(val, reg); \ + raw_write(raw_read(NFC_IPC) & ~NFC_IPC_CREQ, NFC_IPC); \ + } while (0) + +#else +#define NFC_SET_SPAS(v) +#define NFC_SET_ECC_MODE(v) +#define NFC_SET_NFMS(v) (NFMS |= (v)) + +#define WRITE_NFC_IP_REG(val, reg) \ + raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \ + REG_NFC_OPS_STAT) +#endif + +#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT); + +/*! + * Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for + * Specific operation + */ +#define NFC_CMD 0x1 +#define NFC_ADDR 0x2 +#define NFC_INPUT 0x4 +#define NFC_OUTPUT 0x8 +#define NFC_ID 0x10 +#define NFC_STATUS 0x20 + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */ +#define NFC_AUTO_PROG 0x40 +#define NFC_AUTO_READ 0x80 +#define NFC_AUTO_ERASE 0x200 +#define NFC_COPY_BACK_0 0x400 +#define NFC_COPY_BACK_1 0x800 +#define NFC_AUTO_STATE 0x1000 +#endif + +/* Bit Definitions for NFC_IPC*/ +#define NFC_OPS_STAT (1 << 31) + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */ +#define NFC_OP_DONE (1 << 30) +#define NFC_RB (1 << 28) +#define NFC_PS_WIDTH 2 +#define NFC_PS_SHIFT 0 +#define NFC_PS_512 0 +#define NFC_PS_2K 1 +#define NFC_PS_4K 2 +#else +#define NFC_RB (1 << 29) +#endif + +#define NFC_ONE_CYCLE (1 << 2) + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */ +#define NFC_INT_MSK (1 << 15) +#define NFC_AUTO_PROG_DONE_MSK (1 << 14) +#define NFC_NUM_ADDR_PHASE1_WIDTH 2 +#define NFC_NUM_ADDR_PHASE1_SHIFT 12 + +#define NFC_NUM_ADDR_PHASE0_WIDTH 1 +#define NFC_NUM_ADDR_PHASE0_SHIFT 5 + +#define NFC_ONE_LESS_PHASE1 0 +#define NFC_TWO_LESS_PHASE1 1 + +#define NFC_FLASH_ADDR_SHIFT 0 +#else +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_FLASH_ADDR_SHIFT 16 +#endif + +#define NFC_UNLOCK_END_ADDR_SHIFT 16 + +/* Bit definition for NFC_CONFIGRATION_1 */ +#define NFC_SP_EN (1 << 0) +#define NFC_CE (1 << 1) +#define NFC_RST (1 << 2) +#define NFC_ECC_EN (1 << 3) + +#define NFC_FIELD_RESET(width, shift) (~((((1 << (width)) - 1) << (shift)))) + +#define NFC_RBA_SHIFT 4 + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \ + defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */ +#define NFC_RBA_WIDTH 3 +#else +#define NFC_RBA_WIDTH 2 +#endif + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */ +#define NFC_ITERATION_SHIFT 8 +#define NFC_ITERATION_WIDTH 4 +#define NFC_ACTIVE_CS_SHIFT 12 +#define NFC_ACTIVE_CS_WIDTH 3 +/* bit definition for CONFIGRATION3 */ +#define NFC_NO_SDMA (1 << 20) +#define NFC_FMP_SHIFT 16 +#define NFC_FMP_WIDTH 4 +#define NFC_RBB_MODE (1 << 15) +#define NFC_NUM_OF_DEVICES_SHIFT 12 +#define NFC_NUM_OF_DEVICES_WIDTH 4 +#define NFC_DMA_MODE_SHIFT 11 +#define NFC_DMA_MODE_WIDTH 1 +#define NFC_SBB_SHIFT 8 +#define NFC_SBB_WIDTH 3 +#define NFC_BIG (1 << 7) +#define NFC_SB2R_SHIFT 4 +#define NFC_SB2R_WIDTH 3 +#define NFC_FW_SHIFT 3 +#define NFC_FW_WIDTH 1 +#define NFC_TOO (1 << 2) +#define NFC_ADD_OP_SHIFT 0 +#define NFC_ADD_OP_WIDTH 2 +#define NFC_FW_8 1 +#define NFC_FW_16 0 +#define NFC_ST_CMD_SHITF 24 +#define NFC_ST_CMD_WIDTH 8 +#endif + +#define NFC_PPB_32 (0 << 7) +#define NFC_PPB_64 (1 << 7) +#define NFC_PPB_128 (2 << 7) +#define NFC_PPB_256 (3 << 7) +#define NFC_PPB_RESET (~(3 << 7)) + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) +#define NFC_BLS_LOCKED (0 << 6) +#define NFC_BLS_LOCKED_DEFAULT (1 << 6) +#define NFC_BLS_UNLCOKED (2 << 6) +#define NFC_BLS_RESET (~(3 << 6)) +#else +#define NFC_BLS_LOCKED (0 << 16) +#define NFC_BLS_LOCKED_DEFAULT (1 << 16) +#define NFC_BLS_UNLCOKED (2 << 16) +#define NFC_BLS_RESET (~(3 << 16)) +#endif + +#define NFC_WPC_LOCK_TIGHT 1 +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) +#define NFC_WPC_RESET (~(7)) +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \ + defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) +#define NFC_ECC_MODE_4 (0x0 << 6) +#define NFC_ECC_MODE_8 (0x1 << 6) +#define NFC_ECC_MODE_14 (0x3 << 6) +#define NFC_ECC_MODE_16 (0x3 << 6) +#define NFC_SPAS_16 8 +#define NFC_SPAS_64 32 +#define NFC_SPAS_128 64 +#define NFC_SPAS_112 56 +#define NFC_SPAS_218 109 +#define NFC_IPC_CREQ (1 << 0) +#define NFC_IPC_ACK (1 << 1) +#endif + +#define REG_NFC_OPS_STAT NFC_IPC +#define REG_NFC_INTRRUPT NFC_CONFIG2 +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 +#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR0 +#define REG_NFC_FLASH_CMD NFC_FLASH_CMD +#else +#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR_CMD +#define REG_NFC_FLASH_CMD NFC_FLASH_ADDR_CMD +#endif +#define REG_NFC_OPS LAUNCH_NFC +#define REG_NFC_SET_RBA NFC_CONFIG1 +#define REG_NFC_RB NFC_IPC +#define REG_NFC_ECC_EN NFC_CONFIG2 +#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT +#define REG_NFC_CE NFC_CONFIG1 +#define REG_NFC_RST NFC_CONFIG1 +#define REG_NFC_PPB NFC_CONFIG2 +#define REG_NFC_SP_EN NFC_CONFIG1 +#define REG_NFC_BLS NFC_WRPROT +#define REG_UNLOCK_BLK_ADD0 NFC_WRPROT_UNLOCK_BLK_ADD0 +#define REG_UNLOCK_BLK_ADD1 NFC_WRPROT_UNLOCK_BLK_ADD1 +#define REG_UNLOCK_BLK_ADD2 NFC_WRPROT_UNLOCK_BLK_ADD2 +#define REG_UNLOCK_BLK_ADD3 NFC_WRPROT_UNLOCK_BLK_ADD3 +#define REG_NFC_WPC NFC_WRPROT +#define REG_NFC_ONE_CYCLE NFC_CONFIG2 + +/* NFC V3 Specific MACRO functions definitions */ +#define raw_write(v, a) __raw_writel(v, a) +#define raw_read(a) __raw_readl(a) + +/* Set RBA buffer id*/ +#define NFC_SET_RBA(val) \ + raw_write((raw_read(REG_NFC_SET_RBA) & \ + (NFC_FIELD_RESET(NFC_RBA_WIDTH, NFC_RBA_SHIFT))) | \ + ((val) << NFC_RBA_SHIFT), REG_NFC_SET_RBA); + +#define NFC_SET_PS(val) \ + raw_write((raw_read(NFC_CONFIG2) & \ + (NFC_FIELD_RESET(NFC_PS_WIDTH, NFC_PS_SHIFT))) | \ + ((val) << NFC_PS_SHIFT), NFC_CONFIG2); + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 +#define UNLOCK_ADDR(start_addr, end_addr) \ +{ \ + int i = 0; \ + for (; i < NAND_MAX_CHIPS; i++) \ + raw_write(start_addr | \ + (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), \ + REG_UNLOCK_BLK_ADD0 + (i << 2)); \ +} +#define NFC_SET_NFC_ACTIVE_CS(val) \ + raw_write((raw_read(NFC_CONFIG1) & \ + (NFC_FIELD_RESET(NFC_ACTIVE_CS_WIDTH, NFC_ACTIVE_CS_SHIFT))) | \ + ((val) << NFC_ACTIVE_CS_SHIFT), NFC_CONFIG1); + +#define NFC_GET_MAXCHIP_SP() 8 + +#else +#define UNLOCK_ADDR(start_addr, end_addr) \ + raw_write(start_addr | \ + (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), REG_UNLOCK_BLK_ADD0); + +#define NFC_SET_NFC_ACTIVE_CS(val) +#define NFC_GET_MAXCHIP_SP() 1 +#endif + +#define NFC_SET_BLS(val) ((raw_read(REG_NFC_BLS) & NFC_BLS_RESET) | val) +#define NFC_SET_WPC(val) ((raw_read(REG_NFC_WPC) & NFC_WPC_RESET) | val) +#define CHECK_NFC_RB (raw_read(REG_NFC_RB) & NFC_RB) + +#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) +#define NFC_SET_NFC_NUM_ADDR_PHASE1(val) \ + raw_write((raw_read(NFC_CONFIG2) & \ + (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE1_WIDTH, \ + NFC_NUM_ADDR_PHASE1_SHIFT))) | \ + ((val) << NFC_NUM_ADDR_PHASE1_SHIFT), NFC_CONFIG2); + +#define NFC_SET_NFC_NUM_ADDR_PHASE0(val) \ + raw_write((raw_read(NFC_CONFIG2) & \ + (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE0_WIDTH, \ + NFC_NUM_ADDR_PHASE0_SHIFT))) | \ + ((val) << NFC_NUM_ADDR_PHASE0_SHIFT), NFC_CONFIG2); + +#define NFC_SET_NFC_ITERATION(val) \ + raw_write((raw_read(NFC_CONFIG1) & \ + (NFC_FIELD_RESET(NFC_ITERATION_WIDTH, NFC_ITERATION_SHIFT))) | \ + ((val) << NFC_ITERATION_SHIFT), NFC_CONFIG1); + +#define NFC_SET_FW(val) \ + raw_write((raw_read(NFC_CONFIG3) & \ + (NFC_FIELD_RESET(NFC_FW_WIDTH, NFC_FW_SHIFT))) | \ + ((val) << NFC_FW_SHIFT), NFC_CONFIG3); + +#define NFC_SET_NUM_OF_DEVICE(val) \ + raw_write((raw_read(NFC_CONFIG3) & \ + (NFC_FIELD_RESET(NFC_NUM_OF_DEVICES_WIDTH, \ + NFC_NUM_OF_DEVICES_SHIFT))) | \ + ((val) << NFC_NUM_OF_DEVICES_SHIFT), NFC_CONFIG3); + +#define NFC_SET_ADD_OP_MODE(val) \ + raw_write((raw_read(NFC_CONFIG3) & \ + (NFC_FIELD_RESET(NFC_ADD_OP_WIDTH, NFC_ADD_OP_SHIFT))) | \ + ((val) << NFC_ADD_OP_SHIFT), NFC_CONFIG3); + +#define NFC_SET_ADD_CS_MODE(val) \ +{ \ + NFC_SET_ADD_OP_MODE(val); \ + NFC_SET_NUM_OF_DEVICE(this->numchips - 1); \ +} + +#define NFC_SET_ST_CMD(val) \ + raw_write((raw_read(NFC_CONFIG2) & \ + (NFC_FIELD_RESET(NFC_ST_CMD_WIDTH, \ + NFC_ST_CMD_SHITF))) | \ + ((val) << NFC_ST_CMD_SHITF), NFC_CONFIG2); + +#define NFMS_NF_DWIDTH 0 +#define NFMS_NF_PG_SZ 1 +#define NFC_CMD_1_SHIFT 8 + +#define NUM_OF_ADDR_CYCLE (fls(g_page_mask) >> 3) +#define SET_NFC_DELAY_LINE(val) raw_write((val), NFC_DELAY_LINE) + +/*should set the fw,ps,spas,ppb*/ +#define NFC_SET_NFMS(v) \ +do { \ + if (!(v)) \ + NFC_SET_FW(NFC_FW_8); \ + if (((v) & (1 << NFMS_NF_DWIDTH))) \ + NFC_SET_FW(NFC_FW_16); \ + if (((v) & (1 << NFMS_NF_PG_SZ))) { \ + if (IS_2K_PAGE_NAND) { \ + NFC_SET_PS(NFC_PS_2K); \ + NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \ + NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \ + } else if (IS_4K_PAGE_NAND) { \ + NFC_SET_PS(NFC_PS_4K); \ + NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \ + NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \ + } else { \ + NFC_SET_PS(NFC_PS_512); \ + NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE - 1); \ + NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_ONE_LESS_PHASE1); \ + } \ + NFC_SET_ADD_CS_MODE(1); \ + NFC_SET_SPAS(GET_NAND_OOB_SIZE >> 1); \ + NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \ + NFC_SET_ST_CMD(0x70); \ + raw_write(raw_read(NFC_CONFIG3) | NFC_NO_SDMA, NFC_CONFIG3); \ + raw_write(raw_read(NFC_CONFIG3) | NFC_RBB_MODE, NFC_CONFIG3); \ + if (cpu_is_mx51()) \ + SET_NFC_DELAY_LINE(0); \ + } \ +} while (0) +#endif + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_1 +#define NFC_SET_NFMS(v) +#endif + +#define READ_PAGE() send_read_page(0) +#define PROG_PAGE() send_prog_page(0) + +#elif CONFIG_ARCH_MXC_HAS_NFC_V2 + +/* + * For V1/V2 NFC registers Definition + */ + +/* + * Addresses for NFC registers + */ +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define NFC_REG_BASE (nfc_axi_base + 0x1000) +#else +#define NFC_REG_BASE nfc_axi_base +#endif +#define NFC_BUF_SIZE (NFC_REG_BASE + 0xE00) +#define NFC_BUF_ADDR (NFC_REG_BASE + 0xE04) +#define NFC_FLASH_ADDR (NFC_REG_BASE + 0xE06) +#define NFC_FLASH_CMD (NFC_REG_BASE + 0xE08) +#define NFC_CONFIG (NFC_REG_BASE + 0xE0A) +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C) +#define NFC_ECC_STATUS_RESULT_1 (NFC_REG_BASE + 0xE0C) +#define NFC_ECC_STATUS_RESULT_2 (NFC_REG_BASE + 0xE0E) +#define NFC_SPAS (NFC_REG_BASE + 0xE10) +#else +#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C) +#define NFC_RSLTMAIN_AREA (NFC_REG_BASE + 0xE0E) +#define NFC_RSLTSPARE_AREA (NFC_REG_BASE + 0xE10) +#endif +#define NFC_WRPROT (NFC_REG_BASE + 0xE12) +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE20) +#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE22) +#define NFC_UNLOCKSTART_BLKADDR1 (NFC_REG_BASE + 0xE24) +#define NFC_UNLOCKEND_BLKADDR1 (NFC_REG_BASE + 0xE26) +#define NFC_UNLOCKSTART_BLKADDR2 (NFC_REG_BASE + 0xE28) +#define NFC_UNLOCKEND_BLKADDR2 (NFC_REG_BASE + 0xE2A) +#define NFC_UNLOCKSTART_BLKADDR3 (NFC_REG_BASE + 0xE2C) +#define NFC_UNLOCKEND_BLKADDR3 (NFC_REG_BASE + 0xE2E) +#else +#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE14) +#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE16) +#endif +#define NFC_NF_WRPRST (NFC_REG_BASE + 0xE18) +#define NFC_CONFIG1 (NFC_REG_BASE + 0xE1A) +#define NFC_CONFIG2 (NFC_REG_BASE + 0xE1C) + +/*! + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (u16 *)(nfc_axi_base + 0x000) +#define MAIN_AREA1 (u16 *)(nfc_axi_base + 0x200) + +/*! + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define SPARE_AREA0 (u16 *)(nfc_axi_base + 0x1000) +#define SPARE_LEN 64 +#define SPARE_COUNT 8 +#else +#define SPARE_AREA0 (u16 *)(nfc_axi_base + 0x800) +#define SPARE_LEN 16 +#define SPARE_COUNT 4 +#endif +#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT) + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define REG_NFC_ECC_MODE NFC_CONFIG1 +#define SPAS_SHIFT (0) +#define REG_NFC_SPAS NFC_SPAS +#define SPAS_MASK (0xFF00) + +#define NFC_SET_SPAS(v) \ + raw_write(((raw_read(REG_NFC_SPAS) & SPAS_MASK) | ((v<<SPAS_SHIFT))), \ + REG_NFC_SPAS) + +#define NFC_SET_ECC_MODE(v) \ +do { \ + if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) { \ + raw_write((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_8), \ + REG_NFC_ECC_MODE); \ + } else { \ + raw_write((raw_read(REG_NFC_ECC_MODE) | NFC_ECC_MODE_4), \ + REG_NFC_ECC_MODE); \ + } \ +} while (0) + +#define GET_ECC_STATUS() __raw_readl(REG_NFC_ECC_STATUS_RESULT); +#define NFC_SET_NFMS(v) \ +do { \ + if (((v) & (1 << NFMS_NF_PG_SZ))) { \ + if (IS_2K_PAGE_NAND) { \ + (NFMS |= 0x00000100); \ + (NFMS &= ~0x00000200); \ + NFC_SET_SPAS(NFC_SPAS_64); \ + } else if (IS_4K_PAGE_NAND) { \ + (NFMS &= ~0x00000100); \ + (NFMS |= 0x00000200); \ + GET_NAND_OOB_SIZE == 128 ? \ + NFC_SET_SPAS(NFC_SPAS_128) : \ + NFC_SET_SPAS(NFC_SPAS_218); \ + } else { \ + printk(KERN_ERR "Err for setting page/oob size"); \ + } \ + NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \ + } \ +} while (0) +#else +#define NFC_SET_SPAS(v) +#define NFC_SET_ECC_MODE(v) +#define GET_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT); +#define NFC_SET_NFMS(v) (NFMS |= (v)) +#endif + +#define WRITE_NFC_IP_REG(val, reg) \ + raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \ + REG_NFC_OPS_STAT) + +#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT); + +/*! + * Set INT to 0, Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for + * Specific operation + */ +#define NFC_CMD 0x1 +#define NFC_ADDR 0x2 +#define NFC_INPUT 0x4 +#define NFC_OUTPUT 0x8 +#define NFC_ID 0x10 +#define NFC_STATUS 0x20 + +/* Bit Definitions */ +#define NFC_OPS_STAT (1 << 15) +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_BLS_LOCKED 0 +#define NFC_BLS_LOCKED_DEFAULT 1 +#define NFC_BLS_UNLCOKED 2 +#define NFC_WPC_LOCK_TIGHT 1 +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) +#define NFC_FLASH_ADDR_SHIFT 0 +#define NFC_UNLOCK_END_ADDR_SHIFT 0 + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define NFC_ECC_MODE_4 (1<<0) +#define NFC_ECC_MODE_8 (~(1<<0)) +#define NFC_SPAS_16 8 +#define NFC_SPAS_64 32 +#define NFC_SPAS_112 56 +#define NFC_SPAS_128 64 +#define NFC_SPAS_218 109 +#endif +/* NFC Register Mapping */ +#define REG_NFC_OPS_STAT NFC_CONFIG2 +#define REG_NFC_INTRRUPT NFC_CONFIG1 +#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR +#define REG_NFC_FLASH_CMD NFC_FLASH_CMD +#define REG_NFC_OPS NFC_CONFIG2 +#define REG_NFC_SET_RBA NFC_BUF_ADDR +#define REG_NFC_ECC_EN NFC_CONFIG1 +#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT +#define REG_NFC_CE NFC_CONFIG1 +#define REG_NFC_SP_EN NFC_CONFIG1 +#define REG_NFC_BLS NFC_CONFIG +#define REG_NFC_WPC NFC_WRPROT +#define REG_START_BLKADDR NFC_UNLOCKSTART_BLKADDR +#define REG_END_BLKADDR NFC_UNLOCKEND_BLKADDR +#define REG_NFC_RST NFC_CONFIG1 +#define REG_NFC_ONE_CYCLE NFC_CONFIG1 + +/* NFC V1/V2 Specific MACRO functions definitions */ + +#define raw_write(v, a) __raw_writew(v, a) +#define raw_read(a) __raw_readw(a) + +#define NFC_SET_BLS(val) val + +#define UNLOCK_ADDR(start_addr, end_addr) \ +{ \ + raw_write(start_addr, REG_START_BLKADDR); \ + raw_write(end_addr, REG_END_BLKADDR); \ +} + +#define NFC_SET_NFC_ACTIVE_CS(val) +#define NFC_GET_MAXCHIP_SP() 1 +#define NFC_SET_WPC(val) val + +#define NFC_SET_RBA(val) raw_write(val, REG_NFC_SET_RBA); + +#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1 +#define READ_PAGE() send_read_page(0) +#define PROG_PAGE() send_prog_page(0) +#else +#define READ_PAGE() \ +do { \ + send_read_page(0); \ + send_read_page(1); \ + send_read_page(2); \ + send_read_page(3); \ +} while (0) + +#define PROG_PAGE() \ +do { \ + send_prog_page(0); \ + send_prog_page(1); \ + send_prog_page(2); \ + send_prog_page(3); \ +} while (0) +#endif +#define CHECK_NFC_RB 1 + +#endif + +#endif /* __MXC_ND2_H__ */ diff --git a/drivers/mtd/nand/nand_device_info.c b/drivers/mtd/nand/nand_device_info.c new file mode 100644 index 000000000000..3c6333f54e57 --- /dev/null +++ b/drivers/mtd/nand/nand_device_info.c @@ -0,0 +1,2286 @@ +/* + * Copyright (C) 2009-2010 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 <asm/sizes.h> +#include <linux/mtd/nand.h> + +#include "nand_device_info.h" + +/* + * Type 2 + */ +static struct nand_device_info nand_device_info_table_type_2[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 30, + .data_hold_in_ns = 20, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "NAND01GW3", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 30, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 30, + .data_hold_in_ns = 20, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 35, + .data_hold_in_ns = 25, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9F1F08", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 30, + .data_hold_in_ns = 20, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG0S3", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 32, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "NAND02GW3", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 30, + .data_hold_in_ns = 25, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UF082G2M, HY27UG082G2M, HY27UG082G1M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F2G08", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9F2G08U0M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG1S3", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 32, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 30, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 30, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 10, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UH084G2M, HY27UG084G2M, HY27UH084G1M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F4G08", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 25, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 25, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TH58NVG2S3", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 32, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 30, + .data_hold_in_ns = 25, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UH088G2M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 30, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "NAND08GW3BxANx", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 15, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F8G08FABWG", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 32, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 30, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 30, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 32, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + {true} +}; + +/* + * Large MLC + */ +static struct nand_device_info nand_device_info_table_large_mlc[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG1D4BFT00", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 35, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TH58NVG3D4xFT00", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 35, + .data_hold_in_ns = 20, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 35, + .data_hold_in_ns = 15, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TH58NVG4D4xFT00", + }, + { + .end_of_table = false, + .manufacturer_code = 0x45, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 35, + .data_hold_in_ns = 15, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 30, + .address_setup_in_ns = 0, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG2D4BFT00", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 15, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9G4G08U0M", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 45, + .data_hold_in_ns = 25, + .address_setup_in_ns = 50, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UT084G2M, HY27UU088G5M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x20, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 40, + .data_hold_in_ns = 20, + .address_setup_in_ns = 30, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "NAND04GW3C2AN1E", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 15, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9G8G08U0M, K9HAG08U1M", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 60, + .data_hold_in_ns = 30, + .address_setup_in_ns = 50, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UV08AG5M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "Intel JS29F08G08AAMiB1 and Micron MT29F8G08MAA; " + "Intel JS29F08G08CAMiB1 and Micron MT29F16G08QAA", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 15, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9LAG08U0M K9HBG08U1M K9GAG08U0M", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "Intel JS29F32G08FAMiB1 and Micron MT29F32G08TAA", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 20, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F4G08", + }, + { + .end_of_table = false, + .manufacturer_code = 0x89, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "JS29F08G08AAMiB2, JS29F08G08CAMiB2", + }, + { + .end_of_table = false, + .manufacturer_code = 0x89, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "JS29F32G08FAMiB2", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "HY27UW08CGFM", + }, + {true} +}; + +/* + * Type 7 + */ +static struct nand_device_info nand_device_info_table_type_7[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 15, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F8G08FABWG", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F4G08AAA", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xdc, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 512LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 12, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9F4G08", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 15, + .address_setup_in_ns = 35, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9K8G08UXM, K9NBG08U5A, K9WAG08U1A", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 12, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9WAG08UXM", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xda, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 256LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9F2G08U0A", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xf1, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 128LL*SZ_1M, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 2*SZ_1K + 64, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 12, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9F1F08", + }, + {true} +}; + +/* + * Type 8 + */ +static struct nand_device_info nand_device_info_table_type_8[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9GAG08U0M", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9LBG08U0M (32Gb), K9HCG08U1M (64Gb), K9MDG08U5M (128Gb)", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 20, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 0, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "H27UAG, H27UBG", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 23, + .data_hold_in_ns = 20, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 0, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "H27UCG", + }, + {true} +}; + +/* + * Type 9 + */ +static struct nand_device_info nand_device_info_table_type_9[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG3D1DTG00", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TC58NVG4D1DTG00", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 10, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "TH58NVG6D1DTG20", + }, + { + .end_of_table = false, + .manufacturer_code = 0x89, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 10, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "JS29F16G08AAMC1, JS29F32G08CAMC1", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F16G08MAA, MT29F32G08QAA", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F64G08TAA (32Gb), MT29F32G08CBAAA (32Gb) MT29F64G08CFAAA (64Gb)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd9, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 8LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 10, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "MT29F128G08CJAAA", + }, + { + .end_of_table = false, + .manufacturer_code = 0x89, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 10, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "JSF64G08FAMC1", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "K9LBG08U0D", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 8, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9GAG08U0D, K9LBG08U1D, K9HCG08U5D", + }, + {true} +}; + +/* + * Type 10 + */ +static struct nand_device_info nand_device_info_table_type_10[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd3, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 1LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd5, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 25, + .data_hold_in_ns = 15, + .address_setup_in_ns = 30, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + "K9NCG08U5M", + }, + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_SLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 64, + .page_total_size_in_bytes = 4*SZ_1K + 128, + .ecc_strength_in_bits = 4, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 15, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + NULL, + }, + {true} +}; + +/* + * Type 11 + */ +static struct nand_device_info nand_device_info_table_type_11[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 8*SZ_1K + 376, + .ecc_strength_in_bits = 14, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 8, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 25, + "TC58NVG5D2ELAM8 (4GB), TH58NVG6D2ELAM8 (8GB)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x98, + .device_code = 0xde, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 8LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 8*SZ_1K + 376, + .ecc_strength_in_bits = 14, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 8, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 25, + "TH58NVG7D2ELAM8", + }, + {true} +}; + +/* + * Type 15 + */ +static struct nand_device_info nand_device_info_table_type_15[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0xec, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 8*SZ_1K + 436, + .ecc_strength_in_bits = 16, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 20, + .data_hold_in_ns = 10, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 25, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "K9GBG08U0M (4GB, 1CE); K9LCG08U1M (8GB, 2CE); K9HDG08U5M (16GB, 4CE)", + }, + {true} +}; + +/* + * BCH ECC12 + */ +static struct nand_device_info nand_device_info_table_bch_ecc12[] __initdata = { + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 224, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "H27UBG8T2M (4GB, 1CE), H27UCG8UDM (8GB, 2CE), H27UDG8VEM (16GB, 4CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0xad, + .device_code = 0xde, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 8LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 224, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "H27UEG8YEM (32GB, 4CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd7, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 10, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 16, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "MT29F32G08CBAAA (4GB, 1CE), MT29F64G08CFAAA (8GB, 2CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0xd9, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 8LL*SZ_1G, + .block_size_in_pages = 128, + .page_total_size_in_bytes = 4*SZ_1K + 218, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 10, + .data_hold_in_ns = 10, + .address_setup_in_ns = 15, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 16, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "MT29F128G08CJAAA (16GB, 2CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0x48, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 2LL*SZ_1G, + .block_size_in_pages = 256, + .page_total_size_in_bytes = 4*SZ_1K + 224, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "MT29F16G08CBABA (2GB, 1CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0x68, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 4LL*SZ_1G, + .block_size_in_pages = 256, + .page_total_size_in_bytes = 4*SZ_1K + 224, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "MT29F32G08CBABA (4GB, 1CE); " + "MT29F64G08CEABA (8GB, 2CE); " + "MT29F64G08CFABA (8GB, 2CE)", + }, + { + .end_of_table = false, + .manufacturer_code = 0x2c, + .device_code = 0x88, + .cell_technology = NAND_DEVICE_CELL_TECH_MLC, + .chip_size_in_bytes = 8LL*SZ_1G, + .block_size_in_pages = 256, + .page_total_size_in_bytes = 4*SZ_1K + 224, + .ecc_strength_in_bits = 12, + .ecc_size_in_bytes = 512, + .data_setup_in_ns = 15, + .data_hold_in_ns = 10, + .address_setup_in_ns = 20, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = 20, + .tRLOH_in_ns = 5, + .tRHOH_in_ns = 15, + "MT29F128G08CJABA (16GB, 2CE); " + "MT29F128G08CKABA (16GB, 2CE); " + "MT29F256G08CUABA (32GB, 4CE)", + }, + {true} +}; + +/* + * The following macros make it convenient to extract information from an ID + * byte array. All these macros begin with the prefix "ID_". + * + * Macros of the form: + * + * ID_GET_[<manufacturer>_[<modifier>_]]<field> + * + * extract the given field from an ID byte array. Macros of the form: + * + * ID_[<manufacturer>_[<modifier>_]]<field>_<meaning> + * + * contain the value for the given field that has the given meaning. + * + * If the <manufacturer> appears, it means this macro represents a view of this + * field that is specific to the given manufacturer. + * + * If the <modifier> appears, it means this macro represents a view of this + * field that the given manufacturer applies only under specific conditions. + * + * Here is a simple example: + * + * ID_PAGE_SIZE_CODE_2K + * + * This macro has the value of the "Page Size" field that indicates the page + * size is 2K. + * + * A more complicated example: + * + * ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K (0x2) + * + * This macro has the value of the "Page Size" field for Samsung parts that + * indicates the page size is 8K. However, this interpretation is only correct + * for devices that return 6 ID bytes. + */ + +/* Byte 1 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_1(id) ((id)[0]) + +#define ID_GET_MFR_CODE(id) ID_GET_BYTE_1(id) + +/* Byte 2 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_2(id) ((id)[1]) + +#define ID_GET_DEVICE_CODE(id) ID_GET_BYTE_2(id) + #define ID_SAMSUNG_DEVICE_CODE_1_GBIT (0xf1) + #define ID_SAMSUNG_DEVICE_CODE_2_GBIT (0xda) + #define ID_HYNIX_DEVICE_CODE_ECC12 (0xd7) + #define ID_HYNIX_DEVICE_CODE_ECC12_LARGE (0xde) + #define ID_MICRON_DEVICE_CODE_ECC12 (0xd7) /* ECC12 */ + #define ID_MICRON_DEVICE_CODE_ECC12_LARGE (0xd9) /* ECC12 8GB/CE */ + #define ID_MICRON_DEVICE_CODE_ECC12_2GB_PER_CE (0x48) /* L63B 2GB/CE */ + #define ID_MICRON_DEVICE_CODE_ECC12_4GB_PER_CE (0x68) /* L63B 4GB/CE */ + #define ID_MICRON_DEVICE_CODE_ECC12_8GB_PER_CE (0x88) /* L63B 8GB/CE */ + +/* Byte 3 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_3(id) ((id)[2]) + +#define ID_GET_DIE_COUNT_CODE(id) ((ID_GET_BYTE_3(id) >> 0) & 0x3) + +#define ID_GET_CELL_TYPE_CODE(id) ((ID_GET_BYTE_3(id) >> 2) & 0x3) + #define ID_CELL_TYPE_CODE_SLC (0x0) /* All others => MLC. */ + +#define ID_GET_SAMSUNG_SIMUL_PROG(id) ((ID_GET_BYTE_3(id) >> 4) & 0x3) + +#define ID_GET_MICRON_SIMUL_PROG(id) ((ID_GET_BYTE_3(id) >> 4) & 0x3) + +#define ID_GET_CACHE_PROGRAM(id) ((ID_GET_BYTE_3(id) >> 7) & 0x1) + +/* Byte 4 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_4(id) ((id)[3]) + #define ID_HYNIX_BYTE_4_ECC12_DEVICE (0x25) + +#define ID_GET_PAGE_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 0) & 0x3) + #define ID_PAGE_SIZE_CODE_1K (0x0) + #define ID_PAGE_SIZE_CODE_2K (0x1) + #define ID_PAGE_SIZE_CODE_4K (0x2) + #define ID_PAGE_SIZE_CODE_8K (0x3) + #define ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K (0x2) + +#define ID_GET_OOB_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 2) & 0x1) + +#define ID_GET_BLOCK_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 4) & 0x3) + +/* Byte 5 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_5(id) ((id)[4]) + #define ID_MICRON_BYTE_5_ECC12 (0x84) + +#define ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) ((ID_GET_BYTE_5(id) >> 4) & 0x7) + #define ID_SAMSUNG_ECC_LEVEL_CODE_8 (0x03) + #define ID_SAMSUNG_ECC_LEVEL_CODE_24 (0x05) + +#define ID_GET_PLANE_COUNT_CODE(id) ((ID_GET_BYTE_5(id) >> 2) & 0x3) + +/* Byte 6 ------------------------------------------------------------------- */ + +#define ID_GET_BYTE_6(id) ((id)[5]) + #define ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_8K (0x54) + #define ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_4K (0x13) + +#define ID_GET_SAMSUNG_DEVICE_VERSION_CODE(id) ((ID_GET_BYTE_6(id)>>0) & 0x7) + #define ID_SAMSUNG_DEVICE_VERSION_CODE_40NM (0x01) + +/* -------------------------------------------------------------------------- */ + +void nand_device_print_info(struct nand_device_info *info) +{ + unsigned i; + const char *mfr_name; + const char *cell_technology_name; + uint64_t chip_size; + const char *chip_size_units; + unsigned page_data_size_in_bytes; + unsigned page_oob_size_in_bytes; + + /* Check for nonsense. */ + + if (!info) + return; + + /* Prepare the manufacturer name. */ + + mfr_name = "Unknown"; + + for (i = 0; nand_manuf_ids[i].id; i++) { + if (nand_manuf_ids[i].id == info->manufacturer_code) { + mfr_name = nand_manuf_ids[i].name; + break; + } + } + + /* Prepare the name of the cell technology. */ + + switch (info->cell_technology) { + case NAND_DEVICE_CELL_TECH_SLC: + cell_technology_name = "SLC"; + break; + case NAND_DEVICE_CELL_TECH_MLC: + cell_technology_name = "MLC"; + break; + default: + cell_technology_name = "Unknown"; + break; + } + + /* Prepare the chip size. */ + + if ((info->chip_size_in_bytes >= SZ_1G) && + !(info->chip_size_in_bytes % SZ_1G)) { + chip_size = info->chip_size_in_bytes / ((uint64_t) SZ_1G); + chip_size_units = "GiB"; + } else if ((info->chip_size_in_bytes >= SZ_1M) && + !(info->chip_size_in_bytes % SZ_1M)) { + chip_size = info->chip_size_in_bytes / ((uint64_t) SZ_1M); + chip_size_units = "MiB"; + } else { + chip_size = info->chip_size_in_bytes; + chip_size_units = "B"; + } + + /* Prepare the page geometry. */ + + page_data_size_in_bytes = (1<<(fls(info->page_total_size_in_bytes)-1)); + page_oob_size_in_bytes = info->page_total_size_in_bytes - + page_data_size_in_bytes; + + /* Print the information. */ + + printk(KERN_INFO "Manufacturer : %s (0x%02x)\n", mfr_name, + info->manufacturer_code); + printk(KERN_INFO "Device Code : 0x%02x\n", info->device_code); + printk(KERN_INFO "Cell Technology : %s\n", cell_technology_name); + printk(KERN_INFO "Chip Size : %llu %s\n", chip_size, + chip_size_units); + printk(KERN_INFO "Pages per Block : %u\n", + info->block_size_in_pages); + printk(KERN_INFO "Page Geometry : %u+%u\n", page_data_size_in_bytes, + page_oob_size_in_bytes); + printk(KERN_INFO "ECC Strength : %u bits\n", + info->ecc_strength_in_bits); + printk(KERN_INFO "ECC Size : %u B\n", info->ecc_size_in_bytes); + printk(KERN_INFO "Data Setup Time : %u ns\n", info->data_setup_in_ns); + printk(KERN_INFO "Data Hold Time : %u ns\n", info->data_hold_in_ns); + printk(KERN_INFO "Address Setup Time: %u ns\n", + info->address_setup_in_ns); + printk(KERN_INFO "GPMI Sample Delay : %u ns\n", + info->gpmi_sample_delay_in_ns); + if (info->tREA_in_ns >= 0) + printk(KERN_INFO "tREA : %u ns\n", + info->tREA_in_ns); + else + printk(KERN_INFO "tREA : Unknown\n"); + if (info->tREA_in_ns >= 0) + printk(KERN_INFO "tRLOH : %u ns\n", + info->tRLOH_in_ns); + else + printk(KERN_INFO "tRLOH : Unknown\n"); + if (info->tREA_in_ns >= 0) + printk(KERN_INFO "tRHOH : %u ns\n", + info->tRHOH_in_ns); + else + printk(KERN_INFO "tRHOH : Unknown\n"); + if (info->description) + printk(KERN_INFO "Description : %s\n", info->description); + else + printk(KERN_INFO "Description : <None>\n"); + +} + +static struct nand_device_info *nand_device_info_search( + struct nand_device_info *table, uint8_t mfr_code, uint8_t device_code) +{ + + for (; !table->end_of_table; table++) { + if (table->manufacturer_code != mfr_code) + continue; + if (table->device_code != device_code) + continue; + return table; + } + + return 0; + +} + +static struct nand_device_info * __init nand_device_info_fn_toshiba(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an SLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) { + /* Type 2 */ + return nand_device_info_search(nand_device_info_table_type_2, + ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id)); + } + + /* + * Look for 8K page Toshiba MLC devices. + * + * The page size field in byte 4 can't be used because the field was + * redefined in the 8K parts so the value meaning "8K page" is the same + * as the value meaning "4K page" on the 4K page devices. + * + * The only identifiable difference between the 4K and 8K page Toshiba + * devices with a device code of 0xd7 is the undocumented 6th ID byte. + * The 4K device returns a value of 0x13 and the 8K a value of 0x54. + * Toshiba has verified that this is an acceptable method to distinguish + * the two device families. + */ + + if (ID_GET_BYTE_6(id) == ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_8K) { + /* Type 11 */ + table = nand_device_info_table_type_11; + } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* Type 9 */ + table = nand_device_info_table_type_9; + } else { + /* Large MLC */ + table = nand_device_info_table_large_mlc; + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_samsung(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an MLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) != ID_CELL_TYPE_CODE_SLC) { + + /* Is this a Samsung 8K Page MLC device with 16 bit ECC? */ + if ((ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) == + ID_SAMSUNG_ECC_LEVEL_CODE_24) && + (ID_GET_PAGE_SIZE_CODE(id) == + ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K)) { + /* Type 15 */ + table = nand_device_info_table_type_15; + } + /* Is this a Samsung 42nm ECC8 device with a 6 byte ID? */ + else if ((ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) == + ID_SAMSUNG_ECC_LEVEL_CODE_8) && + (ID_GET_SAMSUNG_DEVICE_VERSION_CODE(id) == + ID_SAMSUNG_DEVICE_VERSION_CODE_40NM)) { + /* Type 9 */ + table = nand_device_info_table_type_9; + } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* Type 8 */ + table = nand_device_info_table_type_8; + } else { + /* Large MLC */ + table = nand_device_info_table_large_mlc; + } + + } else { + + /* Check the page size first. */ + if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* Type 10 */ + table = nand_device_info_table_type_10; + } + /* Check the chip size. */ + else if (ID_GET_DEVICE_CODE(id) == + ID_SAMSUNG_DEVICE_CODE_1_GBIT) { + if (!ID_GET_CACHE_PROGRAM(id)) { + /* + * 128 MiB Samsung chips without cache program + * are Type 7. + * + * The K9F1G08U0B does not support multi-plane + * program, so the if statement below cannot be + * used to identify it. + */ + table = nand_device_info_table_type_7; + + } else { + /* Smaller sizes are Type 2 by default. */ + table = nand_device_info_table_type_2; + } + } else { + /* Check number of simultaneously programmed pages. */ + if (ID_GET_SAMSUNG_SIMUL_PROG(id) && + ID_GET_PLANE_COUNT_CODE(id)) { + /* Type 7 */ + table = nand_device_info_table_type_7; + } else { + /* Type 2 */ + table = nand_device_info_table_type_2; + } + + } + + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_stmicro(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an SLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) + /* Type 2 */ + table = nand_device_info_table_type_2; + else + /* Large MLC */ + table = nand_device_info_table_large_mlc; + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_hynix(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an SLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) { + /* Type 2 */ + return nand_device_info_search(nand_device_info_table_type_2, + ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id)); + } + + /* + * Check for ECC12 devices. + * + * We look at the 4th ID byte to distinguish some Hynix ECC12 devices + * from the similar ECC8 part. For example H27UBG8T2M (ECC12) 4th byte + * is 0x25, whereas H27UDG8WFM (ECC8) 4th byte is 0xB6. + */ + + if ((ID_GET_DEVICE_CODE(id) == ID_HYNIX_DEVICE_CODE_ECC12 && + ID_GET_BYTE_4(id) == ID_HYNIX_BYTE_4_ECC12_DEVICE) || + (ID_GET_DEVICE_CODE(id) == ID_HYNIX_DEVICE_CODE_ECC12_LARGE)) { + /* BCH ECC 12 */ + table = nand_device_info_table_bch_ecc12; + } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* + * So far, all other Samsung and Hynix 4K page devices are + * Type 8. + */ + table = nand_device_info_table_type_8; + } else + /* Large MLC */ + table = nand_device_info_table_large_mlc; + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_micron(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an SLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) { + + /* Check number of simultaneously programmed pages. */ + + if (ID_GET_MICRON_SIMUL_PROG(id)) { + /* Type 7 */ + table = nand_device_info_table_type_7; + } else { + /* Zero simultaneously programmed pages means Type 2. */ + table = nand_device_info_table_type_2; + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + + } + + /* + * We look at the 5th ID byte to distinguish some Micron ECC12 NANDs + * from the similar ECC8 part. + * + * For example MT29F64G08CFAAA (ECC12) 5th byte is 0x84, whereas + * MT29F64G08TAA (ECC8) 5th byte is 0x78. + * + * We also have a special case for the Micron L63B family + * (256 page/block), which has unique device codes but no ID fields that + * can easily be used to distinguish the family. + */ + + if ((ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12 && + ID_GET_BYTE_5(id) == ID_MICRON_BYTE_5_ECC12) || + (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_LARGE) || + (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_2GB_PER_CE) || + (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_4GB_PER_CE) || + (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_8GB_PER_CE)) { + /* BCH ECC 12 */ + table = nand_device_info_table_bch_ecc12; + } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* Toshiba devices with 4K pages are Type 9. */ + table = nand_device_info_table_type_9; + } else { + /* Large MLC */ + table = nand_device_info_table_large_mlc; + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_sandisk(const uint8_t id[]) +{ + struct nand_device_info *table; + + if (ID_GET_CELL_TYPE_CODE(id) != ID_CELL_TYPE_CODE_SLC) { + /* Large MLC */ + table = nand_device_info_table_large_mlc; + } else { + /* Type 2 */ + table = nand_device_info_table_type_2; + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +static struct nand_device_info * __init nand_device_info_fn_intel(const uint8_t id[]) +{ + struct nand_device_info *table; + + /* Check for an SLC device. */ + + if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) { + /* Type 2 */ + return nand_device_info_search(nand_device_info_table_type_2, + ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id)); + } + + if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) { + /* Type 9 */ + table = nand_device_info_table_type_9; + } else { + /* Large MLC */ + table = nand_device_info_table_large_mlc; + } + + return nand_device_info_search(table, ID_GET_MFR_CODE(id), + ID_GET_DEVICE_CODE(id)); + +} + +/** + * struct nand_device_type_info - Information about a NAND Flash type. + * + * @name: A human-readable name for this type. + * @table: The device info table for this type. + */ + +struct nand_device_type_info { + struct nand_device_info *table; + const char *name; +}; + +/* + * A table that maps manufacturer IDs to device information tables. + */ + +static struct nand_device_type_info nand_device_type_directory[] __initdata = { + {nand_device_info_table_type_2, "Type 2" }, + {nand_device_info_table_large_mlc, "Large MLC"}, + {nand_device_info_table_type_7, "Type 7" }, + {nand_device_info_table_type_8, "Type 8" }, + {nand_device_info_table_type_9, "Type 9" }, + {nand_device_info_table_type_10, "Type 10" }, + {nand_device_info_table_type_11, "Type 11" }, + {nand_device_info_table_type_15, "Type 15" }, + {nand_device_info_table_bch_ecc12, "BCH ECC12"}, + {0, 0}, +}; + +/** + * struct nand_device_mfr_info - Information about a NAND Flash manufacturer. + * + * @id: The value of the first NAND Flash ID byte, which identifies the + * manufacturer. + * @fn: A pointer to a function to use for identifying devices from the + * given manufacturer. + */ + +struct nand_device_mfr_info { + uint8_t id; + struct nand_device_info *(*fn)(const uint8_t id[]); +}; + +/* + * A table that maps manufacturer IDs to device information tables. + */ + +static struct nand_device_mfr_info nand_device_mfr_directory[] __initdata = { + { + .id = NAND_MFR_TOSHIBA, + .fn = nand_device_info_fn_toshiba, + }, + { + .id = NAND_MFR_SAMSUNG, + .fn = nand_device_info_fn_samsung, + }, + { + .id = NAND_MFR_FUJITSU, + .fn = 0, + }, + { + .id = NAND_MFR_NATIONAL, + .fn = 0, + }, + { + .id = NAND_MFR_RENESAS, + .fn = 0, + }, + { + .id = NAND_MFR_STMICRO, + .fn = nand_device_info_fn_stmicro, + }, + { + .id = NAND_MFR_HYNIX, + .fn = nand_device_info_fn_hynix, + }, + { + .id = NAND_MFR_MICRON, + .fn = nand_device_info_fn_micron, + }, + { + .id = NAND_MFR_AMD, + .fn = 0, + }, + { + .id = NAND_MFR_SANDISK, + .fn = nand_device_info_fn_sandisk, + }, + { + .id = NAND_MFR_INTEL, + .fn = nand_device_info_fn_intel, + }, + {0, 0} +}; + +/** + * nand_device_info_test_table - Validate a device info table. + * + * This function runs tests on the given device info table to check that it + * meets the current assumptions. + */ + +static void __init nand_device_info_test_table( + struct nand_device_info *table, const char *name) +{ + unsigned i; + unsigned j; + uint8_t mfr_code; + uint8_t device_code; + + /* Loop over entries in this table. */ + + for (i = 0; !table[i].end_of_table; i++) { + + /* Get discriminating attributes of the current device. */ + + mfr_code = table[i].manufacturer_code; + device_code = table[i].device_code; + + /* Compare with the remaining devices in this table. */ + + for (j = i + 1; !table[j].end_of_table; j++) { + if ((mfr_code == table[j].manufacturer_code) && + (device_code == table[j].device_code)) + goto error; + } + + } + + return; + +error: + + printk(KERN_EMERG + "\n== NAND Flash device info table failed validity check ==\n"); + + printk(KERN_EMERG "\nDevice Info Table: %s\n", name); + printk(KERN_EMERG "\nTable Index %u\n", i); + nand_device_print_info(table + i); + printk(KERN_EMERG "\nTable Index %u\n", j); + nand_device_print_info(table + j); + printk(KERN_EMERG "\n"); + + BUG(); + +} + +/** + * nand_device_info_test_data - Test the NAND Flash device data. + */ + +static void __init nand_device_info_test_data(void) +{ + + unsigned i; + + for (i = 0; nand_device_type_directory[i].name; i++) { + nand_device_info_test_table( + nand_device_type_directory[i].table, + nand_device_type_directory[i].name); + } + +} + +struct nand_device_info * __init nand_device_get_info(const uint8_t id[]) +{ + unsigned i; + uint8_t mfr_id = ID_GET_MFR_CODE(id); + struct nand_device_info *(*fn)(const uint8_t id[]) = 0; + + /* Test the data. */ + + nand_device_info_test_data(); + + /* Look for information about this manufacturer. */ + + for (i = 0; nand_device_mfr_directory[i].id; i++) { + if (nand_device_mfr_directory[i].id == mfr_id) { + fn = nand_device_mfr_directory[i].fn; + break; + } + } + + if (!fn) + return 0; + + /* + * If control arrives here, we found both a table of device information, + * and a function we can use to identify the current device. Attempt to + * identify the device and return the result. + */ + + return fn(id); + +} diff --git a/drivers/mtd/nand/nand_device_info.h b/drivers/mtd/nand/nand_device_info.h new file mode 100644 index 000000000000..a0960b1cc1e6 --- /dev/null +++ b/drivers/mtd/nand/nand_device_info.h @@ -0,0 +1,140 @@ +/* + * Copyright 2009-2010 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 + */ +#ifndef __DRIVERS_NAND_DEVICE_INFO_H +#define __DRIVERS_NAND_DEVICE_INFO_H + + /* + * The number of ID bytes to read from the NAND Flash device and hand over to + * the identification system. + */ + +#define NAND_DEVICE_ID_BYTE_COUNT (6) + + /* + * The number of ID bytes to read from the NAND Flash device and hand over to + * the identification system. + */ + +enum nand_device_cell_technology { + NAND_DEVICE_CELL_TECH_SLC = 0, + NAND_DEVICE_CELL_TECH_MLC = 1, +}; + +/** + * struct nand_device_info - Information about a single NAND Flash device. + * + * This structure contains all the *essential* information about a NAND Flash + * device, derived from the device's data sheet. For each manufacturer, we have + * an array of these structures. + * + * @end_of_table: If true, marks the end of a table of device + * information. + * @manufacturer_code: The manufacturer code (1st ID byte) reported by + * the device. + * @device_code: The device code (2nd ID byte) reported by the + * device. + * @cell_technology: The storage cell technology. + * @chip_size_in_bytes: The total size of the storage behind a single + * chip select, in bytes. Notice that this is *not* + * necessarily the total size of the storage in a + * *package*, which may contain several chips. + * @block_size_in_pages: The number of pages in a block. + * @page_total_size_in_bytes: The total size of a page, in bytes, including + * both the data and the OOB. + * @ecc_strength_in_bits: The strength of the ECC called for by the + * manufacturer, in number of correctable bits. + * @ecc_size_in_bytes: The size of the data block over which the + * manufacturer calls for the given ECC algorithm + * and strength. + * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the + * maximum of tDS and tWP. A negative value + * indicates this characteristic isn't known. + * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the + * maximum of tDH, tWH and tREH. A negative value + * indicates this characteristic isn't known. + * @address_setup_in_ns: The address setup time, in nanoseconds. Usually + * the maximum of tCLS, tCS and tALS. A negative + * value indicates this characteristic isn't known. + * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative + * value indicates this characteristic isn't known. + * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic + * isn't known. + * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic + * isn't known. + * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic + * isn't known. + */ + +struct nand_device_info { + + /* End of table marker */ + + bool end_of_table; + + /* Manufacturer and Device codes */ + + uint8_t manufacturer_code; + uint8_t device_code; + + /* Technology */ + + enum nand_device_cell_technology cell_technology; + + /* Geometry */ + + uint64_t chip_size_in_bytes; + uint32_t block_size_in_pages; + uint16_t page_total_size_in_bytes; + + /* ECC */ + + uint8_t ecc_strength_in_bits; + uint16_t ecc_size_in_bytes; + + /* Timing */ + + int8_t data_setup_in_ns; + int8_t data_hold_in_ns; + int8_t address_setup_in_ns; + int8_t gpmi_sample_delay_in_ns; + int8_t tREA_in_ns; + int8_t tRLOH_in_ns; + int8_t tRHOH_in_ns; + + /* Description */ + + const char *description; + +}; + +/** + * nand_device_get_info - Get info about a device based on ID bytes. + * + * @id_bytes: An array of NAND_DEVICE_ID_BYTE_COUNT ID bytes retrieved from the + * NAND Flash device. + */ + +struct nand_device_info *nand_device_get_info(const uint8_t id_bytes[]); + +/** + * nand_device_print_info - Prints information about a NAND Flash device. + * + * @info A pointer to a NAND Flash device information structure. + */ + +void nand_device_print_info(struct nand_device_info *info); + +#endif |