⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fsl_elbc_nand.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Freescale Enhanced Local Bus Controller NAND driver * * Copyright (c) 2006-2007 Freescale Semiconductor * * Authors: Nick Spence <nick.spence@freescale.com>, *          Scott Wood <scottwood@freescale.com> * * 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 */#include <linux/module.h>#include <linux/types.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/of_platform.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <linux/mtd/partitions.h>#include <asm/io.h>#include <asm/fsl_lbc.h>#define MAX_BANKS 8#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */struct fsl_elbc_ctrl;/* mtd information per set */struct fsl_elbc_mtd {	struct mtd_info mtd;	struct nand_chip chip;	struct fsl_elbc_ctrl *ctrl;	struct device *dev;	int bank;               /* Chip select bank number           */	u8 __iomem *vbase;      /* Chip select base virtual address  */	int page_size;          /* NAND page size (0=512, 1=2048)    */	unsigned int fmr;       /* FCM Flash Mode Register value     */};/* overview of the fsl elbc controller */struct fsl_elbc_ctrl {	struct nand_hw_control controller;	struct fsl_elbc_mtd *chips[MAX_BANKS];	/* device info */	struct device *dev;	struct fsl_lbc_regs __iomem *regs;	int irq;	wait_queue_head_t irq_wait;	unsigned int irq_status; /* status read from LTESR by irq handler */	u8 __iomem *addr;        /* Address of assigned FCM buffer        */	unsigned int page;       /* Last page written to / read from      */	unsigned int read_bytes; /* Number of bytes read during command   */	unsigned int column;     /* Saved column from SEQIN               */	unsigned int index;      /* Pointer to next byte to 'read'        */	unsigned int status;     /* status read from LTESR after last op  */	unsigned int mdr;        /* UPM/FCM Data Register value           */	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */	unsigned int oob;        /* Non zero if operating on OOB data     */	char *oob_poi;           /* Place to write ECC after read back    */};/* These map to the positions used by the FCM hardware ECC generator *//* Small Page FLASH with FMR[ECCM] = 0 */static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {	.eccbytes = 3,	.eccpos = {6, 7, 8},	.oobfree = { {0, 5}, {9, 7} },};/* Small Page FLASH with FMR[ECCM] = 1 */static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {	.eccbytes = 3,	.eccpos = {8, 9, 10},	.oobfree = { {0, 5}, {6, 2}, {11, 5} },};/* Large Page FLASH with FMR[ECCM] = 0 */static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {	.eccbytes = 12,	.eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},	.oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },};/* Large Page FLASH with FMR[ECCM] = 1 */static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {	.eccbytes = 12,	.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},	.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },};/* * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset * 1, so we have to adjust bad block pattern. This pattern should be used for * x8 chips only. So far hardware does not support x16 chips anyway. */static u8 scan_ff_pattern[] = { 0xff, };static struct nand_bbt_descr largepage_memorybased = {	.options = 0,	.offs = 0,	.len = 1,	.pattern = scan_ff_pattern,};/* * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, * interfere with ECC positions, that's why we implement our own descriptors. * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. */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,};/*=================================*//* * Set up the FCM hardware block and page address fields, and the fcm * structure addr field to point to the correct FCM buffer in memory */static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob){	struct nand_chip *chip = mtd->priv;	struct fsl_elbc_mtd *priv = chip->priv;	struct fsl_elbc_ctrl *ctrl = priv->ctrl;	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;	int buf_num;	ctrl->page = page_addr;	out_be32(&lbc->fbar,	         page_addr >> (chip->phys_erase_shift - chip->page_shift));	if (priv->page_size) {		out_be32(&lbc->fpar,		         ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |		         (oob ? FPAR_LP_MS : 0) | column);		buf_num = (page_addr & 1) << 2;	} else {		out_be32(&lbc->fpar,		         ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |		         (oob ? FPAR_SP_MS : 0) | column);		buf_num = page_addr & 7;	}	ctrl->addr = priv->vbase + buf_num * 1024;	ctrl->index = column;	/* for OOB data point to the second half of the buffer */	if (oob)		ctrl->index += priv->page_size ? 2048 : 512;	dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "	                    "index %x, pes %d ps %d\n",	         buf_num, ctrl->addr, priv->vbase, ctrl->index,	         chip->phys_erase_shift, chip->page_shift);}/* * execute FCM command and wait for it to complete */static int fsl_elbc_run_command(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	struct fsl_elbc_mtd *priv = chip->priv;	struct fsl_elbc_ctrl *ctrl = priv->ctrl;	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;	/* Setup the FMR[OP] to execute without write protection */	out_be32(&lbc->fmr, priv->fmr | 3);	if (ctrl->use_mdr)		out_be32(&lbc->mdr, ctrl->mdr);	dev_vdbg(ctrl->dev,	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));	dev_vdbg(ctrl->dev,	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "	         "fbcr=%08x bank=%d\n",	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),	         in_be32(&lbc->fbcr), priv->bank);	ctrl->irq_status = 0;	/* execute special operation */	out_be32(&lbc->lsor, priv->bank);	/* wait for FCM complete flag or timeout */	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,	                   FCM_TIMEOUT_MSECS * HZ/1000);	ctrl->status = ctrl->irq_status;	/* store mdr value in case it was needed */	if (ctrl->use_mdr)		ctrl->mdr = in_be32(&lbc->mdr);	ctrl->use_mdr = 0;	dev_vdbg(ctrl->dev,	         "fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n",	         ctrl->status, ctrl->mdr, in_be32(&lbc->fmr));	/* returns 0 on success otherwise non-zero) */	return ctrl->status == LTESR_CC ? 0 : -EIO;}static void fsl_elbc_do_read(struct nand_chip *chip, int oob){	struct fsl_elbc_mtd *priv = chip->priv;	struct fsl_elbc_ctrl *ctrl = priv->ctrl;	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;	if (priv->page_size) {		out_be32(&lbc->fir,		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |		         (FIR_OP_CA  << FIR_OP1_SHIFT) |		         (FIR_OP_PA  << FIR_OP2_SHIFT) |		         (FIR_OP_CW1 << FIR_OP3_SHIFT) |		         (FIR_OP_RBW << FIR_OP4_SHIFT));		out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |		                    (NAND_CMD_READSTART << FCR_CMD1_SHIFT));	} else {		out_be32(&lbc->fir,		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |		         (FIR_OP_CA  << FIR_OP1_SHIFT) |		         (FIR_OP_PA  << FIR_OP2_SHIFT) |		         (FIR_OP_RBW << FIR_OP3_SHIFT));		if (oob)			out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);		else			out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);	}}/* cmdfunc send commands to the FCM */static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,                             int column, int page_addr){	struct nand_chip *chip = mtd->priv;	struct fsl_elbc_mtd *priv = chip->priv;	struct fsl_elbc_ctrl *ctrl = priv->ctrl;	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;	ctrl->use_mdr = 0;	/* clear the read buffer */	ctrl->read_bytes = 0;	if (command != NAND_CMD_PAGEPROG)		ctrl->index = 0;	switch (command) {	/* READ0 and READ1 read the entire buffer to use hardware ECC. */	case NAND_CMD_READ1:		column += 256;	/* fall-through */	case NAND_CMD_READ0:		dev_dbg(ctrl->dev,		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"		        " 0x%x, column: 0x%x.\n", page_addr, column);		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */		set_addr(mtd, 0, page_addr, 0);		ctrl->read_bytes = mtd->writesize + mtd->oobsize;		ctrl->index += column;		fsl_elbc_do_read(chip, 0);		fsl_elbc_run_command(mtd);		return;	/* READOOB reads only the OOB because no ECC is performed. */	case NAND_CMD_READOOB:		dev_vdbg(ctrl->dev,		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"			 " 0x%x, column: 0x%x.\n", page_addr, column);		out_be32(&lbc->fbcr, mtd->oobsize - column);		set_addr(mtd, column, page_addr, 1);		ctrl->read_bytes = mtd->writesize + mtd->oobsize;		fsl_elbc_do_read(chip, 1);		fsl_elbc_run_command(mtd);		return;	/* READID must read all 5 possible bytes while CEB is active */	case NAND_CMD_READID:		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");		out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |		                    (FIR_OP_RBW << FIR_OP2_SHIFT));		out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);		/* 5 bytes for manuf, device and exts */		out_be32(&lbc->fbcr, 5);		ctrl->read_bytes = 5;		ctrl->use_mdr = 1;		ctrl->mdr = 0;		set_addr(mtd, 0, 0, 0);		fsl_elbc_run_command(mtd);		return;	/* ERASE1 stores the block and page address */	case NAND_CMD_ERASE1:		dev_vdbg(ctrl->dev,		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "		         "page_addr: 0x%x.\n", page_addr);		set_addr(mtd, 0, page_addr, 0);		return;	/* ERASE2 uses the block and page address from ERASE1 */	case NAND_CMD_ERASE2:		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");		out_be32(&lbc->fir,		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |		         (FIR_OP_PA  << FIR_OP1_SHIFT) |		         (FIR_OP_CM1 << FIR_OP2_SHIFT));		out_be32(&lbc->fcr,		         (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |		         (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -