707 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			707 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  *  spinand driver
 | |
|  *
 | |
|  *  Author:	Howard Chang
 | |
|  *  Created:	Aug 2, 2018
 | |
|  *  Copyright:	Novatek Inc.
 | |
|  *
 | |
|  */
 | |
| #include "config.h"
 | |
| #include <common.h>
 | |
| #include <malloc.h>
 | |
| #include <nand.h>
 | |
| #include "../nvt_flash_spi/nvt_flash_spi_int.h"
 | |
| 
 | |
| #define NAND_VERSION "1.0.17"
 | |
| 
 | |
| #ifdef CONFIG_CMD_NAND
 | |
| typedef struct nand_2plane_id {
 | |
| 	char    *name;
 | |
| 	int     mfr_id;
 | |
| 	int     dev_id;
 | |
| } NAND_2PLANE_ID;
 | |
| 
 | |
| static NAND_2PLANE_ID nand_2plane_list[] = {
 | |
| 	// flash name / Manufacturer ID / Device ID
 | |
| 	{ "MXIC_MX35LF2G14AC",		0xC2,	0x20	},
 | |
| 	{ "MICRON_MT29F2G01AB",		0x2C,	0x24	}
 | |
| };
 | |
| #define NUM_OF_2PLANE_ID (sizeof(nand_2plane_list) / sizeof(NAND_2PLANE_ID))
 | |
| 
 | |
| /* error code and state */
 | |
| enum {
 | |
| 	ERR_NONE	= 0,
 | |
| 	ERR_DMABUSERR	= -1,
 | |
| 	ERR_SENDCMD	= -2,
 | |
| 	ERR_DBERR	= -3,
 | |
| 	ERR_BBERR	= -4,
 | |
| 	ERR_ECC_FAIL	= -5,
 | |
| 	ERR_ECC_UNCLEAN = -6,
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	STATE_READY = 0,
 | |
| 	STATE_CMD_HANDLE,
 | |
| 	STATE_DMA_READING,
 | |
| 	STATE_DMA_WRITING,
 | |
| 	STATE_DMA_DONE,
 | |
| 	STATE_PIO_READING,
 | |
| 	STATE_PIO_WRITING,
 | |
| };
 | |
| 
 | |
