diff options
author | Imre Kaloz <kaloz@openwrt.org> | 2012-05-01 07:00:17 +0000 |
---|---|---|
committer | Imre Kaloz <kaloz@openwrt.org> | 2012-05-01 07:00:17 +0000 |
commit | 880de62f91bcffe7f1c7f16c9463e10853fa2524 (patch) | |
tree | 058bb1c73786a7bf492cc1a554effe4692a9b50e /target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch | |
parent | 7a8d12bafaf8b882d0278156e5f22d0a396687e2 (diff) | |
download | upstream-880de62f91bcffe7f1c7f16c9463e10853fa2524.tar.gz upstream-880de62f91bcffe7f1c7f16c9463e10853fa2524.tar.bz2 upstream-880de62f91bcffe7f1c7f16c9463e10853fa2524.zip |
switch to 2.6.38
SVN-Revision: 31546
Diffstat (limited to 'target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch')
-rw-r--r-- | target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch | 1381 |
1 files changed, 1381 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch b/target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch new file mode 100644 index 0000000000..e495ab81cb --- /dev/null +++ b/target/linux/coldfire/patches/016-Add-nand-driver-support-for-M54418TWR-board.patch @@ -0,0 +1,1381 @@ +From 6a5c2427d53c24a30f28b8061b2cda4edad37f58 Mon Sep 17 00:00:00 2001 +From: Alison Wang <b18965@freescale.com> +Date: Thu, 4 Aug 2011 09:59:43 +0800 +Subject: [PATCH 16/52] Add nand driver support for M54418TWR board + +Disable hw ECC for erase block aligned pages. + +On the erase block, there's always have clean marker for jffs2, +The NFC ECC provide a fake correction on the erase block when +there're clean marker and adjust the bits on the fly when reading back. + +Disable the hw ECC on erase block aligned pages is a workaround +for jffs2 on the NAND. + +Signed-off-by: Alison Wang <b18965@freescale.com> +--- + arch/m68k/include/asm/fsl_nfc.h | 330 +++++++++++++ + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/fsl_nfc.c | 995 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1333 insertions(+), 0 deletions(-) + create mode 100644 arch/m68k/include/asm/fsl_nfc.h + create mode 100644 drivers/mtd/nand/fsl_nfc.c + +--- /dev/null ++++ b/arch/m68k/include/asm/fsl_nfc.h +@@ -0,0 +1,330 @@ ++/* ++ * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Author: Shaohui Xie <b21989@freescale.com> ++ * ++ * Description: ++ * MPC5125/M54418TWR Nand driver. ++ * ++ * This 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. ++ */ ++ ++#ifndef MPC5125_NFC_H ++#define MPC5125_NFC_H ++ ++ ++/* NFC PAD Define */ ++#define PAD_NFC_IO PAD_FUNC0 ++#define PAD_NFC_ALE PAD_FUNC0 ++#define PAD_NFC_CLE PAD_FUNC0 ++#define PAD_NFC_WE PAD_FUNC0 ++#define PAD_NFC_RE PAD_FUNC0 ++#define PAD_NFC_CE0 PAD_FUNC0 ++#define PAD_NFC_CE1 PAD_FUNC1 ++#define PAD_NFC_CE2 PAD_FUNC2 ++#define PAD_NFC_CE3 PAD_FUNC2 ++#define PAD_NFC_RB0 PAD_FUNC0 ++#define PAD_NFC_RB1 PAD_FUNC2 ++#define PAD_NFC_RB2 PAD_FUNC2 ++#define PAD_NFC_RB3 PAD_FUNC2 ++ ++/* NFC Control PAD Define */ ++#define BALL_NFC_CE0 IOCTL_NFC_CE0_B ++#define BALL_NFC_CE1 IOCTL_SDHC1_CLK ++#define BALL_NFC_CE2 IOCTL_PSC1_4 ++#define BALL_NFC_CE3 IOCTL_J1850_TX ++#define BALL_NFC_RB0 IOCTL_NFC_RB ++#define BALL_NFC_RB1 IOCTL_FEC1_TXD_0 ++#define BALL_NFC_RB2 IOCTL_PSC1_3 ++#define BALL_NFC_RB3 IOCTL_J1850_RX ++#define BALL_NFC_ALE IOCTL_EMB_AD19 ++#define BALL_NFC_CLE IOCTL_EMB_AD18 ++#define BALL_NFC_WE IOCTL_EMB_AD16 ++#define BALL_NFC_RE IOCTL_EMB_AD17 ++ ++/* NFC IO Pad Define */ ++#define BALL_NFC_IO0 IOCTL_EMB_AD00 ++#define BALL_NFC_IO1 IOCTL_EMB_AD01 ++#define BALL_NFC_IO2 IOCTL_EMB_AD02 ++#define BALL_NFC_IO3 IOCTL_EMB_AD03 ++#define BALL_NFC_IO4 IOCTL_EMB_AD04 ++#define BALL_NFC_IO5 IOCTL_EMB_AD05 ++#define BALL_NFC_IO6 IOCTL_EMB_AD06 ++#define BALL_NFC_IO7 IOCTL_EMB_AD07 ++ ++/* Addresses for NFC MAIN RAM BUFFER areas */ ++#define NFC_MAIN_AREA(n) ((n) * 0x1000) ++ ++/* Addresses for NFC SPARE BUFFER areas */ ++#define NFC_SPARE_BUFFERS 8 ++#define NFC_SPARE_LEN 0x10 ++#define NFC_SPARE_AREA(n) (0x800 + ((n) * NFC_SPARE_LEN)) ++ ++#define PAGE_2K 0x0800 ++#define PAGE_64 0x0040 ++ ++/* MPC5125 NFC registers */ ++/* Typical Flash Commands */ ++#define READ_PAGE_CMD_CODE 0x7EE0 ++#define PROGRAM_PAGE_CMD_CODE 0x7FC0 ++#define ERASE_CMD_CODE 0x4EC0 ++#define READ_ID_CMD_CODE 0x4804 ++#define RESET_CMD_CODE 0x4040 ++#define DMA_PROGRAM_PAGE_CMD_CODE 0xFFC8 ++#define RANDOM_IN_CMD_CODE 0x7140 ++#define RANDOM_OUT_CMD_CODE 0x70E0 ++#define STATUS_READ_CMD_CODE 0x4068 ++ ++#define PAGE_READ_CMD_BYTE1 0x00 ++#define PAGE_READ_CMD_BYTE2 0x30 ++#define PROGRAM_PAGE_CMD_BYTE1 0x80 ++#define PROGRAM_PAGE_CMD_BYTE2 0x10 ++#define READ_STATUS_CMD_BYTE 0x70 ++#define ERASE_CMD_BYTE1 0x60 ++#define ERASE_CMD_BYTE2 0xD0 ++#define READ_ID_CMD_BYTE 0x90 ++#define RESET_CMD_BYTE 0xFF ++#define RANDOM_OUT_CMD_BYTE1 0x05 ++#define RANDOM_OUT_CMD_BYTE2 0xE0 ++ ++/* NFC ECC mode define */ ++#define ECC_BYPASS 0x0 ++#define ECC_8_BYTE 0x1 ++#define ECC_12_BYTE 0x2 ++#define ECC_15_BYTE 0x3 ++#define ECC_23_BYTE 0x4 ++#define ECC_30_BYTE 0x5 ++#define ECC_45_BYTE 0x6 ++#define ECC_60_BYTE 0x7 ++#define ECC_ERROR 1 ++#define ECC_RIGHT 0 ++ ++/***************** Module-Relative Register Offsets *************************/ ++#define NFC_SRAM_BUFFER 0x0000 ++#define NFC_FLASH_CMD1 0x3F00 ++#define NFC_FLASH_CMD2 0x3F04 ++#define NFC_COL_ADDR 0x3F08 ++#define NFC_ROW_ADDR 0x3F0c ++#define NFC_FLASH_COMMAND_REPEAT 0x3F10 ++#define NFC_ROW_ADDR_INC 0x3F14 ++#define NFC_FLASH_STATUS1 0x3F18 ++#define NFC_FLASH_STATUS2 0x3F1c ++#define NFC_DMA1_ADDR 0x3F20 ++#define NFC_DMA2_ADDR 0x3F34 ++#define NFC_DMA_CONFIG 0x3F24 ++#define NFC_CACHE_SWAP 0x3F28 ++#define NFC_SECTOR_SIZE 0x3F2c ++#define NFC_FLASH_CONFIG 0x3F30 ++#define NFC_IRQ_STATUS 0x3F38 ++ ++/***************** Module-Relative Register Reset Value *********************/ ++#define NFC_SRAM_BUFFER_RSTVAL 0x00000000 ++#define NFC_FLASH_CMD1_RSTVAL 0x30FF0000 ++#define NFC_FLASH_CMD2_RSTVAL 0x007EE000 ++#define NFC_COL_ADDR_RSTVAL 0x00000000 ++#define NFC_ROW_ADDR_RSTVAL 0x11000000 ++#define NFC_FLASH_COMMAND_REPEAT_RSTVAL 0x00000000 ++#define NFC_ROW_ADDR_INC_RSTVAL 0x00000001 ++#define NFC_FLASH_STATUS1_RSTVAL 0x00000000 ++#define NFC_FLASH_STATUS2_RSTVAL 0x00000000 ++#define NFC_DMA1_ADDR_RSTVAL 0x00000000 ++#define NFC_DMA2_ADDR_RSTVAL 0x00000000 ++#define NFC_DMA_CONFIG_RSTVAL 0x00000000 ++#define NFC_CACHE_SWAP_RSTVAL 0x0FFE0FFE ++#define NFC_SECTOR_SIZE_RSTVAL 0x00000420 ++#define NFC_FLASH_CONFIG_RSTVAL 0x000EA631 ++#define NFC_IRQ_STATUS_RSTVAL 0x04000000 ++ ++/***************** Module-Relative Register Mask *************************/ ++ ++/* NFC_FLASH_CMD1 Field */ ++#define CMD1_MASK 0xFFFF0000 ++#define CMD1_SHIFT 0 ++#define CMD_BYTE2_MASK 0xFF000000 ++#define CMD_BYTE2_SHIFT 24 ++#define CMD_BYTE3_MASK 0x00FF0000 ++#define CMD_BYTE3_SHIFT 16 ++ ++/* NFC_FLASH_CM2 Field */ ++#define CMD2_MASK 0xFFFFFF07 ++#define CMD2_SHIFT 0 ++#define CMD_BYTE1_MASK 0xFF000000 ++#define CMD_BYTE1_SHIFT 24 ++#define CMD_CODE_MASK 0x00FFFF00 ++#define CMD_CODE_SHIFT 8 ++#define BUFNO_MASK 0x00000006 ++#define BUFNO_SHIFT 1 ++#define BUSY_MASK 0x00000001 ++#define BUSY_SHIFT 0 ++#define START_MASK 0x00000001 ++#define START_SHIFT 0 ++ ++/* NFC_COL_ADDR Field */ ++#define COL_ADDR_MASK 0x0000FFFF ++#define COL_ADDR_SHIFT 0 ++#define COL_ADDR_COL_ADDR2_MASK 0x0000FF00 ++#define COL_ADDR_COL_ADDR2_SHIFT 8 ++#define COL_ADDR_COL_ADDR1_MASK 0x000000FF ++#define COL_ADDR_COL_ADDR1_SHIFT 0 ++ ++/* NFC_ROW_ADDR Field */ ++#define ROW_ADDR_MASK 0x00FFFFFF ++#define ROW_ADDR_SHIFT 0 ++#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 ++#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 ++#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 ++#define ROW_ADDR_CHIP_SEL_SHIFT 24 ++#define ROW_ADDR_ROW_ADDR3_MASK 0x00FF0000 ++#define ROW_ADDR_ROW_ADDR3_SHIFT 16 ++#define ROW_ADDR_ROW_ADDR2_MASK 0x0000FF00 ++#define ROW_ADDR_ROW_ADDR2_SHIFT 8 ++#define ROW_ADDR_ROW_ADDR1_MASK 0x000000FF ++#define ROW_ADDR_ROW_ADDR1_SHIFT 0 ++ ++/* NFC_FLASH_COMMAND_REPEAT Field */ ++#define COMMAND_REPEAT_MASK 0x0000FFFF ++#define COMMAND_REPEAT_SHIFT 0 ++#define COMMAND_REPEAT_REPEAT_COUNT_MASK 0x0000FFFF ++#define COMMAND_REPEAT_REPEAT_COUNT_SHIFT 0 ++ ++/* NFC_ROW_ADDR_INC Field */ ++#define ROW_ADDR_INC_MASK 0x00FFFFFF ++#define ROW_ADDR_INC_SHIFT 0 ++#define ROW_ADDR_INC_ROW_ADDR3_INC_MASK 0x00FF0000 ++#define ROW_ADDR_INC_ROW_ADDR3_INC_SHIFT 16 ++#define ROW_ADDR_INC_ROW_ADDR2_INC_MASK 0x0000FF00 ++#define ROW_ADDR_INC_ROW_ADDR2_INC_SHIFT 8 ++#define ROW_ADDR_INC_ROW_ADDR1_INC_MASK 0x000000FF ++#define ROW_ADDR_INC_ROW_ADDR1_INC_SHIFT 0 ++ ++/* NFC_FLASH_STATUS1 Field */ ++#define STATUS1_MASK 0xFFFFFFFF ++#define STATUS1_SHIFT 0 ++#define STATUS1_ID_BYTE1_MASK 0xFF000000 ++#define STATUS1_ID_BYTE1_SHIFT 24 ++#define STATUS1_ID_BYTE2_MASK 0x00FF0000 ++#define STATUS1_ID_BYTE2_SHIFT 16 ++#define STATUS1_ID_BYTE3_MASK 0x0000FF00 ++#define STATUS1_ID_BYTE3_SHIFT 8 ++#define STATUS1_ID_BYTE4_MASK 0x000000FF ++#define STATUS1_ID_BYTE4_SHIFT 0 ++ ++/* NFC_FLASH_STATUS2 Field */ ++#define STATUS2_MASK 0xFF0000FF ++#define STATUS2_SHIFT 0 ++#define STATUS2_ID_BYTE5_MASK 0xFF000000 ++#define STATUS2_ID_BYTE5_SHIFT 24 ++#define STATUS_BYTE1_MASK 0x000000FF ++#define STATUS2_STATUS_BYTE1_SHIFT 0 ++ ++/* NFC_DMA1_ADDR Field */ ++#define DMA1_ADDR_MASK 0xFFFFFFFF ++#define DMA1_ADDR_SHIFT 0 ++#define DMA1_ADDR_DMA1_ADDR_MASK 0xFFFFFFFF ++#define DMA1_ADDR_DMA1_ADDR_SHIFT 0 ++ ++/* DMA2_ADDR Field */ ++#define DMA2_ADDR_MASK 0xFFFFFFFF ++#define DMA2_ADDR_SHIFT 0 ++#define DMA2_ADDR_DMA2_ADDR_MASK 0xFFFFFFFF ++#define DMA2_ADDR_DMA2_ADDR_SHIFT 0 ++ ++/* DMA_CONFIG Field */ ++#define DMA_CONFIG_MASK 0xFFFFFFFF ++#define DMA_CONFIG_SHIFT 0 ++#define DMA_CONFIG_DMA1_CNT_MASK 0xFFF00000 ++#define DMA_CONFIG_DMA1_CNT_SHIFT 20 ++#define DMA_CONFIG_DMA2_CNT_MASK 0x000FE000 ++#define DMA_CONFIG_DMA2_CNT_SHIFT 13 ++#define DMA_CONFIG_DMA2_OFFSET_MASK 0x00001FC0 ++#define DMA_CONFIG_DMA2_OFFSET_SHIFT 2 ++#define DMA_CONFIG_DMA1_ACT_MASK 0x00000002 ++#define DMA_CONFIG_DMA1_ACT_SHIFT 1 ++#define DMA_CONFIG_DMA2_ACT_MASK 0x00000001 ++#define DMA_CONFIG_DMA2_ACT_SHIFT 0 ++ ++/* NFC_CACHE_SWAP Field */ ++#define CACHE_SWAP_MASK 0x0FFE0FFE ++#define CACHE_SWAP_SHIFT 1 ++#define CACHE_SWAP_CACHE_SWAP_ADDR2_MASK 0x0FFE0000 ++#define CACHE_SWAP_CACHE_SWAP_ADDR2_SHIFT 17 ++#define CACHE_SWAP_CACHE_SWAP_ADDR1_MASK 0x00000FFE ++#define CACHE_SWAP_CACHE_SWAP_ADDR1_SHIFT 1 ++ ++/* NFC_SECTOR_SIZE Field */ ++#define SECTOR_SIZE_MASK 0x00001FFF ++#define SECTOR_SIZE_SHIFT 0 ++#define SECTOR_SIZE_SECTOR_SIZE_MASK 0x00001FFF ++#define SECTOR_SIZE_SECTOR_SIZE_SHIFT 0 ++ ++/* NFC_FLASH_CONFIG Field */ ++#define CONFIG_MASK 0xFFFFFFFF ++#define CONFIG_SHIFT 0 ++#define CONFIG_STOP_ON_WERR_MASK 0x80000000 ++#define CONFIG_STOP_ON_WERR_SHIFT 31 ++#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 ++#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 ++#define CONFIG_ECC_SRAM_REQ_MASK 0x00200000 ++#define CONFIG_ECC_SRAM_REQ_SHIFT 21 ++#define CONFIG_DMA_REQ_MASK 0x00100000 ++#define CONFIG_DMA_REQ_SHIFT 20 ++#define CONFIG_ECC_MODE_MASK 0x000E0000 ++#define CONFIG_ECC_MODE_SHIFT 17 ++#define CONFIG_FAST_FLASH_MASK 0x00010000 ++#define CONFIG_FAST_FLASH_SHIFT 16 ++#define CONFIG_ID_COUNT_MASK 0x0000E000 ++#define CONFIG_ID_COUNT_SHIFT 13 ++#define CONFIG_CMD_TIMEOUT_MASK 0x00001F00 ++#define CONFIG_CMD_TIMEOUT_SHIFT 8 ++#define CONFIG_16BIT_MASK 0x00000080 ++#define CONFIG_16BIT_SHIFT 7 ++#define CONFIG_BOOT_MODE_MASK 0x00000040 ++#define CONFIG_BOOT_MODE_SHIFT 6 ++#define CONFIG_ADDR_AUTO_INCR_MASK 0x00000020 ++#define CONFIG_ADDR_AUTO_INCR_SHIFT 5 ++#define CONFIG_BUFNO_AUTO_INCR_MASK 0x00000010 ++#define CONFIG_BUFNO_AUTO_INCR_SHIFT 4 ++#define CONFIG_PAGE_CNT_MASK 0x0000000F ++#define CONFIG_PAGE_CNT_SHIFT 0 ++ ++/* NFC_IRQ_STATUS Field */ ++#define MASK 0xEFFC003F ++#define SHIFT 0 ++#define WERR_IRQ_MASK 0x80000000 ++#define WERR_IRQ_SHIFT 31 ++#define CMD_DONE_IRQ_MASK 0x40000000 ++#define CMD_DONE_IRQ_SHIFT 30 ++#define IDLE_IRQ_MASK 0x20000000 ++#define IDLE_IRQ_SHIFT 29 ++#define WERR_STATUS_MASK 0x08000000 ++#define WERR_STATUS_SHIFT 27 ++#define FLASH_CMD_BUSY_MASK 0x04000000 ++#define FLASH_CMD_BUSY_SHIFT 26 ++#define RESIDUE_BUSY_MASK 0x02000000 ++#define RESIDUE_BUSY_SHIFT 25 ++#define ECC_BUSY_MASK 0x01000000 ++#define ECC_BUSY_SHIFT 24 ++#define DMA_BUSY_MASK 0x00800000 ++#define DMA_BUSY_SHIFT 23 ++#define WERR_EN_MASK 0x00400000 ++#define WERR_EN_SHIFT 22 ++#define CMD_DONE_EN_MASK 0x00200000 ++#define CMD_DONE_EN_SHIFT 21 ++#define IDLE_EN_MASK 0x00100000 ++#define IDLE_EN_SHIFT 20 ++#define WERR_CLEAR_MASK 0x00080000 ++#define WERR_CLEAR_SHIFT 19 ++#define CMD_DONE_CLEAR_MASK 0x00040000 ++#define CMD_DONE_CLEAR_SHIFT 18 ++#define IDLE_CLEAR_MASK 0x00020000 ++#define IDLE_CLEAR_SHIFT 17 ++#define RESIDUE_BUFF_NO_MASK 0x00000030 ++#define RESIDUE_BUFF_NO_SHIFT 4 ++#define ECC_BUFF_NO_MASK 0x000000C0 ++#define ECC_BUFF_NO_SHIFT 2 ++#define DMA_BUFF_NO_MASK 0x00000003 ++ ++#endif /* MPC5125_NFC_H */ +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -474,6 +474,13 @@ config MTD_NAND_MPC5121_NFC + This enables the driver for the NAND flash controller on the + MPC5121 SoC. + ++config MTD_NAND_FSL_NFC ++ tristate "Support for NAND on Freescale ColdFire NFC" ++ depends on MTD_NAND && M5441X ++ help ++ Enables support for NAND Flash chips wired onto Freescale PowerPC ++ processor localbus with User-Programmable Machine support. ++ + config MTD_NAND_MXC + tristate "MXC NAND support" + depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi + 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_FSL_NFC) += fsl_nfc.o + obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o + obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o + obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o +--- /dev/null ++++ b/drivers/mtd/nand/fsl_nfc.c +@@ -0,0 +1,995 @@ ++/* ++ * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Author: Shaohui Xie <b21989@freescale.com> ++ * Jason Jin <Jason.jin@freescale.com> ++ * ++ * Description: ++ * MPC5125 Nand driver. ++ * Jason ported to M54418TWR. ++ * ++ * Based on original driver mpc5121_nfc.c. ++ * ++ * This 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. ++ */ ++ ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <asm/fsl_nfc.h> ++#include <asm/mcfsim.h> ++ ++#define DRV_NAME "fsl_nfc" ++#define DRV_VERSION "0.5" ++ ++/* Timeouts */ ++#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ ++#define NFC_TIMEOUT (HZ) ++ ++ ++#define ECC_SRAM_ADDR (0x840 >> 3) ++#define ECC_STATUS_MASK 0x80 ++#define ECC_ERR_COUNT 0x3F ++ ++#define MIN(x, y) ((x < y) ? x : y) ++ ++#ifdef CONFIG_MTD_NAND_FSL_NFC_SWECC ++static int hardware_ecc; ++#else ++static int hardware_ecc = 1; ++#endif ++ ++ ++struct fsl_nfc_prv { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ int irq; ++ void __iomem *regs; ++ struct clk *clk; ++ wait_queue_head_t irq_waitq; ++ uint column; ++ int spareonly; ++ int page; ++}; ++ ++static int get_status; ++static int get_id; ++ ++static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; ++static u8 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 = 11, ++ .len = 4, ++ .veroffs = 15, ++ .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 = 11, ++ .len = 4, ++ .veroffs = 15, ++ .maxblocks = 4, ++ .pattern = mirror_pattern, ++}; ++ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *fsl_nfc_pprobes[] = { "cmdlinepart", NULL }; ++#endif ++#if 0 ++static struct nand_ecclayout nand_hw_eccoob_512 = { ++ .eccbytes = 8, ++ .eccpos = { ++ 8, 9, 10, 11, 12, 13, 14, 15, ++ }, ++ .oobfree = { ++ {0, 5} /* byte 5 is factory bad block marker */ ++ }, ++}; ++#endif ++ ++static struct nand_ecclayout fsl_nfc_ecc45 = { ++ .eccbytes = 45, ++ .eccpos = {19, 20, 21, 22, 23, ++ 24, 25, 26, 27, 28, 29, 30, 31, ++ 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63}, ++ .oobfree = { ++ {.offset = 8, ++ .length = 11} } ++}; ++ ++ ++#if 0 ++static struct nand_ecclayout nand_hw_eccoob_2k = { ++ .eccbytes = 32, ++ .eccpos = { ++ /* 8 bytes of ecc for each 512 bytes of data */ ++ 8, 9, 10, 11, 12, 13, 14, 15, ++ 24, 25, 26, 27, 28, 29, 30, 31, ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ }, ++ .oobfree = { ++ {2, 5}, /* bytes 0 and 1 are factory bad block markers */ ++ {16, 7}, ++ {32, 7}, ++ {48, 7}, ++ }, ++}; ++ ++ ++/* ecc struct for nand 5125 */ ++static struct nand_ecclayout nand5125_hw_eccoob_2k = { ++ .eccbytes = 60, ++ .eccpos = { ++ /* 60 bytes of ecc for one page bytes of data */ ++ 4, 5, ++ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ++ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ++ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, ++ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, ++ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ }, ++ .oobfree = { ++ {2, 2}, /* bytes 0 and 1 are factory bad block markers */ ++ }, ++}; ++#endif ++ ++static inline u32 nfc_read(struct mtd_info *mtd, uint reg) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ return in_be32(prv->regs + reg); ++} ++ ++/* Write NFC register */ ++static inline void nfc_write(struct mtd_info *mtd, uint reg, u32 val) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ out_be32(prv->regs + reg, val); ++} ++ ++/* Set bits in NFC register */ ++static inline void nfc_set(struct mtd_info *mtd, uint reg, u32 bits) ++{ ++ nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); ++} ++ ++/* Clear bits in NFC register */ ++static inline void nfc_clear(struct mtd_info *mtd, uint reg, u32 bits) ++{ ++ nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); ++} ++ ++static inline void ++nfc_set_field(struct mtd_info *mtd, u32 reg, u32 mask, u32 shift, u32 val) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ out_be32(prv->regs + reg, ++ (in_be32(prv->regs + reg) & (~mask)) ++ | val << shift); ++} ++ ++static inline int ++nfc_get_field(struct mtd_info *mtd, u32 reg, u32 field_mask) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ return in_be32(prv->regs + reg) & field_mask; ++} ++ ++static inline u8 nfc_check_status(struct mtd_info *mtd) ++{ ++ u8 fls_status = 0; ++ fls_status = nfc_get_field(mtd, NFC_FLASH_STATUS2, STATUS_BYTE1_MASK); ++ return fls_status; ++} ++ ++/* clear cmd_done and cmd_idle falg for the coming command */ ++static void fsl_nfc_clear(struct mtd_info *mtd) ++{ ++ nfc_write(mtd, NFC_IRQ_STATUS, 1 << CMD_DONE_CLEAR_SHIFT); ++ nfc_write(mtd, NFC_IRQ_STATUS, 1 << IDLE_CLEAR_SHIFT); ++} ++ ++/* Wait for operation complete */ ++static void fsl_nfc_done(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ int rv; ++ ++ nfc_set(mtd, NFC_IRQ_STATUS, IDLE_EN_MASK); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, START_MASK, ++ START_SHIFT, 1); ++ ++ if (!nfc_get_field(mtd, NFC_IRQ_STATUS, IDLE_IRQ_MASK)) { ++ rv = wait_event_timeout(prv->irq_waitq, ++ nfc_get_field(mtd, NFC_IRQ_STATUS, ++ IDLE_IRQ_MASK), NFC_TIMEOUT); ++ if (!rv) ++ printk(KERN_DEBUG DRV_NAME ++ ": Timeout while waiting for BUSY.\n"); ++ } ++ fsl_nfc_clear(mtd); ++} ++ ++static inline u8 fsl_nfc_get_id(struct mtd_info *mtd, int col) ++{ ++ u32 flash_id1 = 0; ++ u8 *pid; ++ ++ flash_id1 = nfc_read(mtd, NFC_FLASH_STATUS1); ++ pid = (u8 *)&flash_id1; ++ ++ return *(pid + col); ++} ++ ++static inline u8 fsl_nfc_get_status(struct mtd_info *mtd) ++{ ++ u32 flash_status = 0; ++ u8 *pstatus; ++ ++ flash_status = nfc_read(mtd, NFC_FLASH_STATUS2); ++ pstatus = (u8 *)&flash_status; ++ ++ return *(pstatus + 3); ++} ++ ++/* Invoke command cycle */ ++static inline void ++fsl_nfc_send_cmd(struct mtd_info *mtd, u32 cmd_byte1, ++ u32 cmd_byte2, u32 cmd_code) ++{ ++ fsl_nfc_clear(mtd); ++ nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_BYTE1_MASK, ++ CMD_BYTE1_SHIFT, cmd_byte1); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD1, CMD_BYTE2_MASK, ++ CMD_BYTE2_SHIFT, cmd_byte2); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, BUFNO_MASK, ++ BUFNO_SHIFT, 0); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_CODE_MASK, ++ CMD_CODE_SHIFT, cmd_code); ++ ++ if (cmd_code == RANDOM_OUT_CMD_CODE) ++ nfc_set_field(mtd, NFC_FLASH_CMD2, BUFNO_MASK, ++ BUFNO_SHIFT, 1); ++} ++ ++/* Receive ID and status from NAND flash */ ++static inline void ++fsl_nfc_send_one_byte(struct mtd_info *mtd, u32 cmd_byte1, u32 cmd_code) ++{ ++ fsl_nfc_clear(mtd); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_BYTE1_MASK, ++ CMD_BYTE1_SHIFT, cmd_byte1); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, BUFNO_MASK, ++ BUFNO_SHIFT, 0); ++ ++ nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_CODE_MASK, ++ CMD_CODE_SHIFT, cmd_code); ++} ++ ++/* NFC interrupt handler */ ++static irqreturn_t ++fsl_nfc_irq(int irq, void *data) ++{ ++ struct mtd_info *mtd = data; ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ nfc_clear(mtd, NFC_IRQ_STATUS, IDLE_EN_MASK); ++ wake_up(&prv->irq_waitq); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Do address cycle(s) */ ++static void ++fsl_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) ++{ ++ ++ if (column != -1) { ++ nfc_set_field(mtd, NFC_COL_ADDR, ++ COL_ADDR_MASK, ++ COL_ADDR_SHIFT, column); ++ } ++ ++ if (page != -1) { ++ nfc_set_field(mtd, NFC_ROW_ADDR, ++ ROW_ADDR_MASK, ++ ROW_ADDR_SHIFT, page); ++ } ++ ++ /* DMA Disable */ ++ nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_MASK); ++ ++ /* PAGE_CNT = 1 */ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, ++ CONFIG_PAGE_CNT_SHIFT, 0x1); ++} ++ ++/* Control chips select signal on m54418twr board */ ++static void ++m54418twr_select_chip(struct mtd_info *mtd, int chip) ++{ ++ if (chip < 0) { ++ MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & ++ MCF_GPIO_PAR_FBCTL_TA_MASK); ++ MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_TS | ++ MCF_GPIO_PAR_FBCTL_TA_TA; ++ ++ MCF_GPIO_PAR_BE = ++ MCF_GPIO_PAR_BE_BE3_BE3 | MCF_GPIO_PAR_BE_BE2_BE2 | ++ MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; ++ ++ MCF_GPIO_PAR_CS &= ~MCF_GPIO_PAR_CS_CS1_NFC_CE; ++ MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS0_CS0; ++ return; ++ } ++ ++ MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & ++ MCF_GPIO_PAR_FBCTL_TA_MASK); ++ MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_ALE | ++ MCF_GPIO_PAR_FBCTL_TA_NFC_RB; ++ MCF_GPIO_PAR_BE = MCF_GPIO_PAR_BE_BE3_FB_A1 | ++ MCF_GPIO_PAR_BE_BE2_FB_A0 | ++ MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; ++ ++ MCF_GPIO_PAR_CS &= (MCF_GPIO_PAR_BE_BE3_MASK & ++ MCF_GPIO_PAR_BE_BE2_MASK); ++ MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS1_NFC_CE; ++ return; ++} ++ ++/* Read NAND Ready/Busy signal */ ++static int ++fsl_nfc_dev_ready(struct mtd_info *mtd) ++{ ++ /* ++ * NFC handles ready/busy signal internally. Therefore, this function ++ * always returns status as ready. ++ */ ++ return 1; ++} ++ ++/* Write command to NAND flash */ ++static void ++fsl_nfc_command(struct mtd_info *mtd, unsigned command, ++ int column, int page) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ prv->column = (column >= 0) ? column : 0; ++ prv->spareonly = 0; ++ get_id = 0; ++ get_status = 0; ++ ++ if (page != -1) ++ prv->page = page; ++ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE); ++ ++ if (!(page%0x40)) { ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); ++ } ++ ++ switch (command) { ++ case NAND_CMD_PAGEPROG: ++ if (!(prv->page%0x40)) ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); ++ ++ fsl_nfc_send_cmd(mtd, ++ PROGRAM_PAGE_CMD_BYTE1, ++ PROGRAM_PAGE_CMD_BYTE2, ++ PROGRAM_PAGE_CMD_CODE); ++ break; ++ /* ++ * NFC does not support sub-page reads and writes, ++ * so emulate them using full page transfers. ++ */ ++ case NAND_CMD_READ0: ++ column = 0; ++ goto read0; ++ break; ++ ++ case NAND_CMD_READ1: ++ prv->column += 256; ++ command = NAND_CMD_READ0; ++ column = 0; ++ goto read0; ++ break; ++ ++ case NAND_CMD_READOOB: ++ prv->spareonly = 1; ++ command = NAND_CMD_READ0; ++ column = 0; ++read0: ++ fsl_nfc_send_cmd(mtd, ++ PAGE_READ_CMD_BYTE1, ++ PAGE_READ_CMD_BYTE2, ++ READ_PAGE_CMD_CODE); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ fsl_nfc_command(mtd, NAND_CMD_READ0, column, page); ++ column = 0; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ fsl_nfc_send_cmd(mtd, ++ ERASE_CMD_BYTE1, ++ ERASE_CMD_BYTE2, ++ ERASE_CMD_CODE); ++ break; ++ case NAND_CMD_ERASE2: ++ return; ++ case NAND_CMD_READID: ++ get_id = 1; ++ fsl_nfc_send_one_byte(mtd, command, READ_ID_CMD_CODE); ++ break; ++ case NAND_CMD_STATUS: ++ get_status = 1; ++ fsl_nfc_send_one_byte(mtd, command, STATUS_READ_CMD_CODE); ++ break; ++ case NAND_CMD_RNDOUT: ++ fsl_nfc_send_cmd(mtd, ++ RANDOM_OUT_CMD_BYTE1, ++ RANDOM_OUT_CMD_BYTE2, ++ RANDOM_OUT_CMD_CODE); ++ break; ++ case NAND_CMD_RESET: ++ fsl_nfc_send_one_byte(mtd, command, RESET_CMD_CODE); ++ break; ++ default: ++ return; ++ } ++ ++ fsl_nfc_addr_cycle(mtd, column, page); ++ ++ fsl_nfc_done(mtd); ++} ++ ++/* Copy data from/to NFC spare buffers. */ ++static void ++fsl_nfc_copy_spare(struct mtd_info *mtd, uint offset, ++ u8 *buffer, uint size, int wr) ++{ ++ struct nand_chip *nand = mtd->priv; ++ struct fsl_nfc_prv *prv = nand->priv; ++ uint o, s, sbsize, blksize; ++ ++ /* ++ * NAND spare area is available through NFC spare buffers. ++ * The NFC divides spare area into (page_size / 512) chunks. ++ * Each chunk is placed into separate spare memory area, using ++ * first (spare_size / num_of_chunks) bytes of the buffer. ++ * ++ * For NAND device in which the spare area is not divided fully ++ * by the number of chunks, number of used bytes in each spare ++ * buffer is rounded down to the nearest even number of bytes, ++ * and all remaining bytes are added to the last used spare area. ++ * ++ * For more information read section 26.6.10 of MPC5121e ++ * Microcontroller Reference Manual, Rev. 3. ++ */ ++ ++ /* Calculate number of valid bytes in each spare buffer */ ++/* sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1;*/ ++ sbsize = (mtd->oobsize / (mtd->writesize / 2048)) & ~1; ++ ++ ++ while (size) { ++ /* Calculate spare buffer number */ ++ s = offset / sbsize; ++ if (s > NFC_SPARE_BUFFERS - 1) ++ s = NFC_SPARE_BUFFERS - 1; ++ ++ /* ++ * Calculate offset to requested data block in selected spare ++ * buffer and its size. ++ */ ++ o = offset - (s * sbsize); ++ blksize = min(sbsize - o, size); ++ ++ if (wr) ++ memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, ++ buffer, blksize); ++ else { ++ memcpy_fromio(buffer, ++ prv->regs + NFC_SPARE_AREA(s) + o, blksize); ++ } ++ ++ buffer += blksize; ++ offset += blksize; ++ size -= blksize; ++ }; ++} ++ ++/* Copy data from/to NFC main and spare buffers */ ++static void ++fsl_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, int wr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ uint c = prv->column; ++ uint l; ++ ++ /* Handle spare area access */ ++ if (prv->spareonly || c >= mtd->writesize) { ++ /* Calculate offset from beginning of spare area */ ++ if (c >= mtd->writesize) ++ c -= mtd->writesize; ++ ++ prv->column += len; ++ fsl_nfc_copy_spare(mtd, c, buf, len, wr); ++ return; ++ } ++ ++ /* ++ * Handle main area access - limit copy length to prevent ++ * crossing main/spare boundary. ++ */ ++ l = min((uint)len, mtd->writesize - c); ++ prv->column += l; ++ ++ if (wr) ++ memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); ++ else { ++ if (get_status) { ++ get_status = 0; ++ *buf = fsl_nfc_get_status(mtd); ++ } else if (l == 1 && c <= 3 && get_id) { ++ *buf = fsl_nfc_get_id(mtd, c); ++ } else ++ memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); ++ } ++ ++ /* Handle crossing main/spare boundary */ ++ if (l != len) { ++ buf += l; ++ len -= l; ++ fsl_nfc_buf_copy(mtd, buf, len, wr); ++ } ++} ++ ++/* Read data from NFC buffers */ ++static void ++fsl_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ fsl_nfc_buf_copy(mtd, buf, len, 0); ++} ++ ++/* Write data to NFC buffers */ ++static void ++fsl_nfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ fsl_nfc_buf_copy(mtd, (u_char *)buf, len, 1); ++} ++ ++/* Compare buffer with NAND flash */ ++static int ++fsl_nfc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ u_char tmp[256]; ++ uint bsize; ++ ++ while (len) { ++ bsize = min(len, 256); ++ fsl_nfc_read_buf(mtd, tmp, bsize); ++ ++ if (memcmp(buf, tmp, bsize)) ++ return 1; ++ ++ buf += bsize; ++ len -= bsize; ++ } ++ ++ return 0; ++} ++ ++/* Read byte from NFC buffers */ ++static u8 ++fsl_nfc_read_byte(struct mtd_info *mtd) ++{ ++ u8 tmp; ++ fsl_nfc_read_buf(mtd, &tmp, sizeof(tmp)); ++ return tmp; ++} ++ ++/* Read word from NFC buffers */ ++static u16 ++fsl_nfc_read_word(struct mtd_info *mtd) ++{ ++ u16 tmp; ++ fsl_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); ++ return tmp; ++} ++ ++#if 0 ++static void fsl_nfc_check_ecc_status(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ u8 ecc_status, ecc_count; ++ ++ ecc_status = *(u8 *)(prv->regs + ECC_SRAM_ADDR * 8 + 7); ++ ecc_count = ecc_status & ECC_ERR_COUNT; ++ if (ecc_status & ECC_STATUS_MASK) { ++ /*mtd->ecc_stats.failed++;*/ ++ printk("ECC failed to correct all errors!\n"); ++ } else if (ecc_count) { ++ /*mtd->ecc_stats.corrected += ecc_count;*/ ++ printk(KERN_INFO"ECC corrected %d errors\n", ecc_count); ++ } ++ ++} ++#endif ++ ++static void ++copy_from_to_spare(struct mtd_info *mtd, void *pbuf, int len, int wr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ int i, copy_count, copy_size; ++ ++/* copy_count = mtd->writesize / 512;*/ ++ copy_count = mtd->writesize / 2048; ++ /* ++ * Each spare area has 16 bytes for 512, 2K and normal 4K nand. ++ * For 4K nand with large 218 byte spare size, the size is 26 bytes for ++ * the first 7 buffers and 36 for the last. ++ */ ++/* copy_size = mtd->oobsize == 218 ? 26 : 16;*/ ++ copy_size = 64; ++ ++ /* ++ * Each spare area has 16 bytes for 512, 2K and normal 4K nand. ++ * For 4K nand with large 218 byte spare size, the size is 26 ++ * bytes for the first 7 buffers and 36 for the last. ++ */ ++ for (i = 0; i < copy_count - 1 && len > 0; i++) { ++ if (wr) ++ memcpy_toio(prv->regs + NFC_SPARE_AREA(i), ++ pbuf, MIN(len, copy_size)); ++ else ++ memcpy_fromio(pbuf, prv->regs + NFC_SPARE_AREA(i), ++ MIN(len, copy_size)); ++ pbuf += copy_size; ++ len -= copy_size; ++ } ++ if (len > 0) { ++ if (wr) ++ memcpy_toio(prv->regs + NFC_SPARE_AREA(i), ++ pbuf, len); ++ else ++ memcpy_fromio(pbuf, ++ prv->regs + NFC_SPARE_AREA(i), len); ++ } ++} ++ ++ ++static int fsl_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page, int sndcmd) ++{ ++ fsl_nfc_command(mtd, NAND_CMD_READ0, 0, page); ++ ++ copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 0); ++ return 0; ++} ++ ++static int fsl_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ fsl_nfc_command(mtd, NAND_CMD_READ0, 0, page); ++ /* copy the oob data */ ++ copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 1); ++ fsl_nfc_command(mtd, NAND_CMD_PAGEPROG, 0, page); ++ return 0; ++} ++ ++static int fsl_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int page) ++{ ++ struct fsl_nfc_prv *prv = chip->priv; ++ /*fsl_nfc_check_ecc_status(mtd);*/ ++ ++ memcpy_fromio((void *)buf, prv->regs + NFC_MAIN_AREA(0), ++ mtd->writesize); ++ copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 0); ++ return 0; ++} ++ ++static void fsl_nfc_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf) ++{ ++ struct fsl_nfc_prv *prv = chip->priv; ++ memcpy_toio(prv->regs + NFC_MAIN_AREA(0), buf, mtd->writesize); ++ copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 1); ++} ++ ++static void fsl_nfc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ return; ++} ++ ++/* Free driver resources */ ++static void ++fsl_nfc_free(struct platform_device *dev, struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ kfree(prv); ++} ++ ++static int __devinit ++fsl_nfc_probe(struct platform_device *pdev) ++{ ++ struct fsl_nfc_prv *prv; ++ struct resource *res; ++ struct mtd_info *mtd; ++#ifdef CONFIG_MTD_PARTITIONS ++ struct mtd_partition *parts; ++#endif ++ struct nand_chip *chip; ++ unsigned long regs_paddr, regs_size; ++ int retval = 0; ++ ++ prv = kzalloc(sizeof(*prv), GFP_KERNEL); ++ if (!prv) { ++ printk(KERN_ERR DRV_NAME ": Memory exhausted!\n"); ++ return -ENOMEM; ++ } ++ mtd = &prv->mtd; ++ chip = &prv->chip; ++ ++ mtd->priv = chip; ++ chip->priv = prv; ++ ++ prv->irq = platform_get_irq(pdev, 0); ++ if (prv->irq <= 0) { ++ printk(KERN_ERR DRV_NAME ": Error mapping IRQ!\n"); ++ return -EINVAL; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ printk(KERN_ERR "%s platform_get_resource MEM failed %x\n", ++ __func__, (unsigned int)res); ++ retval = -ENOMEM; ++ goto error; ++ } ++ regs_paddr = res->start; ++ regs_size = res->end - res->start + 1; ++ ++#if 0 ++ if (!request_mem_region(regs_paddr, regs_size, DRV_NAME)) { ++ printk(KERN_ERR DRV_NAME ": Error requesting memory region!\n"); ++ return -EBUSY; ++ } ++ ++ prv->regs = ioremap(regs_paddr, regs_size); ++#endif ++ prv->regs = (void __iomem *)regs_paddr; ++ if (!prv->regs) { ++ printk(KERN_ERR DRV_NAME ": Error mapping memory region!\n"); ++ return -ENOMEM; ++ } ++ ++ mtd->name = "NAND"; ++ mtd->writesize = 2048; ++ mtd->oobsize = 64; ++ ++ chip->dev_ready = fsl_nfc_dev_ready; ++ chip->cmdfunc = fsl_nfc_command; ++ chip->read_byte = fsl_nfc_read_byte; ++ chip->read_word = fsl_nfc_read_word; ++ chip->read_buf = fsl_nfc_read_buf; ++ chip->write_buf = fsl_nfc_write_buf; ++ chip->verify_buf = fsl_nfc_verify_buf; ++ chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT | ++ NAND_BUSWIDTH_16 | NAND_CACHEPRG; ++ ++ chip->select_chip = m54418twr_select_chip; ++ ++ if (hardware_ecc) { ++ chip->ecc.read_page = fsl_nfc_read_page; ++ chip->ecc.write_page = fsl_nfc_write_page; ++ chip->ecc.read_oob = fsl_nfc_read_oob; ++ chip->ecc.write_oob = fsl_nfc_write_oob; ++ chip->ecc.layout = &fsl_nfc_ecc45; ++ ++ /* propagate ecc.layout to mtd_info */ ++ mtd->ecclayout = chip->ecc.layout; ++ chip->ecc.calculate = NULL; ++ chip->ecc.hwctl = fsl_nfc_enable_hwecc; ++ chip->ecc.correct = NULL; ++ chip->ecc.mode = NAND_ECC_HW; ++ /* RS-ECC is applied for both MAIN+SPARE not MAIN alone */ ++ chip->ecc.steps = 1; ++ chip->ecc.bytes = 45; ++ chip->ecc.size = 0x800; ++ ++ /* set ECC mode = ECC_45_BYTE */ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE); ++ /* set ECC_STATUS write position */ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_SRAM_ADDR_MASK, ++ CONFIG_ECC_SRAM_ADDR_SHIFT, ECC_SRAM_ADDR); ++ /* enable ECC_STATUS results write */ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_SRAM_REQ_MASK, ++ CONFIG_ECC_SRAM_REQ_SHIFT, 1); ++ } else { ++ chip->ecc.mode = NAND_ECC_SOFT; ++ /* set ECC BY_PASS */ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); ++ } ++ chip->bbt_td = &bbt_main_descr; ++ chip->bbt_md = &bbt_mirror_descr; ++ bbt_main_descr.pattern = bbt_pattern; ++ bbt_mirror_descr.pattern = mirror_pattern; ++ ++ init_waitqueue_head(&prv->irq_waitq); ++ retval = request_irq(prv->irq, fsl_nfc_irq, IRQF_DISABLED, ++ DRV_NAME, mtd); ++ if (retval) { ++ printk(KERN_ERR DRV_NAME ": Error requesting IRQ!\n"); ++ goto error; ++ } ++ ++ /* SET SECTOR SIZE */ ++ nfc_write(mtd, NFC_SECTOR_SIZE, PAGE_2K | PAGE_64); ++ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_ADDR_AUTO_INCR_MASK, ++ CONFIG_ADDR_AUTO_INCR_SHIFT, 0); ++ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_BUFNO_AUTO_INCR_MASK, ++ CONFIG_BUFNO_AUTO_INCR_SHIFT, 0); ++ /* SET FAST_FLASH = 1 */ ++#if 0 ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_FAST_FLASH_MASK, ++ CONFIG_FAST_FLASH_SHIFT, 1); ++#endif ++ ++ nfc_set_field(mtd, NFC_FLASH_CONFIG, ++ CONFIG_16BIT_MASK, ++ CONFIG_16BIT_SHIFT, 1); ++ ++ ++ /* Detect NAND chips */ ++ if (nand_scan(mtd, 1)) { ++ printk(KERN_ERR DRV_NAME ": NAND Flash not found !\n"); ++ free_irq(prv->irq, mtd); ++ retval = -ENXIO; ++ goto error; ++ } ++ ++ platform_set_drvdata(pdev, mtd); ++ ++ /* Register device in MTD */ ++#ifdef CONFIG_MTD_PARTITIONS ++ retval = parse_mtd_partitions(mtd, fsl_nfc_pprobes, &parts, 0); ++ if (retval < 0) { ++ printk(KERN_ERR DRV_NAME ": Error parsing MTD partitions!\n"); ++ free_irq(prv->irq, mtd); ++ retval = -EINVAL; ++ goto error; ++ } ++ ++ printk(KERN_DEBUG "parse partition: partnr = %d\n", retval); ++ ++ if (retval > 0) ++ retval = add_mtd_partitions(mtd, parts, retval); ++ else ++#endif ++ retval = add_mtd_device(mtd); ++ ++ if (retval) { ++ printk(KERN_ERR DRV_NAME ": Error adding MTD device!\n"); ++ free_irq(prv->irq, mtd); ++ goto error; ++ } ++ ++ return 0; ++error: ++ fsl_nfc_free(pdev, mtd); ++ return retval; ++} ++ ++static int __exit ++fsl_nfc_remove(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd = platform_get_drvdata(pdev); ++ struct nand_chip *chip = mtd->priv; ++ struct fsl_nfc_prv *prv = chip->priv; ++ ++ nand_release(mtd); ++ free_irq(prv->irq, mtd); ++ fsl_nfc_free(pdev, mtd); ++ ++ return 0; ++} ++ ++static struct platform_driver fsl_nfc_driver = { ++ .probe = fsl_nfc_probe, ++ .remove = __exit_p(fsl_nfc_remove), ++ .suspend = NULL, ++ .resume = NULL, ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init fsl_nfc_init(void) ++{ ++ pr_info("FSL NFC MTD nand Driver %s\n", DRV_VERSION); ++ if (platform_driver_register(&fsl_nfc_driver) != 0) { ++ printk(KERN_ERR DRV_NAME ": Driver register failed!\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static void __exit fsl_nfc_cleanup(void) ++{ ++ platform_driver_unregister(&fsl_nfc_driver); ++} ++ ++module_init(fsl_nfc_init); ++module_exit(fsl_nfc_cleanup); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("FSL NFC NAND MTD driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); |