| /* |
| * (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; |
| } |
| |
| |