| static struct nand_ecclayout hw_smallpage_ecclayout = {
 | |
| 	.eccbytes = 12,
 | |
| 	.eccpos = {0, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
 | |
| 	.oobfree = { {1, 3}, {5, 1} }
 | |
| };
 | |
| 
 | |
| static struct nand_ecclayout spinand_oob_64 = {
 | |
| 	.eccbytes = 32,
 | |
| 	.eccpos = {
 | |
| 		8, 9, 10, 11, 12, 13, 14, 15,
 | |
| 		24, 25, 26, 27, 28, 29, 30, 31,
 | |
| 		40, 41, 42, 43, 44, 45, 46, 47,
 | |
| 		56, 57, 58, 59, 60, 61, 62, 63},
 | |
| 	.oobavail = 12,
 | |
| 	.oobfree = {
 | |
| 		{.offset = 16,
 | |
| 			.length = 4},
 | |
| 		{.offset = 32,
 | |
| 			.length = 4},
 | |
| 		{.offset = 48,
 | |
| 			.length = 4},
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static void drv_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 | |
| {
 | |
| 
 | |
| }
 | |
| 
 | |
| static void nvt_parse_frequency(u32 *freq)
 | |
| {
 | |
| 	/* Enable it after dts parsing ready*/
 | |
| 	ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
 | |
| 	int nodeoffset;
 | |
| 	u32 *cell = NULL;
 | |
| 	char path[20] = {0};
 | |
| 
 | |
| 	sprintf(path,"/nand@%lx",(unsigned long)IOADDR_NAND_REG_BASE);
 | |
| 
 | |
| 	nodeoffset = fdt_path_offset((const void*)fdt_addr, path);
 | |
| 
 | |
| 	cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "clock-frequency", NULL);
 | |
| 
 | |
| 	if (cell > 0)
 | |
| 		*freq = __be32_to_cpu(cell[0]);
 | |
| 	else
 | |
| 		*freq = 12000000;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static int nvt_scan_nand_type(struct drv_nand_dev_info *info, int32_t* id)
 | |
| {
 | |
| 	int maf_id, dev_id;
 | |
| 	uint32_t reg;
 | |
| 	struct nand_flash_dev *type = nvt_nand_ids;
 | |
| 
 | |
| 	maf_id = *id & 0xFF;
 | |
| 	dev_id = (*id >> 8) & 0xFF;
 | |
| 
 | |
| 	debug("the maf_id is 0x%x, the dev_id is 0x%x\n", maf_id, dev_id);
 | |
| 
 | |
| 	for (; type->name != NULL; type++) {
 | |
| 		if (type->mfr_id == maf_id && type->dev_id == dev_id)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (!type->name) {
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	if ((*id & 0xFFFF) == XTX_XT26G01C) {
 | |
| 		UINT32 error_sts_reg;
 | |
| 
 | |
| 		//printf("Flash is XT26G01C, use special ecc_sts type. \n");
 | |
| 		info->ecc_status_type = 1;
 | |
| 
 | |
| 		error_sts_reg = NAND_STS_BIT_4 | NAND_STS_BIT_5 | NAND_STS_BIT_6 | NAND_STS_BIT_7;
 | |
| 		error_sts_reg |= NAND_STS_BIT_4_EQUAL_1 | NAND_STS_BIT_5_EQUAL_1 | NAND_STS_BIT_6_EQUAL_1 | NAND_STS_BIT_7_EQUAL_1;
 | |
| 		NAND_SETREG(info, NAND_STATUS_CHECK2_REG_OFS, error_sts_reg);
 | |
| 	} else {
 | |
| 		info->ecc_status_type = 0;
 | |
| 	}
 | |
| 
 | |
| 	info->flash_info->chip_id = *id;
 | |
| 	info->flash_info->chip_sel = 0;
 | |
| 	info->flash_info->page_size = type->pagesize;
 | |
| 	info->flash_info->block_size = type->erasesize;
 | |
| 	info->flash_info->device_size = type->chipsize;
 | |
| 	info->flash_info->page_per_block = (type->erasesize / type->pagesize);
 | |
| 
 | |
| 	debug("page_size %d, block_size %d, device_size %d page_per_block %d\n", \
 | |
| 		info->flash_info->page_size, \
 | |
| 		info->flash_info->block_size, \
 | |
| 		info->flash_info->device_size, \
 | |
| 		info->flash_info->page_per_block);
 | |
| 
 | |
| 	if (type->pagesize == 4096) {
 | |
| 		info->flash_info->phy_page_ratio = 8;
 | |
| 		info->flash_info->oob_size = 128;
 | |
| 		info->flash_info->device_size = 512;
 | |
| 		info->flash_info->module_config = NAND_PAGE4K | NAND_2COL_ADDR;
 | |
| 		nand_hostSetupPageSize(0, NAND_PAGE_SIZE_4096);
 | |
| 	} else {
 | |
| 		info->flash_info->phy_page_ratio = 4;
 | |
| 		info->flash_info->oob_size = 64;
 | |
| 		info->flash_info->module_config = NAND_PAGE2K | NAND_2COL_ADDR;
 | |
| 		nand_hostSetupPageSize(0, NAND_PAGE_SIZE_2048);
 | |
| 	}
 | |
| 
 | |
| 	if (info->use_ecc == NANDCTRL_SPIFLASH_USE_INTERNAL_RS_ECC) {
 | |
| 		info->flash_info->module_config |= NAND_PRI_ECC_RS;
 | |
| 	} else {
 | |
| 		info->flash_info->module_config |= NAND_PRI_ECC_SPI_ON_DIE;
 | |
| 		info->flash_info->ecc_strength = type->ecc.strength_ds;
 | |
| 	}
 | |
| 
 | |
| 	reg = NAND_GETREG(info, NAND_MODULE0_REG_OFS);
 | |
| 	reg &= ~(0x7FFFF);
 | |
| 
 | |
| 	NAND_SETREG(info, NAND_MODULE0_REG_OFS, reg | info->flash_info->module_config);
 | |
| 
 | |
| 	return E_OK;
 | |
| }
 | |
| 
 | |
| static int drv_nand_readpage(struct drv_nand_dev_info *info,
 | |
| 				int column, int page_addr)
 | |
| {
 | |
| 	return nand_cmd_read_operation(info, (int8_t *)info->data_buff,
 | |
| 			page_addr * info->flash_info->page_size, 1);
 | |
| }
 | |
| 
 | |
| static int drv_nand_write_page(struct drv_nand_dev_info *info,
 | |
| 				int column, int page_addr)
 | |
| {
 | |
| 	if (column != info->flash_info->page_size)
 | |
| 		nand_cmd_write_spare_sram(info);
 | |
| 
 | |
| 	return nand_cmd_write_operation_single(info, (int8_t *)info->data_buff,
 | |
| 				page_addr * info->flash_info->page_size, column);
 | |
| }
 | |
| 
 | |
| static int drv_nand_dev_ready(struct mtd_info *mtd)
 | |
| {
 | |
| 	struct nand_chip *this = mtd->priv;
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 	return nand_host_check_ready(info);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int drv_nand_read_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
 | |
| 				uint8_t *buf, int oob_required, int page)
 | |
| 
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 	int eccsize = chip->ecc.size;
 | |
| 	int eccsteps = chip->ecc.steps;
 | |
| 	u8 status = 0, chip_id = info->flash_info->chip_id & 0xFF;
 | |
| 	u8 dev_id = (info->flash_info->chip_id >> 8) & 0xFF;
 | |
| 	u32 full_id = info->flash_info->chip_id;
 | |
| 	int ret = 0, count = 0, i = 0;
 | |
| 
 | |
| 	chip->read_buf(mtd, buf, eccsize * eccsteps);
 | |
| 
 | |
| 	if (info->nand_chip.oob_poi)
 | |
| 		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 | |
| 
 | |
| 	if (info->retcode == ERR_ECC_UNCLEAN) {
 | |
| 		mtd->ecc_stats.failed++;
 | |
| 	} else if (info->retcode == ECC_CORRECTED) {
 | |
| 		if (info->use_ecc == NANDCTRL_SPIFLASH_USE_INTERNAL_RS_ECC) {
 | |
| 				mtd->ecc_stats.corrected += \
 | |
| 					nand_cmd_read_ecc_corrected(info);
 | |
| 
 | |
| 				ret = status;
 | |
| 		} else {
 | |
| 			if (chip_id == NAND_MXIC) {
 | |
| 				ret = nand_cmd_read_flash_ecc_corrected(info);
 | |
| 				mtd->ecc_stats.corrected += ret;
 | |
| 			} else if ((chip_id == NAND_TOSHIBA) || \
 | |
| 					(full_id== WINBOND_W25N02KV)) {
 | |
| 				status = nand_cmd_read_status(info, \
 | |
| 					NAND_SPI_STS_FEATURE_4);
 | |
| 
 | |
| 				for (i = 0; i < 4; i++)
 | |
| 					count += ((status >> i) & 0x1) ? 1 : 0;
 | |
| 
 | |
| 				mtd->ecc_stats.corrected += count;
 | |
| 				ret = count;
 | |
| 			} else if (chip_id == SPI_NAND_XTX) {
 | |
| 				if (dev_id == (XTX_XT26G01C>>8)) {
 | |
| 					status = nand_cmd_read_status(info, \
 | |
| 						NAND_SPI_STS_FEATURE_3);
 | |
| 
 | |
| 					// ECC sts = b[7..4]
 | |
| 					count = (status >> 4) & 0xF;
 | |
| 					mtd->ecc_stats.corrected += count;
 | |
| 					ret = count;
 | |
| 				} else {
 | |
| 					status = nand_cmd_read_status(info, \
 | |
| 						NAND_SPI_STS_FEATURE_3);
 | |
| 
 | |
| 					count = (status >> 2) & 0xF;
 | |
| 					mtd->ecc_stats.corrected += count;
 | |
| 					ret = count;
 | |
| 				}
 | |
| 			} else {
 | |
| 				mtd->ecc_stats.corrected++;
 | |
| 				ret = 1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int drv_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
 | |
| 				  const uint8_t *buf, int oob_required, int page)
 | |
| {
 | |
| 	/* this function will write page data and oob into nand */
 | |
| 	int eccsize = chip->ecc.size;
 | |
| 	int eccsteps = chip->ecc.steps;
 | |
| 
 | |
| 	chip->write_buf(mtd, buf, eccsize * eccsteps);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int drv_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 | |
| {
 | |
| 	struct nand_chip *nand_chip = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = nand_chip->priv;
 | |
| 
 | |
| 	int ret = info->retcode;
 | |
| 
 | |
| 	info->retcode = ERR_NONE;
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		return NAND_STATUS_FAIL;
 | |
| 	else
 | |
| 		return E_OK;
 | |
| }
 | |
| 
 | |
| static uint8_t drv_nand_read_byte(struct mtd_info *mtd)
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 
 | |
| 	char retval = 0xFF;
 | |
| 
 | |
| 	if (info->buf_start < info->buf_count)
 | |
| 		retval = info->data_buff[info->buf_start++];
 | |
| 
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| static u16 drv_nand_read_word(struct mtd_info *mtd)
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 
 | |
| 	u16 retval = 0xFFFF;
 | |
| 
 | |
| 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
 | |
| 		retval = *((u16 *)(info->data_buff+info->buf_start));
 | |
| 		info->buf_start += 2;
 | |
| 	}
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| static void drv_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 
 | |
| 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 | |
| 
 | |
| 	memcpy(buf, info->data_buff + info->buf_start, real_len);
 | |
| 
 | |
| 	info->buf_start += real_len;
 | |
| }
 | |
| 
 | |
| static void drv_nand_write_buf(struct mtd_info *mtd,
 | |
| 	const uint8_t *buf, int len)
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 
 | |
| 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 | |
| 
 | |
| 	memcpy(info->data_buff + info->buf_start, buf, real_len);
 | |
| 
 | |
| 	info->buf_start += real_len;
 | |
| }
 | |
| 
 | |
| static void drv_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 | |
| 		int column, int page_addr)
 | |
| {
 | |
| 	struct nand_chip *this = mtd_to_nand(mtd);
 | |
| 	struct drv_nand_dev_info *info = this->priv;
 | |
| 
 | |
| 	int ret;
 | |
| 	uint8_t *ptr;
 | |
| 
 | |
| 	info->data_size = 0;
 | |
| 	info->state = STATE_READY;
 | |
| 	info->retcode = ERR_NONE;
 | |
| 
 | |
| 	switch (command) {
 | |
| 	case NAND_CMD_READOOB:
 | |
| 		info->buf_count = mtd->writesize + mtd->oobsize;
 | |
| 		info->buf_start = mtd->writesize + column;
 | |
| 
 | |
| 		ptr = (uint8_t *)info->data_buff;
 | |
| 		ptr = info->data_buff + info->buf_start;
 | |
| 		if (info->buf_start != info->flash_info->page_size) {
 | |
| 			dev_err(&info->pdev->dev,
 | |
| 			"info->buf_start = %d, != 0\n", info->buf_start);
 | |
| 		}
 | |
| 
 | |
| 		nand_cmd_read_page_spare_data(info, (int8_t *)ptr,
 | |
| 				info->flash_info->page_size * page_addr);
 | |
| 		/* We only are OOB, so if the data has error, does not matter */
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_READ0:
 | |
| 		if (((unsigned long)(info->data_buff)) % CACHE_LINE_SIZE)
 | |
| 			printf("NAND_CMD_READ0 : is not Cache_Line_Size alignment!\n");
 | |
| 
 | |
| 		info->buf_start = column;
 | |
| 		info->buf_count = mtd->writesize + mtd->oobsize;
 | |
| 
 | |
| 		ret = drv_nand_readpage(info, column, page_addr);
 | |
| 		if (ret == E_CTX)
 | |
| 			info->retcode = ERR_ECC_UNCLEAN;
 | |
| 		else if (ret < 0)
 | |
| 			info->retcode = ERR_SENDCMD;
 | |
| 		else if (ret == ECC_CORRECTED)
 | |
| 			info->retcode = ECC_CORRECTED;
 | |
| 		else
 | |
| 			info->retcode = ERR_NONE;
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_SEQIN:
 | |
| 		info->buf_start = column;
 | |
| 		info->buf_count = mtd->writesize + mtd->oobsize;
 | |
| 		memset(info->data_buff, 0xff, info->buf_count);
 | |
| 
 | |
| 		/* save column/page_addr for next CMD_PAGEPROG */
 | |
| 		info->seqin_column = column;
 | |
| 		info->seqin_page_addr = page_addr;
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_PAGEPROG:
 | |
| 		if (((unsigned long)(info->data_buff)) % CACHE_LINE_SIZE)
 | |
| 			printf("not MIPS_Cache_Line_Size alignment!\n");
 | |
| 
 | |
| 		ret = drv_nand_write_page(info, info->seqin_column, info->seqin_page_addr);
 | |
| 		if (ret)
 | |
| 			info->retcode = ERR_SENDCMD;
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_ERASE1:
 | |
| 		ret = nand_cmd_erase_block(info, page_addr);
 | |
| 		if (ret)
 | |
| 			info->retcode = ERR_BBERR;
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_ERASE2:
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_READID:
 | |
| 		info->buf_start = 0;
 | |
| 		info->buf_count = 4;
 | |
| 		if(info->flash_info->chip_id)
 | |
| 			memcpy((uint32_t *)info->data_buff, &info->flash_info->chip_id, 4);
 | |
| 		else {
 | |
| 			ret = nvt_nand_read_id(info, (uint32_t *)info->data_buff);
 | |
| 			if (ret)
 | |
| 				info->retcode = ERR_SENDCMD;
 | |
| 		}
 | |
| 	break;
 | |
| 	case NAND_CMD_STATUS:
 | |
| 		info->buf_start = 0;
 | |
| 		info->buf_count = 1;
 | |
| 		nand_cmd_read_status(info, NAND_SPI_STS_FEATURE_2);
 | |
| 		if (!(info->data_buff[0] & 0x80))
 | |
| 			info->data_buff[0] = 0x80;
 | |
| 	break;
 | |
| 
 | |
| 	case NAND_CMD_RESET:
 | |
| 	break;
 | |
| 
 | |
| 	default:
 | |
| 		printf("non-supported command.\n");
 | |
| 	break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int drv_nand_reset(struct drv_nand_dev_info *info)
 | |
| {
 | |
| 	uint32_t reg, clk_div, freq = 0x0;
 | |
| 
 | |
| 	/* Config NAND module clock */
 | |
| 	reg = INW(IOADDR_CG_REG_BASE + 0x74);
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		reg |= (0x400);
 | |
| 	} else {	// 52x, 560
 | |
| 		reg |= (0x1);
 | |
| 	}
 | |
| 	OUTW(IOADDR_CG_REG_BASE + 0x74, reg);
 | |
| 
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		reg = INW(IOADDR_CG_REG_BASE + 0x98);
 | |
| 		reg |= (0x2);
 | |
| 		OUTW(IOADDR_CG_REG_BASE + 0x98, reg);
 | |
| 	} else {
 | |
| 		reg = INW(IOADDR_CG_REG_BASE + 0x84);
 | |
| 		reg |= (0x1);
 | |
| 		OUTW(IOADDR_CG_REG_BASE + 0x84, reg);
 | |
| 	}
 | |
| 
 | |
| 	NAND_SETREG(info, NAND_TIME0_REG_OFS, 0x06002222);
 | |
| 	NAND_SETREG(info, NAND_TIME1_REG_OFS, 0x7f0f);
 | |
| 
 | |
| 	/* Need use PinCtrl framework */
 | |
| 	reg = INW(IOADDR_TOP_REG_BASE + 0x4);
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		reg |= 0x3;
 | |
| 	} else {
 | |
| 		reg |= 0x00002000;
 | |
| 	}
 | |
| 	OUTW(IOADDR_TOP_REG_BASE + 0x4, reg);
 | |
| 
 | |
| 	reg = INW(IOADDR_TOP_REG_BASE + 0xA0);
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		reg &= ~(0x3F);
 | |
| 	} else {
 | |
| 		reg &= ~(0x0000050F);
 | |
| 	}
 | |
| 	OUTW(IOADDR_TOP_REG_BASE + 0xA0, reg);
 | |
| 	/* reset NAND Config NAND module configuration */
 | |
| 
 | |
| 	/* Config clock div */
 | |
| 	nvt_parse_frequency(&freq);
 | |
| 	clk_div = NVT_FLASH_SOURCE_CLK/freq - 1;
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		reg = INW(IOADDR_CG_REG_BASE + 0x48);
 | |
| 		reg &= ~(0x3F0000);
 | |
| 		reg |= (clk_div << 16);
 | |
| 		OUTW(IOADDR_CG_REG_BASE + 0x48, reg);
 | |
| 	} else {
 | |
| 		reg = INW(IOADDR_CG_REG_BASE + 0x40);
 | |
| 		reg &= ~(0x3F000);
 | |
| 		reg |= (clk_div << 12);
 | |
| 		OUTW(IOADDR_CG_REG_BASE + 0x40, reg);
 | |
| 	}
 | |
| 
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51090) {
 | |
| 		/* Config driving */
 | |
| 		reg = INW(IOADDR_PAD_REG_BASE + 0x50);
 | |
| 		reg &= ~(0xFFFFFFF);
 | |
| 
 | |
| 		if (freq <= 24000000)
 | |
| 			reg |= 0x0111101;	// data,clk,cs = 6mA
 | |
| 		else if (freq <= 60000000)
 | |
| 			reg |= 0x0222202;	// data,clk = 9mA, cs = 6mA
 | |
| 		else // freq > 60MHz
 | |
| 			reg |= 0x0222203;	// data = 9mA, clk = 12mA, cs = 6mA
 | |
| 
 | |
| 		OUTW(IOADDR_PAD_REG_BASE + 0x50, reg);
 | |
| 	} else {
 | |
| 		/* Release SRAM */
 | |
| 		reg = INW(IOADDR_TOP_REG_BASE + 0x1000);
 | |
| 		reg &= ~(0x20000);
 | |
| 		OUTW(IOADDR_TOP_REG_BASE + 0x1000, reg);
 | |
| 
 | |
| 		/* Config driving */
 | |
| 		reg = INW(IOADDR_PAD_REG_BASE + 0x40);
 | |
| 		reg &= ~(0x3F00FF);
 | |
| 		reg |= 0x150055;
 | |
| 		OUTW(IOADDR_PAD_REG_BASE + 0x40, reg);
 | |
| 	}
 | |
| 
 | |
| 	nand_hostSetNandType(info, NANDCTRL_SPI_NAND_TYPE);
 | |
| 
 | |
| 	nand_host_settiming2(info, 0x9F51);
 | |
| 
 | |
| 	info->ops_freq = freq;
 | |
| 
 | |
| 	return nand_cmd_reset(info);
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| int nvt_nand_read_id(struct drv_nand_dev_info *info, uint32_t *id)
 | |
| {
 | |
| 	uint8_t  card_id[8];
 | |
| 
 | |
| 	if (nand_cmd_read_id(card_id, info) != 0) {
 | |
| 		printf("NAND cmd timeout\r\n");
 | |
| 		return -1;
 | |
| 	} else {
 | |
| 		printf("id =  0x%02x 0x%02x 0x%02x 0x%02x\n",
 | |
| 			card_id[0], card_id[1], card_id[2], card_id[3]);
 | |
| 
 | |
| 		*id = card_id[0] | (card_id[1] << 8) | (card_id[2] << 16) | \
 | |
| 			(card_id[3] << 24);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| int nvt_nand_scan_2plane_id(uint32_t id)
 | |
| {
 | |
| 	int32_t i, read_id,flash_id;
 | |
| 
 | |
| 	read_id = (id & 0x0000FFFF);
 | |
| 
 | |
| 	for (i = 0 ; i < NUM_OF_2PLANE_ID; i++) {
 | |
| 		flash_id = (nand_2plane_list[i].mfr_id + (nand_2plane_list[i].dev_id << 8));
 | |
| 		if (read_id == flash_id)
 | |
| 			return 1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int nvt_nand_board_nand_init(struct nand_chip *nand)
 | |
| {
 | |
| 	int32_t id;
 | |
| 	struct drv_nand_dev_info *info;
 | |
| 	u32 status;
 | |
| 
 | |
| #ifdef CONFIG_NVT_SPI_NONE
 | |
| 	return -ENOMEM;
 | |
| #endif
 | |
| 
 | |
| 	info = malloc(sizeof(struct drv_nand_dev_info));
 | |
| 	if (!info) {
 | |
| 		printf("alloc drv_nand_dev_info failed!\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	info->flash_info = malloc(sizeof(struct nvt_nand_flash));
 | |
| 	if (!info) {
 | |
| 		printf("alloc nvt_nand_flash failed!\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 	info->flash_info->spi_nand_status.bBlockUnlocked    = FALSE;
 | |
| 	info->flash_info->spi_nand_status.bQuadEnable       = FALSE;
 | |
| 	info->flash_info->spi_nand_status.bQuadProgram      = FALSE;
 | |
| 	info->flash_info->spi_nand_status.uiTimerRecord     = FALSE;
 | |
| 
 | |
| 	nand->priv = info;
 | |
| 	info->mmio_base = (void *)(CONFIG_SYS_NAND_BASE);
 | |
| 	info->use_ecc = CONFIG_NVT_NAND_ECC;
 | |
| 
 | |
| 	drv_nand_reset(info);
 | |
| 	nand_phy_config(info);
 | |
| 	nand_dll_reset();
 | |
| 
 | |
| 	/*Delay 1 ms for spinand characteristic*/
 | |
| 	mdelay(1);
 | |
| 
 | |
| 	nvt_nand_read_id(info, (uint32_t *)&id);
 | |
| 
 | |
| #ifndef CONFIG_FLASH_ONLY_DUAL
 | |
| 	if ((id & 0xFFFF) != TOSHIBA_TC58CVG)
 | |
| 		info->flash_info->spi_nand_status.bQuadProgram = TRUE;
 | |
| #endif
 | |
| 
 | |
| 	if (nvt_scan_nand_type(info, &id)) {
 | |
| 		printf("flash not support with id 0x%x\n", id);
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	info->data_buff = malloc(info->flash_info->page_size+info->flash_info->oob_size + CACHE_LINE_SIZE);
 | |
| 
 | |
| 	if (((unsigned long)(info->data_buff)) % CACHE_LINE_SIZE)
 | |
| 		info->data_buff = (uint8_t *)((((unsigned long)info->data_buff + CACHE_LINE_SIZE - 1)) & 0xFFFFFFC0);
 | |
| 
 | |
| 	if(info->data_buff == NULL) {
 | |
| 		printf("allocate nand buffer fail !\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	status = nand_cmd_read_status(info, NAND_SPI_STS_FEATURE_2);
 | |
| 
 | |
| 	if (info->use_ecc == NANDCTRL_SPIFLASH_USE_INTERNAL_RS_ECC)
 | |
| 		status &= ~SPINAND_CONFIG_ECCEN;
 | |
| 	else
 | |
| 		status |= SPINAND_CONFIG_ECCEN;
 | |
| 
 | |
| #ifndef CONFIG_FLASH_ONLY_DUAL
 | |
| 	if (((id & 0xFF) == NAND_MXIC) || ((id & 0xFF) == SPI_NAND_GD) || \
 | |
| 		((id & 0xFF) == SPI_NAND_DOSILICON) || ((id & 0xFF) == SPI_NAND_XTX)) {
 | |
| 		status |= SPINAND_CONFIG_QUADEN;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	nand_cmd_write_status(info, NAND_SPI_STS_FEATURE_2, status);
 | |
| 
 | |
| 	info->use_dma = 1;
 | |
| 	info->data_size = 0;
 | |
| 	info->state = STATE_READY;
 | |
| 
 | |
| 	if (info->flash_info->page_size == 512)
 | |
| 		nand->ecc.layout = &hw_smallpage_ecclayout;
 | |
| 	else
 | |
| 		nand->ecc.layout = &spinand_oob_64;
 | |
| 	nand->ecc.size = 0x200;
 | |
| 	nand->ecc.bytes = 0x8;
 | |
| 	nand->ecc.steps = 0x4;
 | |
| 	nand->ecc.total	= nand->ecc.steps * nand->ecc.bytes;
 | |
| 	nand->ecc.strength = 1;
 | |
| 
 | |
| 	nand->cmd_ctrl = drv_nand_hwcontrol;
 | |
| 	nand->dev_ready = drv_nand_dev_ready;
 | |
| 	nand->ecc.mode = NAND_ECC_HW;
 | |
| 	nand->ecc.read_page = drv_nand_read_page_ecc;
 | |
| 	nand->ecc.write_page = drv_nand_write_page_ecc;
 | |
| 	nand->options = NAND_NO_SUBPAGE_WRITE;
 | |
| 	nand->waitfunc = drv_nand_waitfunc;
 | |
| 	nand->read_byte = drv_nand_read_byte;
 | |
| 	nand->read_word = drv_nand_read_word;
 | |
| 	nand->read_buf = drv_nand_read_buf;
 | |
| 	nand->write_buf = drv_nand_write_buf;
 | |
| 	nand->cmdfunc = drv_nand_cmdfunc;
 | |
| 	nand->chip_delay = 0;
 | |
| 
 | |
| 	if (info->use_ecc == NANDCTRL_SPIFLASH_USE_INTERNAL_RS_ECC) {
 | |
| 		nand->ecc.strength = 4;
 | |
| 	} else {
 | |
| 		nand->ecc.strength = info->flash_info->ecc_strength;
 | |
| 	}
 | |
| 
 | |
| 	if (nvt_nand_scan_2plane_id(id) == 1) {
 | |
| 		info->flash_info->plane_flags = 1;
 | |
| 		//printf("2 plane en\n");
 | |
| 	} else {
 | |
| 		info->flash_info->plane_flags = 0;
 | |
| 		//printf("2 plane dis\n");
 | |
| 	}
 | |
| 
 | |
| 	printf("nvt spinand %d-bit mode @ %d Hz\n", \
 | |
| 		info->flash_info->spi_nand_status.bQuadProgram ? 4 : 1, \
 | |
| 		info->ops_freq);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | 
