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

📄 fsl_elbc_nand.c

📁 uboot200903最新版本的通用uboot
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Freescale Enhanced Local Bus Controller FCM NAND driver * * Copyright (c) 2006-2008 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 <common.h>#include <malloc.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <asm/io.h>#include <asm/errno.h>#ifdef VERBOSE_DEBUG#define DEBUG_ELBC#define vdbg(format, arg...) printf("DEBUG: " format, ##arg)#else#define vdbg(format, arg...) do {} while (0)#endif/* Can't use plain old DEBUG because the linux mtd * headers define it as a macro. */#ifdef DEBUG_ELBC#define dbg(format, arg...) printf("DEBUG: " format, ##arg)#else#define dbg(format, arg...) do {} while (0)#endif#define MAX_BANKS 8#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)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 */	fsl_lbus_t *regs;	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     */	uint8_t *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;	fsl_lbus_t *lbc = ctrl->regs;	int buf_num;	ctrl->page = page_addr;	if (priv->page_size) {		out_be32(&lbc->fbar, page_addr >> 6);		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->fbar, page_addr >> 5);		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;	vdbg("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;	fsl_lbus_t *lbc = ctrl->regs;	long long end_tick;	u32 ltesr;	/* 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);	vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",	     in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));	vdbg("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);	/* execute special operation */	out_be32(&lbc->lsor, priv->bank);	/* wait for FCM complete flag or timeout */	end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks();	ltesr = 0;	while (end_tick > get_ticks()) {		ltesr = in_be32(&lbc->ltesr);		if (ltesr & LTESR_CC)			break;	}	ctrl->status = ltesr & LTESR_NAND_MASK;	out_be32(&lbc->ltesr, ctrl->status);	out_be32(&lbc->lteatr, 0);	/* store mdr value in case it was needed */	if (ctrl->use_mdr)		ctrl->mdr = in_be32(&lbc->mdr);	ctrl->use_mdr = 0;	vdbg("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;	fsl_lbus_t *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;	fsl_lbus_t *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:		vdbg("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:		vdbg("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:		vdbg("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:		vdbg("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:		vdbg("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));		out_be32(&lbc->fbcr, 0);		ctrl->read_bytes = 0;		fsl_elbc_run_command(mtd);		return;	/* SEQIN sets up the addr buffer and all registers except the length */	case NAND_CMD_SEQIN: {		u32 fcr;		vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "		     "page_addr: 0x%x, column: 0x%x.\n",		     page_addr, column);		ctrl->column = column;		ctrl->oob = 0;		if (priv->page_size) {			fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |			      (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);			out_be32(&lbc->fir,				 (FIR_OP_CW0 << FIR_OP0_SHIFT) |				 (FIR_OP_CA  << FIR_OP1_SHIFT) |				 (FIR_OP_PA  << FIR_OP2_SHIFT) |				 (FIR_OP_WB  << FIR_OP3_SHIFT) |				 (FIR_OP_CW1 << FIR_OP4_SHIFT));		} else {			fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |			      (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);			out_be32(&lbc->fir,				 (FIR_OP_CW0 << FIR_OP0_SHIFT) |				 (FIR_OP_CM2 << FIR_OP1_SHIFT) |				 (FIR_OP_CA  << FIR_OP2_SHIFT) |				 (FIR_OP_PA  << FIR_OP3_SHIFT) |

⌨️ 快捷键说明

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