📄 mxc_nd.c
字号:
/* * Copyright 2004-2006 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 <asm/mach/flash.h>#include <asm/io.h>#include "mxc_nd.h"/*! * Number of static partitions on NAND Flash. */#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(struct mtd_partition))/*! * The version of this driver */#define DVR_VER "2.0"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 = NULL;/*! * Define delays in microsec for NAND device operations */#define TROP_US_DELAY 2000/*! * \def BITPOS(x) * Macros to get byte and bit positions of ECC *//*! * Macros to get byte and bit positions of ECC */#define COLPOS(x) ((x) >> 3)#define BITPOS(x) ((x)& 0xf)/*! * \def SPARE_SINGLEBIT_ERROR * Define single bit Error positions in Main & Spare area *//*! Define single bit Error positions in Main & Spare area */#define MAIN_SINGLEBIT_ERROR 0x4#define SPARE_SINGLEBIT_ERROR 0x1struct nand_info { bool bSpareOnly; bool bStatusRequest; u16 colAddr;};static struct nand_info g_nandfc_info;#ifdef CONFIG_MTD_NAND_MXC_SWECCstatic int hardware_ecc = 0;#elsestatic int hardware_ecc = 1;#endif#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2static int Ecc_disabled;#endifstatic int is2k_Pagesize = 0;static struct clk *nfc_clk;/* * OOB placement block for use with hardware ecc generation */static struct nand_ecclayout nand_hw_eccoob_8 = { .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} }};static struct nand_ecclayout nand_hw_eccoob_16 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 6}, {12, 4}}};/*! * @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_PARTITIONSstatic const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL };#endifstatic 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 = MAIN_AREA0 + (col >> 1) + (512 * buf_id); } else { buf = SPARE_AREA0 + (col >> 1) + (16 * 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 = 0, lastErrSpare = 0; /* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -