blob: afccaf91e22c96d1138f3a4555e528b878ef4cdf [file] [log] [blame]
/*
* 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;
}