📄 bf5xx_nand.c
字号:
/* 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 + -