387 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * Driver for Blackfin on-chip NAND controller.
 | 
						|
 *
 | 
						|
 * Enter bugs at http://blackfin.uclinux.org/
 | 
						|
 *
 | 
						|
 * Copyright (c) 2007-2008 Analog Devices Inc.
 | 
						|
 *
 | 
						|
 * Licensed under the GPL-2 or later.
 | 
						|
 */
 | 
						|
 | 
						|
/* TODO:
 | 
						|
 * - move bit defines into mach-common/bits/nand.h
 | 
						|
 * - try and replace all IRQSTAT usage with STAT polling
 | 
						|
 * - have software ecc mode use same algo as hw ecc ?
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <asm/io.h>
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
# define pr_stamp() printf("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__)
 | 
						|
#else
 | 
						|
# define pr_stamp()
 | 
						|
#endif
 | 
						|
 | 
						|
#include <nand.h>
 | 
						|
 | 
						|
#include <asm/blackfin.h>
 | 
						|
 | 
						|
/* Bit masks for NFC_CTL */
 | 
						|
 | 
						|
#define                    WR_DLY  0xf        /* Write Strobe Delay */
 | 
						|
#define                    RD_DLY  0xf0       /* Read Strobe Delay */
 | 
						|
#define                    NWIDTH  0x100      /* NAND Data Width */
 | 
						|
#define                   PG_SIZE  0x200      /* Page Size */
 | 
						|
 | 
						|
/* Bit masks for NFC_STAT */
 | 
						|
 | 
						|
#define                     NBUSY  0x1        /* Not Busy */
 | 
						|
#define                   WB_FULL  0x2        /* Write Buffer Full */
 | 
						|
#define                PG_WR_STAT  0x4        /* Page Write Pending */
 | 
						|
#define                PG_RD_STAT  0x8        /* Page Read Pending */
 | 
						|
#define                  WB_EMPTY  0x10       /* Write Buffer Empty */
 | 
						|
 | 
						|
/* Bit masks for NFC_IRQSTAT */
 | 
						|
 | 
						|
#define                  NBUSYIRQ  0x1        /* Not Busy IRQ */
 | 
						|
#define                    WB_OVF  0x2        /* Write Buffer Overflow */
 | 
						|
#define                   WB_EDGE  0x4        /* Write Buffer Edge Detect */
 | 
						|
#define                    RD_RDY  0x8        /* Read Data Ready */
 | 
						|
#define                   WR_DONE  0x10       /* Page Write Done */
 | 
						|
 | 
						|
#define NAND_IS_512() (CONFIG_BFIN_NFC_CTL_VAL & 0x200)
 | 
						|
 | 
						|
/*
 | 
						|
 * hardware specific access to control-lines
 | 
						|
 */
 | 
						|
static void bfin_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	if (cmd == NAND_CMD_NONE)
 | 
						|
		return;
 | 
						|
 | 
						|
	while (bfin_read_NFC_STAT() & WB_FULL)
 | 
						|
		continue;
 | 
						|
 | 
						|
	if (ctrl & NAND_CLE)
 | 
						|
		bfin_write_NFC_CMD(cmd);
 | 
						|
	else
 | 
						|
		bfin_write_NFC_ADDR(cmd);
 | 
						|
	SSYNC();
 | 
						|
}
 | 
						|
 | 
						|
int bfin_nfc_devready(struct mtd_info *mtd)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
	return (bfin_read_NFC_STAT() & NBUSY ? 1 : 0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * PIO mode for buffer writing and reading
 | 
						|
 */
 | 
						|
static void bfin_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	int i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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)
 | 
						|
			if (ctrlc())
 | 
						|
				return;
 | 
						|
 | 
						|
		/* Contents do not matter */
 | 
						|
		bfin_write_NFC_DATA_RD(0x0000);
 | 
						|
		SSYNC();
 | 
						|
 | 
						|
		while (!(bfin_read_NFC_IRQSTAT() & RD_RDY))
 | 
						|
			if (ctrlc())
 | 
						|
				return;
 | 
						|
 | 
						|
		buf[i] = bfin_read_NFC_READ();
 | 
						|
 | 
						|
		bfin_write_NFC_IRQSTAT(RD_RDY);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static uint8_t bfin_nfc_read_byte(struct mtd_info *mtd)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	uint8_t val;
 | 
						|
	bfin_nfc_read_buf(mtd, &val, 1);
 | 
						|
	return val;
 | 
						|
}
 | 
						|
 | 
						|
