blob: f4ddfd984fd43230ba96c4f15e4f1044664659c6 [file] [log] [blame]
/*
* (C) Copyright 2016 ZXIC Inc.
*/
#include <common.h>
#include <asm/arch/denali.h>
#include <asm/io.h>
#include <bbt.h>
#include "flash.h"
static struct nand_flash_device_para *nand_info = NULL;
/* nand flash parameter config */
static const struct nand_flash_device_para nand_flash_para[] = {
/* MT29F4G08ABBDAH4 for 7520V5 evb 512MB X 8 */
{0x2C, 0xAC, 0x90, 0, 4, 2048,11, 64, 17, 4096, 0x20000},
/* MT29F2G08ABBEA for 7520 evb 256MB X 8 */
{0x2C, 0xAA, 0x90, 0, 4, 2048,11, 64, 17, 2048, 0x20000},
/* K9F2G08U0B for 7520 FPGA */
{0xEC, 0xDA, 0x10, 0, 4, 2048,11, 64, 17,2048, 0x20000},
/* JSFBA3YHABB for 7510 evb 256MB X 8 ---> DDR 128M*/
{0xAD, 0xAA, 0x90, 0, 8, 2048, 11, 128, 17,2048, 0x20000},
/* FM6BD2G1GA for 7510 evb 256MB X 8 ---> DDR 128M */
{0xC8, 0xAA, 0x90, 0, 4, 2048, 11, 64, 17, 2048, 0x20000},
/* H9TA4GG4GDMCPR for 7510 evb 512MB X 8 ---> DDR 512M */
{0xAD, 0xAC, 0x90, 0, 8, 2048, 11, 128, 17, 4096, 0x20000},
/* JSFCBX3Y7ABB for 7510 evb 512MB X 8 ---> DDR 256M */
{0x01, 0xAC, 0x90, 0, 8, 2048, 11, 128, 17, 4096, 0x20000},
/* FM6BD4G2GA for 7510 evb 512MB X 8 ---> DDR 256M*/
{0xC8, 0xAC, 0x90, 0, 4, 2048, 11, 64, 17, 4096, 0x20000},
/* NM1281KSLAXAJ for 7510 evb 256MB X 8 ---> DDR 128M*/
{0x98, 0xAA, 0x90, 0, 8, 2048, 11, 128, 17, 2048, 0x20000},
/* W71NW20GF3FW for 7520 evb 256MB X 8 */
{0xEF, 0xAA, 0x90, 0, 4, 2048,11, 64, 17, 2048, 0x20000},
{0}
};
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static uint32_t wait_for_ready(uint32_t status_type)
{
uint32_t status = 0;
while (!(readl(INTR_STATUS(0)) & status_type));
status = readl(INTR_STATUS(0));
writew(0xffff, INTR_STATUS(0));
return status;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static void index_addr(uint32_t address, uint32_t data)
{
writel(address, NAND_DATA);
writel(data, NAND_DATA_10);
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static void index_addr_read_data(uint32_t address, uint32_t *pdata)
{
writel(address, NAND_DATA);
*pdata = readl(NAND_DATA_10);
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
void nand_read_oob(uint8_t *buf, uint32_t offset, uint32_t len)
{
uint32_t cmd, i, status_type;
uint32_t *buf32 = NULL;
uint32_t addr = offset >> (nand_info->page_size_shift); //addr = bank0 | page
/* 0. disable ECC */
writel(ECC_DISABLE__FLAG, ECC_ENABLE);
writel(0xffff, INTR_STATUS(0));
writel(TRANSFER_SPARE_REG__FLAG, TRANSFER_SPARE_REG);
/* setup page read request for SPARE_ACCESS read */
cmd = MODE_10 | addr;
index_addr((uint32_t)cmd, SPARE_ACCESS);
cmd = MODE_10 | addr;
index_addr((uint32_t)cmd, 0x2001);
/* page 33 of the NAND controller spec indicates we should not
use the pipeline commands in Spare area only mode. So we
don't.
*/
cmd = MODE_01 | addr;
writel(cmd, NAND_DATA);
status_type = wait_for_ready(INTR_STATUS__LOAD_COMP | INTR_STATUS__TIME_OUT);
if (status_type & INTR_STATUS__TIME_OUT)
printf("[OT]\n"); /* OOB TIMEOUT */
/* 0. read oob data */
buf32 = (uint32_t *)buf;
for (i = 0; i < len / 4; i++)
*buf32++ = readl(NAND_DATA_10);
/* We set the device back to MAIN_ACCESS here as I observed
* instability with the controller if you do a block erase
* and the last transaction was a SPARE_ACCESS. Block erase
* is reliable (according to the MTD test infrastructure)
* if you are in MAIN_ACCESS.
*/
cmd = MODE_10 | addr;
index_addr((uint32_t)cmd, MAIN_ACCESS);
}
void clear_intr(void)
{
while(readl(INTR_STATUS(0)))
{
writew(0xffff, INTR_STATUS(0));
}
writew(0xffff, INTR_STATUS(0));
nsdelay(400000);//0.02s
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t read_page(uint32_t buf, uint32_t offset)
{
uint32_t status = 0;
int32_t ecc_status = 0;
uint32_t page = offset >> (nand_info->page_size_shift);
uint32_t mode = MODE_10; /* MODE_10 | BANK(denali->flash_bank) */
if((buf & 0x3) != 0) /* DMAµØÖ·ÒªÇó4×Ö½Ú¶ÔÆë */
{
printf("[AE]\n"); /* [read_page]:ADDR ERROR */
return -1;
}
ecc_status =readl(ECC_CORRECTION);
if(page < 64)
{
writel(0x8, ECC_CORRECTION);
}
/* clear status */
writew(0xffff, INTR_STATUS(0));
writel(TRANSFER_MAIN_REG__FLAG, TRANSFER_SPARE_REG);
/* enable DMA */
writel(DMA_ENABLE__FLAG, DMA_ENABLE);
/* setup transfer type and # of pages -FOR DMA */
index_addr(mode | page, 0x2001); //mode10(0x08000000) | bank0(0) | page,
// 0x2000 | DENALI_READ(0) | 1
/* set memory high address bits 23:8 -FOR DMA */
index_addr(mode | ((uint16_t)(buf >> 16) << 8), 0x2200);
/* set memory low address bits 23:8 -FOR DMA */
index_addr(mode | ((uint16_t)buf << 8), 0x2300);
/* interrupt when complete, burst len = 64 bytes -FOR DMA*/
index_addr( mode | 0x14000, 0x2400); //zhouqi not interrupt 0X40
/* wait_for_ready */
status = wait_for_ready (INTR_STATUS__DMA_CMD_COMP | INTR_STATUS__ECC_ERR);
if (status & INTR_STATUS__ECC_ERR )
{
printf("[EE]\n"); /* [read_page]: ECC ERROR */
/* 0. clear status */
clear_intr();
return -1;
}
if (status & INTR_STATUS__TIME_OUT)
{
printf("TO\n"); /* [read_page]: TIME OUT */
/* clear status */
clear_intr();
return -1;
}
writel(ecc_status, ECC_CORRECTION);
/* disable DMA */
writel(DMA_DISABLE__FLAG, DMA_ENABLE);
return 0;
}
/*******************************************************************************
* from: must page align
len: must page align
*/
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t nand_read(uint32_t from, uint32_t len, uint32_t to)
{
uint32_t offset = from;
uint32_t left_to_read = len;
uint32_t p_to = to;
int32_t ret = 0;
int32_t page_size = (nand_info->page_size);
if((offset & (page_size - 1)) || (len & (page_size - 1)))
{
printf("param err.\n");
return -1;
}
while(left_to_read > 0)
{
ret = read_page(p_to, offset);
if(ret != 0)
{
return -1;
}
left_to_read -= page_size;
offset += page_size;
p_to += page_size;
}
return 0;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t read_data(uint32_t from, uint32_t len, uint32_t to)
{
uint32_t offset = from;
uint32_t left_to_read = len;
uint32_t p_to = to;
int32_t ret = 0;
int32_t block_size = (nand_info->block_size);
while( left_to_read > 0 )
{
uint32_t block_offset = offset & (block_size - 1);
uint32_t read_length;
if( nand_block_isbad(offset) )
{
offset += block_size;
continue;
}
if (left_to_read < (block_size - block_offset))
read_length = left_to_read;
else
read_length = block_size - block_offset;
ret = nand_read(offset, read_length, p_to);
if(ret != 0)
{
return -1;
}
left_to_read -= read_length;
offset += read_length;
p_to += read_length;
}
return 0;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t nand_read_id (void)
{
uint32_t id[5];
uint32_t i = 0;
uint32_t addr = (uint32_t)MODE_11; /* | BANK(0) */
struct nand_flash_device_para *nand = &nand_flash_para[0];
index_addr(addr | 0, 0x90);
index_addr(addr | 1, 0);
for (i = 0; i < 5; i++)
{
index_addr_read_data(addr | 2, &id[i]);
}
i = 0;
while( nand->manuf_id != 0 )
{
if( ((uint8_t)id[0] == nand->manuf_id) &&
((uint8_t)id[1] == nand->device_id) &&
((uint8_t)id[2] == nand->res_id))
{
nand_info = nand;
writel(nand_info->bus_num, DEVICE_WIDTH);
writel(nand_info->page_size, DEVICE_MAIN_AREA_SIZE);
writel(nand_info->page_size, LOGICAL_PAGE_DATA_SIZE);
writel(nand_info->oob_size, DEVICE_SPARE_AREA_SIZE);
writel(nand_info->ecc_strength, ECC_CORRECTION);
return 0;
};
nand++;
i++;
}
return 1;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t nand_init (void)
{
int32_t ret = 0;
writel(ECC_DISABLE__FLAG, ECC_ENABLE); /* ecc_enable */
writel(2, SPARE_AREA_SKIP_BYTES);
ret = nand_read_id();
if( ret != 0 )
return -1;
flash.flash_type = NAND_BOOT;
flash.manuf_id = nand_info->manuf_id;
flash.device_id = nand_info->device_id;
flash.page_size = nand_info->page_size;
flash.page_size_shift = nand_info->page_size_shift;
flash.oob_size = nand_info->oob_size;
flash.block_size = nand_info->block_size;
flash.block_size_shift = nand_info->block_size_shift;
flash.block_num = nand_info->block_num;
flash.read = read_data;
flash.read_oob = nand_read_oob;
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 != NAND_BOOT)
{
printf("not nand flash.\n");
return -1;
}
writel(CFG_START_MODE_NAND, CFG_BOOT_MODE_START_MODE_FOR_UBOOT);
ret = nand_init();
if(ret != 0)
{
printf("nand init err.\n");
return -1;
}
printf("nand init ok.\n");
nand_creat_bbt();
return 0;
}