Blackfin: add driver for on-chip NAND controller
This is a port of the Linux Blackfin on-chip NFC driver to U-Boot. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
		
							parent
							
								
									4148e02aba
								
							
						
					
					
						commit
						be9d8c780e
					
				|  | @ -35,6 +35,7 @@ COBJS-y += nand_ids.o | |||
| COBJS-y += nand_util.o | ||||
| endif | ||||
| 
 | ||||
| COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o | ||||
| COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o | ||||
| COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o | ||||
| COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o | ||||
|  |  | |||
|  | @ -0,0 +1,385 @@ | |||
| /*
 | ||||
|  * 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); | ||||
| 
 | ||||
| 		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; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue