| /* | 
 |  * Freescale QuadSPI driver. | 
 |  * | 
 |  * Copyright (C) 2013 Freescale Semiconductor, Inc. | 
 |  * | 
 |  * 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. | 
 |  */ | 
 | #include <sdio.h> | 
 | #include <common.h> | 
 | #include <asm/io.h> | 
 | #include "nor.h" | 
 | #include <image.h> | 
 | #include <linux/byteorder/generic.h> | 
 | #include <secure_verify.h> | 
 | #include "config.h" | 
 | #include "flash.h" | 
 |  | 
 |  | 
 | /* Used when the "_ext_id" is two bytes at most */ | 
 | #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\ | 
 | 		.id = {							\ | 
 | 			((_jedec_id) >> 16) & 0xff,			\ | 
 | 			((_jedec_id) >> 8) & 0xff,			\ | 
 | 			(_jedec_id) & 0xff,				\ | 
 | 			((_ext_id) >> 8) & 0xff,			\ | 
 | 			(_ext_id) & 0xff,				\ | 
 | 			},						\ | 
 | 		.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),	\ | 
 | 		.sector_size = (_sector_size),				\ | 
 | 		.n_sectors = (_n_sectors),				\ | 
 | 		.page_size = 256,					\ | 
 | 		.flags = (_flags), | 
 |  | 
 |  | 
 | spinor_cmd_t nor_cmd_table[]= | 
 | {	 | 
 | 	{CMD_RDFT, TX_DMA_DIS, RX_DMA_DIS, ADDR_TX_EN, ADDR_WIDTH_24, DATA_TX_DIS, DATA_RX_EN, DUMY_TX_EN, 1, 0, ADDR_MULTI_LINE_DIS, DATA_MULTI_LINE_DIS, TRANS_MOD_SINGLE, "read fast"}, | 
 | 	{CMD_RDID, TX_DMA_DIS, RX_DMA_DIS, ADDR_TX_DIS, ADDR_WIDTH_24, DATA_TX_DIS, DATA_RX_EN, DUMY_TX_DIS, 0, 0, ADDR_MULTI_LINE_DIS, DATA_MULTI_LINE_DIS, TRANS_MOD_SINGLE, "read identification"}, | 
 | 	{NULL} | 
 | }; | 
 |  | 
 | #ifdef CONFIG_ZX297520V3_UFI_MINI_32K_NOR | 
 | static const struct nor_info spi_nor_ids[] = { | 
 | 	/* GigaDevice */ | 
 | 	{ "gd25q128", INFO(0xc86018, 0, 32 * 1024, 512, 0) }, | 
 | 	/* winbond */	 | 
 | 	{ "w25q128fw", INFO(0xef6018, 0, 32 * 1024, 512, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "fm25m4aa", INFO(0xf84218, 0, 32 * 1024, 512, 0) }, | 
 | 	/* fudanwei */ | 
 | 	{ "fm25w128", INFO(0xA12818, 0, 32 * 1024, 512, 0) }, | 
 | 	/* XMC */ | 
 | 	{ "XM25QU64C", INFO(0x204117, 0, 32 * 1024, 256, 0) }, | 
 | 	{ "XM25QU128", INFO(0x205018, 0, 32 * 1024, 512, 0) }, | 
 | 	{ "XM25QU128C", INFO(0x204118, 0, 32 * 1024, 512, 0) }, | 
 | 	/* DQ25Q128AL */ | 
 | 	{ "DQ25Q128AL", INFO(0x546018, 0, 32 * 1024, 512, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "DS25M4AB", INFO(0xE54218, 0, 32 * 1024, 512, 0) }, | 
 | 	/* esmt(eon) */ | 
 | 	{ "EN25SX128A", INFO(0x1C7818, 0, 32 * 1024, 512, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "FM25M4AA", INFO(0xF84218, 0, 32 * 1024, 512, 0) }, | 
 | 	{ }, | 
 | }; | 
 | #else | 
 | static const struct nor_info spi_nor_ids[] = { | 
 | 	/* GigaDevice */ | 
 | 	{ "gd25q128", INFO(0xc86018, 0, 64 * 1024, 256, 0) }, | 
 | 	/* winbond */	 | 
 | 	{ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "fm25m4aa", INFO(0xf84218, 0, 64 * 1024, 256, 0) }, | 
 | 	/* fudanwei */ | 
 | 	{ "fm25w128", INFO(0xA12818, 0, 64 * 1024, 256, 0) }, | 
 | 	/* xmc */ | 
 | 	{ "XM25QU128", INFO(0x205018, 0, 64 * 1024, 256, 0) }, | 
 | 	{ "XM25QU128C", INFO(0x204118, 0, 64 * 1024, 256, 0) }, | 
 | 	/* DQ25Q128AL */ | 
 | 	{ "DQ25Q128AL", INFO(0x546018, 0, 64 * 1024, 256, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "DS25M4AB", INFO(0xE54218, 0, 64 * 1024, 256, 0) }, | 
 | 	/* esmt(eon) */ | 
 | 	{ "EN25SX128A", INFO(0x1C7818, 0, 64 * 1024, 256, 0) }, | 
 | 	/* dosilicon */ | 
 | 	{ "FM25M4AA", INFO(0xF84218, 0, 64 * 1024, 256, 0) }, | 
 | 	{ }, | 
 | }; | 
 | #endif | 
 |  | 
 |  | 
 | struct nor_info *spi_nor_flash = NULL; | 
 |  | 
 |  | 
 |  void spifc_enable(void) | 
 | { | 
 |    volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |      | 
 |     if(spifc->SFC_EN & FC_EN_BACK) | 
 |     { | 
 | 		printf("spifc en err.\n"); | 
 | 		return; | 
 | 	}    | 
 |     spifc->SFC_EN |= (1 << FC_EN);   | 
 |     spifc->SFC_CTRL0 |= (1 << FC_SCLK_PAUSE_EN); | 
 | } | 
 |  | 
 |  void spifc_disable(void) | 
 | { | 
 |     volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |      | 
 |     if(!(spifc->SFC_EN & FC_EN_BACK)) | 
 |     { | 
 | 		printf("spifc dis err.\n"); | 
 | 		return; | 
 | 	}      | 
 |     spifc->SFC_EN &= (~(1 <<FC_EN));     | 
 | } | 
 |  | 
 |  void spifc_setup_cmd(spinor_cmd_t *cmd, uint32_t addr, uint32_t len) | 
 | {    | 
 | 	volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |  | 
 | 	/* clear dma config */ | 
 | 	spifc->SFC_CTRL0 &= ~((1 << FC_RX_DMA_EN)|(1 << FC_TX_DMA_EN)); | 
 | 	/* clear fifo */ | 
 | 	spifc->SFC_CTRL0 |= (1 << FC_RXFIFO_CLR)|(1 << FC_TXFIFO_CLR); | 
 |  | 
 | 	/* clear interrupt register */ | 
 | 	spifc->SFC_INT_SW_CLR = 0xFF;   | 
 | 	 | 
 | 	/* dma + fifo config */ | 
 | 	spifc->SFC_CTRL0 |= ((cmd->tx_dma_en << FC_TX_DMA_EN)  | 
 | 						  | (cmd->rx_dma_en << FC_RX_DMA_EN) | 
 | 						  | (1 << FC_RXFIFO_THRES) | 
 | 						  | (1 << FC_TXFIFO_THRES)); | 
 |  | 
 | 	/* addr dumy data code config */ | 
 |     spifc->SFC_CTRL1 = 0; | 
 |     spifc->SFC_CTRL1 = ((cmd->addr_tx_en << FC_ADDR_TX_EN)  | 
 | 						| (cmd->dumy_tx_en << FC_DUMMY_TX_EN)  | 
 |                         | (cmd->data_rx_en << FC_READ_DAT_EN)  | 
 |                         | (cmd->data_tx_en << FC_WRITE_DAT_EN)); | 
 |  | 
 |     spifc->SFC_CTRL2 = 0; | 
 |     spifc->SFC_CTRL2 = ((cmd->dumy_byte_num << FC_DUMMY_BYTE_NUM)  | 
 |             			| (cmd->dumy_bit_num << FC_DUMMY_BIT_NUM)  | 
 | 	            		| (cmd->addr_byte_num << FC_ADDR_BYTE_NUM) | 
 | 	            		| (cmd->addr_multi_line_en << FC_ADDR_MULTI_LINE_EN) | 
 | 	            		| (cmd->data_multi_line_en << FC_DAT_MULTI_LINE_EN) | 
 | 	            		| (cmd->trans_mod << FC_TRANS_MOD)); | 
 |  | 
 |     if(len) | 
 |         spifc->SFC_BYTE_NUM = len - 1; | 
 |     else | 
 |         spifc->SFC_BYTE_NUM = 0;  | 
 | 	 | 
 | 	spifc->SFC_ADDR = addr;  | 
 |     spifc->SFC_INS = cmd->cmd;           | 
 | } | 
 |  | 
 |  int spifc_wait_cmd_end(void) | 
 | { | 
 |     volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |     uint32_t intr_status = 0; | 
 |  | 
 |     while(!(spifc->SFC_INT_RAW & FC_INT_RAW_MASK)); | 
 |      | 
 |     intr_status = spifc->SFC_INT_RAW; | 
 |     spifc->SFC_INT_SW_CLR = intr_status;           /* ??? */    | 
 |  | 
 | 	if(intr_status & FC_INT_RAW_CMD_END) | 
 |     {    | 
 |         return 0; | 
 |     } | 
 |     else | 
 |     { | 
 | 		printf("intr status err.\n"); | 
 |         return -1; | 
 |     }    | 
 | } | 
 |  | 
 | uint32_t spifc_read_fifo(uint8_t *buf, uint32_t len) | 
 | { | 
 |     volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |     uint32_t *p = (uint32_t *)buf; | 
 |     uint32_t cnt = 0; | 
 |  | 
 | 	int remainder_cnt = len % 4; | 
 |  | 
 | 	if(remainder_cnt != 0) | 
 | 	{ | 
 | 		len = len + (4 - remainder_cnt); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		remainder_cnt = 4; | 
 | 	} | 
 |     | 
 |     while(cnt < (len>>2))   | 
 |     { | 
 |         if(spifc->SFC_SW & (FC_RX_FIFO_CNT_MASK<<FC_RX_FIFO_CNT))//rx fifo not empty | 
 |         {            | 
 |             p[cnt++]= spifc->SFC_DATA; | 
 |         } | 
 |     } | 
 |  | 
 |     return ((cnt<<2) - (4 - remainder_cnt)); | 
 | } | 
 |  | 
 |  void spifc_start(void) | 
 | { | 
 |     volatile struct spifc_nor_reg_t* spifc = (struct spifc_nor_reg_t*)SYS_SPI_NAND_BASE; | 
 |      | 
 |     spifc->SFC_START |= FC_START; | 
 | } | 
 |  | 
 | spinor_cmd_t *cmd_seek(u8 opcode) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for(i = 0; (&nor_cmd_table[i]) != NULL; i++) | 
 | 	{ | 
 | 		if(opcode == nor_cmd_table[i].cmd) | 
 | 		{ | 
 | 			return (&nor_cmd_table[i]); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return NULL;	 | 
 | } | 
 |  | 
 | int nor_read_reg(u8 opcode, int len, u8 *buf) | 
 | { | 
 | 	int ret = 0; | 
 | 	spinor_cmd_t *cmd = NULL; | 
 | 	 | 
 | 	cmd = cmd_seek(opcode); | 
 | 	if(cmd == NULL) | 
 | 	{ | 
 | 		printf("cmd_seek unkown cmd error.\n"); | 
 | 		return -1; | 
 | 	} | 
 | 	 | 
 | 	spifc_setup_cmd(cmd, 0x0, len);             | 
 |     spifc_start();                           | 
 |     ret = spifc_wait_cmd_end();              | 
 |     if(ret != 0) | 
 |     { | 
 | 		printf("spifc_wait_cmd_end error.\n"); | 
 | 	   	return ret; | 
 |     } | 
 |  | 
 | 	ret = spifc_read_fifo(buf, len); | 
 | 	if(ret < 0) | 
 | 	{ | 
 | 		printf("spifc_read_fifo error.\n"); | 
 | 	    return ret; | 
 | 	} | 
 |  | 
 | 	return 0;	 | 
 | } | 
 |  | 
 | int nor_read_id(void) | 
 | { | 
 | 	int	tmp; | 
 | 	u8	id[SPI_NOR_MAX_ID_LEN]; | 
 | 	const struct nor_info	*info; | 
 |  | 
 | 	tmp = nor_read_reg(CMD_RDID, SPI_NOR_MAX_ID_LEN, id); | 
 | 	if(tmp < 0)  | 
 | 	{ | 
 | 		printf("error reading JEDEC ID\n"); | 
 | 		return tmp; | 
 | 	} | 
 |  | 
 | 	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++)  | 
 | 	{ | 
 | 		info = &spi_nor_ids[tmp]; | 
 | 		if(info->id_len)  | 
 | 		{ | 
 | 			if(!memcmp(info->id, id, info->id_len)) | 
 | 			{ | 
 | 				spi_nor_flash = info; | 
 | 				return 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	printf("unrecognized JEDEC id\n"); | 
 | 	return -1; | 
 | 	 | 
 | } | 
 |   | 
 | int spi_nor_read(uint32_t from, size_t len, u_char *buf) | 
 | { | 
 | 	int ret; | 
 | 	spinor_cmd_t *cmd = NULL; | 
 |  | 
 | 	cmd = cmd_seek(CMD_RDFT); | 
 | 	if(cmd == NULL) | 
 | 	{ | 
 | 		printf("cmd_seek unkown error.\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	spifc_setup_cmd(cmd, from, len);	             | 
 | 	spifc_start(); | 
 | 	ret = spifc_read_fifo(buf, len); | 
 | 	if(ret < 0) | 
 | 	{ | 
 | 		printf("spifc_read_fifo error.\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = spifc_wait_cmd_end();              | 
 |     if(ret != 0) | 
 |     { | 
 | 		printf("spifc_wait_cmd_end error.\n"); | 
 | 	   	return ret; | 
 |     } | 
 | 	 | 
 | 	return 0; | 
 | } | 
 |  | 
 | int nor_read(uint32_t from, uint32_t len, uint32_t to) | 
 | { | 
 | 	int ret; | 
 | 	u32 page_offset, page_size, i; | 
 | 	struct nor_info *info = spi_nor_flash; | 
 | 	 | 
 | 	page_offset = from & (info->page_size - 1); | 
 |  | 
 | 	/* do all the bytes fit onto one page? */ | 
 | 	if (page_offset + len <= info->page_size) { | 
 | 		ret = spi_nor_read(from, len, (uint8_t *)to); | 
 | 	} else { | 
 | 		/* the size of data remaining on the first page */ | 
 | 		page_size = info->page_size - page_offset; | 
 | 		ret = spi_nor_read(from, page_size, (uint8_t *)to); | 
 |  | 
 | 		/* read everything in nor->page_size chunks */ | 
 | 		for (i = page_size; i < len; i += page_size) { | 
 | 			page_size = len - i; | 
 | 			if (page_size > info->page_size) | 
 | 				page_size = info->page_size; | 
 |  | 
 | 			ret = spi_nor_read(from + i, page_size, ((uint8_t *)to + i)); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | int nor_init(void) | 
 | { | 
 | 	int ret = 0; | 
 |  | 
 | 	spifc_disable(); //hsy ? | 
 | 	 | 
 | 	spifc_enable(); | 
 |  | 
 | 	ret = nor_read_id(); | 
 | 	if(ret != 0) | 
 | 	{ | 
 | 		return -1; | 
 | 	} | 
 | 	 | 
 | 	flash.flash_type = NOR_BOOT; | 
 | 	flash.page_size = spi_nor_flash->page_size; | 
 | 	flash.read = nor_read;		 | 
 | 	 | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  ********************************************************************** | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  *	 Output: | 
 |  * Returns: | 
 |  * Others: | 
 |  ********************************************************************** | 
 |  */ | 
 | int board_flash_init(void) | 
 | { | 
 | 	int ret = 0; | 
 | 	char boot_mode = 0; | 
 | 	 | 
 | 	boot_mode = get_boot_mode(); | 
 | 	if(boot_mode != NOR_BOOT) | 
 | 	{ | 
 | 		printf("not nor flash.\n"); | 
 | 		return -1;  | 
 | 	} | 
 | 	 | 
 | 	writel(CFG_START_MODE_NOR, CFG_BOOT_MODE_START_MODE_FOR_UBOOT);  | 
 | 	ret = nor_init(); | 
 | 	if(ret != 0) | 
 | 	{ | 
 | 		printf("nor init err.\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	printf("nor init ok.\n"); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 |  |