static void bfin_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < len; ++i) {
 | 
						|
		while (bfin_read_NFC_STAT() & WB_FULL)
 | 
						|
			if (ctrlc())
 | 
						|
				return;
 | 
						|
 | 
						|
		bfin_write_NFC_DATA_WR(buf[i]);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * ECC functions
 | 
						|
 * These allow the bfin to use the controller's ECC
 | 
						|
 * generator block to ECC the data as it passes through
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * ECC error correction function
 | 
						|
 */
 | 
						|
static int bfin_nfc_correct_data_256(struct mtd_info *mtd, u_char *dat,
 | 
						|
					u_char *read_ecc, u_char *calc_ecc)
 | 
						|
{
 | 
						|
	u32 syndrome[5];
 | 
						|
	u32 calced, stored;
 | 
						|
	unsigned short failing_bit, failing_byte;
 | 
						|
	u_char data;
 | 
						|
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	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)
 | 
						|
		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];
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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) {
 | 
						|
		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
 | 
						|
	 */
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int bfin_nfc_correct_data(struct mtd_info *mtd, u_char *dat,
 | 
						|
					u_char *read_ecc, u_char *calc_ecc)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	ret = bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
 | 
						|
 | 
						|
	/* If page size is 512, correct second 256 bytes */
 | 
						|
	if (NAND_IS_512()) {
 | 
						|
		dat += 256;
 | 
						|
		read_ecc += 8;
 | 
						|
		calc_ecc += 8;
 | 
						|
		ret |= bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void reset_ecc(void)
 | 
						|
{
 | 
						|
	bfin_write_NFC_RST(0x1);
 | 
						|
	while (bfin_read_NFC_RST() & 1)
 | 
						|
		continue;
 | 
						|
}
 | 
						|
 | 
						|
static void bfin_nfc_enable_hwecc(struct mtd_info *mtd, int mode)
 | 
						|
{
 | 
						|
	reset_ecc();
 | 
						|
}
 | 
						|
 | 
						|
static int bfin_nfc_calculate_ecc(struct mtd_info *mtd,
 | 
						|
		const u_char *dat, u_char *ecc_code)
 | 
						|
{
 | 
						|
	u16 ecc0, ecc1;
 | 
						|
	u32 code[2];
 | 
						|
	u8 *p;
 | 
						|
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	/* 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);
 | 
						|
 | 
						|
	/* 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 (NAND_IS_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);
 | 
						|
	}
 | 
						|
 | 
						|
	reset_ecc();
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_BFIN_NFC_BOOTROM_ECC
 | 
						|
# define BOOTROM_ECC 1
 | 
						|
#else
 | 
						|
# define BOOTROM_ECC 0
 | 
						|
#endif
 | 
						|
 | 
						|
static 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 },
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Board-specific NAND initialization. The following members of the
 | 
						|
 * argument are board-specific (per include/linux/mtd/nand.h):
 | 
						|
 * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
 | 
						|
 * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
 | 
						|
 * - cmd_ctrl: hardwarespecific function for accesing control-lines
 | 
						|
 * - dev_ready: hardwarespecific function for  accesing device ready/busy line
 | 
						|
 * - enable_hwecc?: function to enable (reset)  hardware ecc generator. Must
 | 
						|
 *   only be provided if a hardware ECC is available
 | 
						|
 * - ecc.mode: mode of ecc, see defines
 | 
						|
 * - chip_delay: chip dependent delay for transfering data from array to
 | 
						|
 *   read regs (tR)
 | 
						|
 * - options: various chip options. They can partly be set to inform
 | 
						|
 *   nand_scan about special functionality. See the defines for further
 | 
						|
 *   explanation
 | 
						|
 * Members with a "?" were not set in the merged testing-NAND branch,
 | 
						|
 * so they are not set here either.
 | 
						|
 */
 | 
						|
int board_nand_init(struct nand_chip *chip)
 | 
						|
{
 | 
						|
	pr_stamp();
 | 
						|
 | 
						|
	/* set width/ecc/timings/etc... */
 | 
						|
	bfin_write_NFC_CTL(CONFIG_BFIN_NFC_CTL_VAL);
 | 
						|
 | 
						|
	/* clear interrupt status */
 | 
						|
	bfin_write_NFC_IRQMASK(0x0);
 | 
						|
	bfin_write_NFC_IRQSTAT(0xffff);
 | 
						|
 | 
						|
	/* enable GPIO function enable register */
 | 
						|
#ifdef __ADSPBF54x__
 | 
						|
	bfin_write_PORTJ_FER(bfin_read_PORTJ_FER() | 6);
 | 
						|
#elif defined(__ADSPBF52x__)
 | 
						|
	bfin_write_PORTH_FER(bfin_read_PORTH_FER() | 0xFCFF);
 | 
						|
	bfin_write_PORTH_MUX(0);
 | 
						|
#else
 | 
						|
# error no support for this variant
 | 
						|
#endif
 | 
						|
 | 
						|
	chip->cmd_ctrl = bfin_nfc_cmd_ctrl;
 | 
						|
	chip->read_buf = bfin_nfc_read_buf;
 | 
						|
	chip->write_buf = bfin_nfc_write_buf;
 | 
						|
	chip->read_byte = bfin_nfc_read_byte;
 | 
						|
 | 
						|
#ifdef CONFIG_BFIN_NFC_NO_HW_ECC
 | 
						|
# define ECC_HW 0
 | 
						|
#else
 | 
						|
# define ECC_HW 1
 | 
						|
#endif
 | 
						|
	if (ECC_HW) {
 | 
						|
		if (BOOTROM_ECC) {
 | 
						|
			chip->badblock_pattern = &bootrom_bbt;
 | 
						|
			chip->ecc.layout = &bootrom_ecclayout;
 | 
						|
		}
 | 
						|
		if (!NAND_IS_512()) {
 | 
						|
			chip->ecc.bytes = 3;
 | 
						|
			chip->ecc.size = 256;
 | 
						|
		} else {
 | 
						|
			chip->ecc.bytes = 6;
 | 
						|
			chip->ecc.size = 512;
 | 
						|
		}
 | 
						|
		chip->ecc.mode = NAND_ECC_HW;
 | 
						|
		chip->ecc.calculate = bfin_nfc_calculate_ecc;
 | 
						|
		chip->ecc.correct   = bfin_nfc_correct_data;
 | 
						|
		chip->ecc.hwctl     = bfin_nfc_enable_hwecc;
 | 
						|
	} else
 | 
						|
		chip->ecc.mode = NAND_ECC_SOFT;
 | 
						|
	chip->dev_ready = bfin_nfc_devready;
 | 
						|
	chip->chip_delay = 0;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |