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

📄 bf5xx_nand.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* linux/drivers/mtd/nand/bf5xx_nand.c * * Copyright 2006-2008 Analog Devices Inc. *	http://blackfin.uclinux.org/ *	Bryan Wu <bryan.wu@analog.com> * * Blackfin BF5xx on-chip NAND flash controller driver * * Derived from drivers/mtd/nand/s3c2410.c * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk> * * Derived from drivers/mtd/nand/cafe.c * Copyright © 2006 Red Hat, Inc. * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> * * Changelog: *	12-Jun-2007  Bryan Wu:  Initial version *	18-Jul-2007  Bryan Wu: *		- ECC_HW and ECC_SW supported *		- DMA supported in ECC_HW *		- YAFFS tested as rootfs in both ECC_HW and ECC_SW * * TODO: * 	Enable JFFS2 over NAND as rootfs * * 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/platform_device.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/io.h>#include <linux/bitops.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <linux/mtd/partitions.h>#include <asm/blackfin.h>#include <asm/dma.h>#include <asm/cacheflush.h>#include <asm/nand.h>#include <asm/portmux.h>#define DRV_NAME	"bf5xx-nand"#define DRV_VERSION	"1.2"#define DRV_AUTHOR	"Bryan Wu <bryan.wu@analog.com>"#define DRV_DESC	"BF5xx on-chip NAND FLash Controller Driver"#ifdef CONFIG_MTD_NAND_BF5XX_HWECCstatic int hardware_ecc = 1;#elsestatic int hardware_ecc;#endifstatic const unsigned short bfin_nfc_pin_req[] =	{P_NAND_CE,	 P_NAND_RB,	 P_NAND_D0,	 P_NAND_D1,	 P_NAND_D2,	 P_NAND_D3,	 P_NAND_D4,	 P_NAND_D5,	 P_NAND_D6,	 P_NAND_D7,	 P_NAND_WE,	 P_NAND_RE,	 P_NAND_CLE,	 P_NAND_ALE,	 0};#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECCstatic uint8_t bbt_pattern[] = { 0xff };static struct nand_bbt_descr bootrom_bbt = {	.options = 0,	.offs = 63,	.len = 1,	.pattern = bbt_pattern,};static struct nand_ecclayout bootrom_ecclayout = {	.eccbytes = 24,	.eccpos = {		0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2,		0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2,		0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,		0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2,		0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2,		0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,		0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2,		0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2	},	.oobfree = {		{ 0x8 * 0 + 3, 5 },		{ 0x8 * 1 + 3, 5 },		{ 0x8 * 2 + 3, 5 },		{ 0x8 * 3 + 3, 5 },		{ 0x8 * 4 + 3, 5 },		{ 0x8 * 5 + 3, 5 },		{ 0x8 * 6 + 3, 5 },		{ 0x8 * 7 + 3, 5 },	}};#endif/* * Data structures for bf5xx nand flash controller driver *//* bf5xx nand info */struct bf5xx_nand_info {	/* mtd info */	struct nand_hw_control		controller;	struct mtd_info			mtd;	struct nand_chip		chip;	/* platform info */	struct bf5xx_nand_platform	*platform;	/* device info */	struct device			*device;	/* DMA stuff */	struct completion		dma_completion;};/* * Conversion functions */static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd){	return container_of(mtd, struct bf5xx_nand_info, mtd);}static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev){	return platform_get_drvdata(pdev);}static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev){	return pdev->dev.platform_data;}/* * struct nand_chip interface function pointers *//* * bf5xx_nand_hwcontrol * * Issue command and address cycles to the chip */static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,				   unsigned int ctrl){	if (cmd == NAND_CMD_NONE)		return;	while (bfin_read_NFC_STAT() & WB_FULL)		cpu_relax();	if (ctrl & NAND_CLE)		bfin_write_NFC_CMD(cmd);	else		bfin_write_NFC_ADDR(cmd);	SSYNC();}/* * bf5xx_nand_devready() * * returns 0 if the nand is busy, 1 if it is ready */static int bf5xx_nand_devready(struct mtd_info *mtd){	unsigned short val = bfin_read_NFC_IRQSTAT();	if ((val & NBUSYIRQ) == NBUSYIRQ)		return 1;	else		return 0;}/* * ECC functions * These allow the bf5xx to use the controller's ECC * generator block to ECC the data as it passes through *//* * ECC error correction function */static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,					u_char *read_ecc, u_char *calc_ecc){	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);	u32 syndrome[5];	u32 calced, stored;	int i;	unsigned short failing_bit, failing_byte;	u_char data;	calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);	stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);	syndrome[0] = (calced ^ stored);	/*	 * syndrome 0: all zero	 * No error in data	 * No action	 */	if (!syndrome[0] || !calced || !stored)		return 0;	/*	 * sysdrome 0: only one bit is one	 * ECC data was incorrect	 * No action	 */	if (hweight32(syndrome[0]) == 1) {		dev_err(info->device, "ECC data was incorrect!\n");		return 1;	}	syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);	syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);	syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);	syndrome[4] = syndrome[2] ^ syndrome[3];	for (i = 0; i < 5; i++)		dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);	dev_info(info->device,		"calced[0x%08x], stored[0x%08x]\n",		calced, stored);	/*	 * sysdrome 0: exactly 11 bits are one, each parity	 * and parity' pair is 1 & 0 or 0 & 1.	 * 1-bit correctable error	 * Correct the error	 */	if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {		dev_info(info->device,			"1-bit correctable error, correct it.\n");		dev_info(info->device,			"syndrome[1] 0x%08x\n", syndrome[1]);		failing_bit = syndrome[1] & 0x7;		failing_byte = syndrome[1] >> 0x3;		data = *(dat + failing_byte);		data = data ^ (0x1 << failing_bit);		*(dat + failing_byte) = data;		return 0;	}	/*	 * sysdrome 0: random data	 * More than 1-bit error, non-correctable error	 * Discard data, mark bad block	 */	dev_err(info->device,		"More than 1-bit error, non-correctable error.\n");	dev_err(info->device,		"Please discard data, mark bad block\n");	return 1;}static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,					u_char *read_ecc, u_char *calc_ecc){	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);	struct bf5xx_nand_platform *plat = info->platform;	unsigned short page_size = (plat->page_size ? 512 : 256);	int ret;	ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);	/* If page size is 512, correct second 256 bytes */	if (page_size == 512) {		dat += 256;		read_ecc += 8;		calc_ecc += 8;		ret |= bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);	}	return ret;}static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode){	return;}static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,		const u_char *dat, u_char *ecc_code){	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);	struct bf5xx_nand_platform *plat = info->platform;	u16 page_size = (plat->page_size ? 512 : 256);	u16 ecc0, ecc1;	u32 code[2];	u8 *p;	/* first 4 bytes ECC code for 256 page size */	ecc0 = bfin_read_NFC_ECC0();	ecc1 = bfin_read_NFC_ECC1();	code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);	dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);	/* first 3 bytes in ecc_code for 256 page size */	p = (u8 *) code;	memcpy(ecc_code, p, 3);	/* second 4 bytes ECC code for 512 page size */	if (page_size == 512) {		ecc0 = bfin_read_NFC_ECC2();		ecc1 = bfin_read_NFC_ECC3();		code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);		/* second 3 bytes in ecc_code for second 256		 * bytes of 512 page size		 */		p = (u8 *) (code + 1);		memcpy((ecc_code + 3), p, 3);		dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);	}	return 0;}/* * PIO mode for buffer writing and reading */static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len){	int i;	unsigned short val;	/*	 * Data reads are requested by first writing to NFC_DATA_RD	 * and then reading back from NFC_READ.	 */	for (i = 0; i < len; i++) {		while (bfin_read_NFC_STAT() & WB_FULL)			cpu_relax();		/* Contents do not matter */		bfin_write_NFC_DATA_RD(0x0000);		SSYNC();		while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)			cpu_relax();		buf[i] = bfin_read_NFC_READ();		val = bfin_read_NFC_IRQSTAT();		val |= RD_RDY;		bfin_write_NFC_IRQSTAT(val);		SSYNC();	}}static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd){	uint8_t val;	bf5xx_nand_read_buf(mtd, &val, 1);	return val;}static void bf5xx_nand_write_buf(struct mtd_info *mtd,				const uint8_t *buf, int len){	int i;	for (i = 0; i < len; i++) {		while (bfin_read_NFC_STAT() & WB_FULL)			cpu_relax();		bfin_write_NFC_DATA_WR(buf[i]);		SSYNC();	}}static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len){	int i;	u16 *p = (u16 *) buf;	len >>= 1;	/*	 * Data reads are requested by first writing to NFC_DATA_RD	 * and then reading back from NFC_READ.	 */	bfin_write_NFC_DATA_RD(0x5555);	SSYNC();

⌨️ 快捷键说明

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