blob: ad5c57c42957404f48a712b1888106bba11b342e [file] [log] [blame]
/******************************************************************************
*
* alt_nand.c - API for the Altera SoC FPGA NAND device.
*
******************************************************************************/
/******************************************************************************
*
* Copyright 2013 Altera Corporation. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
******************************************************************************/
#include "hwlib.h"
#include "alt_clock_manager.h"
#include "alt_nand_flash.h"
#include "socal/alt_nand.h"
#include "socal/alt_rstmgr.h"
#include "socal/alt_sysmgr.h"
#include "socal/hps.h"
#include "socal/socal.h"
#include "alt_nand_private.h"
//#include "denali_flash_regs.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
static int s_nand_is_interrupt_enabled = false;
static uint32_t g_nand_interrup_status_register_poll_counter_limit;
static nand_interrupt_handler_t s_nand_interrupt_handler = NULL;
uint32_t alt_nand_number_blocks_of_plane_get(void);
static __inline uint32_t alt_nand_compose_map01_cmd_addr(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr);
static __inline uint32_t alt_nand_get_interrupt_status_register_addr(const uint32_t bank);
static __inline uint32_t alt_nand_get_interrupt_enable_register_addr(const uint32_t bank);
static __inline uint32_t alt_nand_get_device_reset_register_bank(const uint32_t bank);
uint32_t nand_read_register(const uint32_t offset);
static uint32_t real_table[ALT_HHP_NAND_NUM_OF_BLOCK_TOTAL/32];
alt_nand_bad_block_table_t nand_bad_block_table = real_table;
ALT_NAND_MGR_t nand_io =
{
(ALT_NAND_CFG_raw_t*)ALT_NAND_CFG_ADDR,
(ALT_NAND_PARAM_raw_t*)ALT_NAND_PARAM_ADDR,
(ALT_NAND_STAT_raw_t*)ALT_NAND_STAT_ADDR,
(ALT_NAND_ECC_raw_t*)ALT_NAND_ECC_ADDR,
(ALT_NAND_DMA_raw_t*)ALT_NAND_DMA_ADDR,
(uint32_t *)ALT_NANDDATA_ADDR, /*control_address */
ALT_CAST(uint32_t *, (ALT_CAST(char *, (ALT_NANDDATA_ADDR)) + 0x10))
};
ALT_NAND_MGR_t * nand = &nand_io;
FLASH_CHARACTERIZATION_t memory =
{
/*manufacturer id */ ALT_HHP_NAND_MANUFACTURER_ID,
/*device_id */ ALT_HHP_NAND_DEVICE_ID,
/*device_param_0 */ 0,
/*device_param_1 */ 0,
/*device_param_2 */ 0,
/*page_size */ ALT_HHP_NAND_PAGE_SIZE,
/*spare_size */ ALT_HHP_NAND_SPARE_SIZE,
/*revision */ ALT_HHP_NAND_REVISION,
/*onfi_device_feature */ 0,
/*onfi_optional_commands */ 0,
/*onfi_timing_mode */ 0,
/*onfi_pgm_cache_timing_mode */ 0,
/*onfi_compliant */ 1,
/*onfi_device_no_of_luns */ 0,
/*onfi_device_no_of_blocks_per_lun */ 0,
/*features */ 0,
/*number_of_planes */ ALT_HHP_NAND_NUMBER_OF_PLANES,
/*pages_per_block */ ALT_HHP_NAND_PAGES_PER_BLOCK,
/*device_width */ ALT_HHP_NAND_DEVICE_WIDTH,
/*block_size */ ALT_HHP_NAND_PAGES_PER_BLOCK * ALT_HHP_NAND_PAGE_SIZE,
/*spare_area_skip_bytes */ ALT_HHP_NAND_SPARE_SKIP,
/*first block of next plane */ ALT_HHP_NAND_FIRST_BLOCK_OF_NEXT_PLANE,
/*page_size_in_32 */ ALT_HHP_NAND_PAGE_SIZE / sizeof(uint32_t),
/*block_shift */ ALT_HHP_NAND_BLOCK_SHIFT,
/*dma_burst_length */ 0,
/*ecc_correct */ ALT_HHP_NAND_ECC_CORRECT,
};
FLASH_CHARACTERIZATION_t * flash = &memory;
ALT_STATUS_CODE alt_nand_flash_init(const bool load_block0_page0,
const bool page_size_512,
alt_nand_flash_custom_init_t custom_init,
void *user_arg)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
ALT_NAND_PARAM_raw_t * param = (ALT_NAND_PARAM_raw_t *)(nand->param);
ALT_STATUS_CODE ret = ALT_E_SUCCESS;
uint32_t x;
alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_NAND_SET_MSK);
alt_nand_set_sysmgr_bootstrap_value( ALT_NAND_BOOTSTRAP_INHIBIT_INIT_DISABLE,
load_block0_page0,
page_size_512,
ALT_NAND_BOOTSTRAP_TWO_ROW_ADDR_CYCLES_DISABLE
);
alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_NAND_SET_MSK);
g_nand_interrup_status_register_poll_counter_limit = (uint32_t)(-1);
ret = (*custom_init)(user_arg);
if (ret == ALT_E_RESERVED) // no custom initialization being done
{
alt_nand_reset_bank(0);
}
// Read flash device characterization
flash->manufacturer_id = alt_read_word(&param->manufacturer_id);
flash->device_id = alt_read_word(&param->device_id);
flash->device_param_0 = alt_read_word(&param->device_param_0);
flash->device_param_1 = alt_read_word(&param->device_param_1);
flash->device_param_2 = alt_read_word(&param->device_param_2);
flash->page_size = alt_read_word(&cfg->device_main_area_size);
flash->spare_size = alt_read_word(&cfg->device_spare_area_size);
flash->revision = alt_read_word(&param->revision);
flash->onfi_device_features = alt_read_word(&param->onfi_device_features);
flash->onfi_optional_commands = alt_read_word(&param->onfi_optional_commands);
flash->onfi_timing_mode = alt_read_word(&param->onfi_timing_mode);
flash->onfi_pgm_cache_timing_mode = alt_read_word(&param->onfi_pgm_cache_timing_mode);
flash->onfi_compliant = alt_read_word(&param->onfi_device_no_of_luns) >> 8;
flash->onfi_device_no_of_luns = alt_read_word(&param->onfi_device_no_of_luns) & 0xff;
x = alt_read_word(&param->onfi_device_no_of_blocks_per_lun_l);
flash->onfi_device_no_of_blocks_per_lun = (alt_read_word(&param->onfi_device_no_of_blocks_per_lun_u) << 16) + x;
flash->features = alt_read_word(&param->features);
x = alt_read_word(&cfg->number_of_planes);
switch (x)
{
case 0:
flash->number_of_planes = 1;
break;
case 1:
flash->number_of_planes = 2;
break;
case 3:
flash->number_of_planes = 4;
break;
case 7:
flash->number_of_planes = 4;
break;
default:
flash->number_of_planes = 1;
break;
}
flash->pages_per_block = alt_read_word(&cfg->pages_per_block);
// Device Width register content should automatically update SystemManager:NandGrp:BootStrap:page512 or page512x16 bit
flash->device_width = alt_read_word(&cfg->device_width);
// Set the skip bytes and then read back the result.
alt_write_word(&cfg->spare_area_skip_bytes, flash->spare_area_skip_bytes);
flash->spare_area_skip_bytes = alt_read_word(&cfg->spare_area_skip_bytes);
flash->block_size = flash->pages_per_block * flash->page_size;
flash->first_block_of_next_plane = alt_read_word(&cfg->first_block_of_next_plane);
// Set flash config based on read config
flash->page_size_32 = flash->page_size / sizeof(uint32_t);
flash->page_shift = ffs32(flash->page_size);
flash->block_shift = ffs32(flash->pages_per_block);
alt_nand_rb_pin_mode_clear(ALT_NAND_CFG_RB_PIN_END_BANK0_SET_MSK);
alt_nand_flash_ecc_disable();
alt_write_word(&cfg->first_block_of_next_plane,alt_nand_number_blocks_of_plane_get());
flash->first_block_of_next_plane = alt_read_word(&cfg->first_block_of_next_plane);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_nand_flash_uninit(void)
{
alt_nand_flash_ecc_disable();
alt_nand_dma_set_enabled( false );
return ALT_E_SUCCESS;
}
#define ALT_NAND_INVALID_FLASH_ADDR 0xffffffff
uint32_t alt_nand_block_address_get(const uint32_t addr)
{
return addr >> (flash->block_shift + flash->page_shift);
}
uint32_t alt_nand_page_address_get(const uint32_t addr)
{
return addr >> flash->page_shift & ((1 << flash->block_shift) - 1);
}
uint32_t alt_nand_flash_addr_compose(const uint32_t block_num,
const uint32_t page_num)
{
return (block_num << (flash->block_shift + flash->page_shift)) + (page_num << flash->page_shift);
}
ALT_STATUS_CODE alt_nand_flash_block_erase(const uint32_t block_addr,
alt_nand_callback_t completion_callback,
void *completion_arg)
{
int32_t ret = ALT_E_SUCCESS;
int32_t res = -1;
uint32_t bank = alt_nand_bank_get();
const uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr( bank );
const uint32_t addr = alt_nand_compose_map10_cmd_addr( bank, block_addr, 0 );
alt_write_word(interrup_status_register, ALT_HHP_UINT32_MASK );
alt_nand_write_indirect( addr, ALT_HHP_NAND_10_OP_ERASE_BLOCK );
res = alt_nand_poll_for_interrupt_status_register(interrup_status_register,
ALT_NAND_INT_STATUS_TIME_OUT |
ALT_NAND_INT_STATUS_ERASE_COMP |
ALT_NAND_INT_STATUS_ERASE_FAIL);
if (!(res & ALT_NAND_STAT_INTR_STAT0_ERASE_COMP_SET_MSK))
{
//printf("FAIL: erasing not complete. (%08lX, %ld)\n", res, block_addr);
ret = ALT_E_ERROR;
}
return ret;
}
ALT_STATUS_CODE alt_nand_flash_page_read(const uint32_t page_addr,
const uint32_t num_pages,
void *dest,
const uint32_t dest_size)
{
// currently only one page
uint32_t bank = alt_nand_bank_get();
uint32_t page = alt_nand_page_address_get(page_addr);
uint32_t block = alt_nand_block_address_get(page_addr);
return alt_nand_full_page_read_with_map10( bank, block, page, (uint32_t *)dest);
}
ALT_STATUS_CODE alt_nand_flash_page_write(const uint32_t page_addr,
const uint32_t num_pages,
const void *src,
const uint32_t src_size)
{
// currently only one page
uint32_t bank = alt_nand_bank_get();
uint32_t page = alt_nand_page_address_get(page_addr);
uint32_t block = alt_nand_block_address_get(page_addr);
return alt_nand_full_page_write_with_map10( bank, block, page, (uint32_t *)src);
}
ALT_STATUS_CODE alt_nand_flash_page_dma_read(const uint32_t page_addr,
const uint32_t num_pages,
void *dest,
const uint32_t dest_size,
alt_nand_callback_t completion_callback,
void *completion_arg)
{
ALT_STATUS_CODE status = ALT_E_SUCCESS;
uint32_t mem_addr = page_addr;
uint32_t dest_addr = (uint32_t)dest;
uint32_t buff_size = dest_size;
uint32_t bank = alt_nand_bank_get();
uint32_t page;
uint32_t block;
for (int i = 0; (i < num_pages) && (buff_size >= flash->page_size); i++) {
page = alt_nand_page_address_get(mem_addr);
block = alt_nand_block_address_get(mem_addr);
status = alt_nand_dma_page_read(bank, block, page, dest_addr);
if (status == ALT_E_SUCCESS)
{
mem_addr += flash->page_size;
dest_addr += flash->page_size;
buff_size -= flash->page_size;
}
else
break;
}
if (status == ALT_E_SUCCESS)
{
(*completion_callback)(status, completion_arg);
}
return status;
}
ALT_STATUS_CODE alt_nand_flash_page_dma_write(const uint32_t page_addr,
const uint32_t num_pages,
const void *src,
const uint32_t src_size,
alt_nand_callback_t completion_callback,
void *completion_arg)
{
ALT_STATUS_CODE status = ALT_E_SUCCESS;
uint32_t mem_addr = page_addr;
uint32_t src_addr = (uint32_t)src;
uint32_t buff_size = src_size;
uint32_t bank = alt_nand_bank_get();
uint32_t page;
uint32_t block;
for (int i = 0; (i < num_pages) && (buff_size >= flash->page_size); i++) {
page = alt_nand_page_address_get(mem_addr);
block = alt_nand_block_address_get(mem_addr);
status = alt_nand_dma_page_write(bank, block, page, src_addr);
if (status == ALT_E_SUCCESS)
{
mem_addr += flash->page_size;
src_addr += flash->page_size;
buff_size -= flash->page_size;
}
else
break;
}
if (status == ALT_E_SUCCESS)
{
(*completion_callback)(status, completion_arg);
}
return status;
}
ALT_STATUS_CODE alt_nand_flash_ecc_enable(const ALT_NAND_ECC_CORRECTION_t ecc_correction)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
alt_setbits_word(&cfg->ecc_enable, ALT_NAND_CFG_ECC_EN_FLAG_SET_MSK);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_nand_flash_ecc_disable(void)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
alt_clrbits_word(&cfg->ecc_enable, ALT_NAND_CFG_ECC_EN_FLAG_SET_MSK);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_nand_flash_ecc_status_get(ALT_NAND_FLASH_ECC_STATUS_t *ecc_status)
{
ALT_NAND_ECC_raw_t * ecc = (ALT_NAND_ECC_raw_t *)(nand->ecc);
uint32_t status;
status = alt_read_word(&ecc->ECCCorInfo_b01);
ecc_status->corrected_errors[0] = (status & ALT_NAND_ECC_ECCCORINFO_B01_MAX_ERRORS_B0_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B01_MAX_ERRORS_B0_LSB;
ecc_status->corrected_errors[1] = (status & ALT_NAND_ECC_ECCCORINFO_B01_MAX_ERRORS_B1_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B01_MAX_ERRORS_B1_LSB;
ecc_status->uncorrected_error[0] = (status & ALT_NAND_ECC_ECCCORINFO_B01_UNCOR_ERR_B0_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B01_UNCOR_ERR_B0_LSB;
ecc_status->uncorrected_error[1] = (status & ALT_NAND_ECC_ECCCORINFO_B01_UNCOR_ERR_B1_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B01_UNCOR_ERR_B1_LSB;
status = alt_read_word(&ecc->ECCCorInfo_b23);
ecc_status->corrected_errors[2] = (status & ALT_NAND_ECC_ECCCORINFO_B23_MAX_ERRORS_B2_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B23_MAX_ERRORS_B2_LSB;
ecc_status->corrected_errors[3] = (status & ALT_NAND_ECC_ECCCORINFO_B23_MAX_ERRORS_B3_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B23_MAX_ERRORS_B3_LSB;
ecc_status->uncorrected_error[2] = (status & ALT_NAND_ECC_ECCCORINFO_B23_UNCOR_ERR_B2_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B23_UNCOR_ERR_B2_LSB;
ecc_status->uncorrected_error[3] = (status & ALT_NAND_ECC_ECCCORINFO_B23_UNCOR_ERR_B3_SET_MSK) >> ALT_NAND_ECC_ECCCORINFO_B23_UNCOR_ERR_B3_LSB;
return ALT_E_SUCCESS;
}
uint32_t alt_nand_int_status_get(void)
{
uint32_t bank = alt_nand_bank_get();
uint32_t reg = alt_nand_get_interrupt_status_register_addr(bank);
return alt_read_word(reg);
}
ALT_STATUS_CODE alt_nand_int_clear(const uint32_t mask)
{
uint32_t bank = alt_nand_bank_get();
uint32_t reg = alt_nand_get_interrupt_status_register_addr(bank);
alt_setbits_word(reg, mask);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_nand_int_disable(const uint32_t mask)
{
uint32_t bank = alt_nand_bank_get();
uint32_t reg = alt_nand_get_interrupt_enable_register_addr(bank);
alt_clrbits_word(reg, mask);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_nand_int_enable(const uint32_t mask)
{
uint32_t bank = alt_nand_bank_get();
uint32_t reg = alt_nand_get_interrupt_enable_register_addr(bank);
alt_setbits_word(reg, mask);
return ALT_E_SUCCESS;
}
uint32_t alt_nand_num_planes_get(void)
{
return flash->number_of_planes;
}
uint32_t alt_nand_number_blocks_of_plane_get(void)
{
return (flash->onfi_device_no_of_blocks_per_lun / flash->number_of_planes);
}
uint32_t alt_nand_first_block_of_next_plane(void)
{
return flash->first_block_of_next_plane;
}
uint32_t alt_nand_num_blocks_get(void)
{
return (alt_nand_num_planes_get() * alt_nand_number_blocks_of_plane_get()) ;
}
uint32_t alt_nand_num_pages_per_block_get(void)
{
return flash->pages_per_block;
}
uint32_t alt_nand_sector_size_get(void)
{
return ALT_HHP_NAND_SECTOR_SIZE;
}
uint32_t alt_nand_spare_size_get(void)
{
return flash->spare_size;
}
bool alt_nand_block_is_bad(const uint32_t block_addr)
{
return false;
}
ALT_STATUS_CODE alt_nand_bad_block_table_get(alt_nand_bad_block_table_t bad_block_table,
const uint32_t bad_block_table_len)
{
alt_nand_callback_t dma_callback = &alt_nand_dma_page_callback;
uint32_t completion_arg = 0;
uint32_t total_blocks = (flash->onfi_device_no_of_luns * flash->onfi_device_no_of_blocks_per_lun);
uint32_t addr = (total_blocks - 1) << (flash->block_shift + flash->page_shift); // highest block is reseved for bad block table
uint32_t buff[ALT_HHP_NAND_PAGE_SIZE];
alt_nand_flash_page_dma_read( addr, 1, buff, sizeof(buff), dma_callback, &completion_arg);
for (int i = 0; i <ALT_HHP_NAND_NUM_OF_BLOCK_TOTAL/32; i++)
{
bad_block_table[i] = buff[i];
}
return ALT_E_SUCCESS;
}
void alt_nand_rb_pin_mode_set(uint32_t mask)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
alt_setbits_word(&cfg->rb_pin_enabled, mask);
}
void alt_nand_rb_pin_mode_clear(uint32_t mask)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
alt_clrbits_word(&cfg->rb_pin_enabled, mask);
}
void alt_nand_reset_bank(uint32_t bank)
{
ALT_NAND_CFG_raw_t * cfg = (ALT_NAND_CFG_raw_t *)(nand->cfg);
uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr(bank);
uint32_t device_reset_bank = alt_nand_get_device_reset_register_bank(bank);
// Write on clear of all interrupt status
alt_write_word(interrup_status_register, ALT_HHP_UINT32_MASK);
// Controller sends a RESET command to device
alt_setbits_word(&cfg->device_reset, device_reset_bank);
alt_nand_poll_for_interrupt_status_register(interrup_status_register, ALT_NAND_STAT_INTR_STAT0_RST_COMP_SET_MSK);
// Write on clear of all interrupt status
alt_write_word(interrup_status_register, ALT_HHP_UINT32_MASK);
}
/*
* Count the consecutive zero bits (trailing) on the right in parallel
*
* Some bit fiddleing stuff.
* From... http://graphics.stanford.edu/~seander/bithacks.html
*/
uint32_t ffs32(uint32_t v)
{
uint32_t r = 0;
do
{
if(v == 0)
break;
if(v & 0xFFFF0000){v >>= 16;r |= 16;}
if(v & 0x0000FF00){v >>= 8;r |= 8;}
if(v & 0x000000F0){v >>= 4;r |= 4;}
if(v & 0x0000000C){v >>= 2;r |= 2;}
if(v & 0x00000002){ ;r |= 1;}
} while(0);
return(r);
}
uint32_t alt_nand_poll_for_interrupt_status_register(uint32_t interrup_status_register, uint32_t interrup_status_mask )
{
uint32_t ret;
uint32_t i = 0;
if ( !s_nand_is_interrupt_enabled )
{
ret = alt_read_word( interrup_status_register );
while( !( ret & (interrup_status_mask | ALT_NAND_INT_STATUS_UNSUP_CMD ) ) )
{
ret = alt_read_word( interrup_status_register );
//printf("<alt_nand_poll for interrupt status register>->interrupt status reg = %08lX\n", ret);
if ( ++i == g_nand_interrup_status_register_poll_counter_limit )
{
break;
}
}
if ( ret & ALT_NAND_STAT_INTR_EN0_UNSUP_CMD_SET_MSK )
{
//printf( "Warning: Unsupported CMD interrupt INTR_STATUS_UNSUP_CMD is raised!!!!\n" );
}
}
else
{
ret = s_nand_interrupt_handler( interrup_status_register, interrup_status_mask );
}
return ret;
}
int32_t alt_nand_full_page_read_with_map10(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, uint32_t *buffer )
{
int32_t ret = ALT_E_SUCCESS;
int32_t res = -1;
const uint32_t PAGES_TO_READ = 1; // NOTE: Only 2 bits wide
const uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr( bank );
const uint32_t addr = alt_nand_compose_map10_cmd_addr( bank, block_addr, page_addr );
alt_write_word( interrup_status_register, ALT_HHP_UINT32_MASK );
// Sets up a pipeline read-ahead of for a read (0x2001)
alt_nand_write_indirect( addr, ALT_HHP_NAND_10_OP_READ_PIPE | PAGES_TO_READ );
res = alt_nand_poll_for_interrupt_status_register( interrup_status_register,
ALT_NAND_INT_STATUS_TIME_OUT |
ALT_NAND_INT_STATUS_LOAD_COMP );
if((res & ALT_NAND_INT_STATUS_LOAD_COMP) != 0)
{
alt_nand_full_page_read_with_map10_post_read_with_map01( bank, block_addr, page_addr, buffer);
}
else
{
//printf("FAIL: Timeout loading NAND page. (%08lX,%ld,%ld)\n", res, bank, page_addr);
ret = ALT_E_ERROR;
}
return ret;
}
uint32_t alt_nand_get_device_reset_register_bank(const uint32_t bank)
{
uint32_t ret=0;
switch( bank )
{
case 0:
ret = ALT_NAND_CFG_DEVICE_RST_BANK0_SET_MSK;
break;
case 1:
ret = ALT_NAND_CFG_DEVICE_RST_BANK1_SET_MSK;
break;
case 2:
ret = ALT_NAND_CFG_DEVICE_RST_BANK2_SET_MSK;
break;
case 3:
ret = ALT_NAND_CFG_DEVICE_RST_BANK3_SET_MSK;
break;
default:
// info_assert(0, "Do not support more than 4 banks");
break;
}
return ret;
}
uint32_t alt_nand_get_interrupt_status_register_addr(const uint32_t bank)
{
ALT_NAND_STAT_raw_t * stat = (ALT_NAND_STAT_raw_t *)(nand->stat);
uint32_t ret=0;
switch( bank )
{
case 0:
ret = (uint32_t)(&stat->intr_status0);
break;
case 1:
ret = (uint32_t)(&stat->intr_status1);
break;
case 2:
ret = (uint32_t)(&stat->intr_status2);
break;
case 3:
ret = (uint32_t)(&stat->intr_status3);
break;
default:
// info_assert(0, "Do not support more than 4 banks");
break;
}
return ret;
}
uint32_t alt_nand_get_interrupt_enable_register_addr(uint32_t bank)
{
uint32_t interrup_enable_register=0;
ALT_NAND_STAT_raw_t * stat = (ALT_NAND_STAT_raw_t *)(nand->stat);
switch( bank )
{
case 0:
interrup_enable_register = (uint32_t)(&stat->intr_en0);
//printf("interrupt status register: %08lX \n", interrup_enable_register);
break;
case 1:
interrup_enable_register = (uint32_t)(&stat->intr_en1);
break;
case 2:
interrup_enable_register = (uint32_t)(&stat->intr_en2);
break;
case 3:
interrup_enable_register = (uint32_t)(&stat->intr_en3);
break;
default:
// info_assert(0, "Do not support more than 4 banks");
break;
}
return interrup_enable_register;
}
uint32_t alt_nand_compose_map01_cmd_addr(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr)
{
const uint32_t BANK_MASK = 0x3;
const uint32_t BLOCK_ADDR_MASK = (1 << (23 - flash->block_shift + 1)) - 1;
const uint32_t PAGE_ADDR_MASK = (1 << flash->block_shift) - 1;
const uint32_t ret = ALT_HHP_NAND_MODE_01 |
((bank & BANK_MASK) << ALT_HHP_NAND_ADDR_MAP_BANK_SEL_LSB_INDEX) |
((block_addr & BLOCK_ADDR_MASK) << flash->block_shift) |
(page_addr & PAGE_ADDR_MASK);
return ret;
}
uint32_t alt_nand_compose_map10_cmd_addr(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr)
{
const uint32_t BANK_MASK = 0x3;
const uint32_t BLOCK_ADDR_MASK = (1 << (23 - flash->block_shift + 1)) - 1;
const uint32_t PAGE_ADDR_MASK = (1 << flash->block_shift) - 1;
const uint32_t ret = ALT_HHP_NAND_MODE_10 |
((bank & BANK_MASK) << ALT_HHP_NAND_ADDR_MAP_BANK_SEL_LSB_INDEX) |
((block_addr & BLOCK_ADDR_MASK) << flash->block_shift) |
(page_addr & PAGE_ADDR_MASK);
return ret;
}
void alt_nand_write_indirect(const uint32_t address, const uint32_t value)
{
alt_write_word(nand->ctrl_addr, address);
alt_write_word(nand->data_addr, value);
}
uint32_t alt_nand_read_indirect(const uint32_t address)
{
alt_write_word(nand->ctrl_addr, address);
return alt_read_word(nand->data_addr);
}
void alt_nand_full_page_read_with_map10_post_read_with_map01(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, uint32_t *buffer)
{
const uint32_t addr = alt_nand_compose_map01_cmd_addr( bank, block_addr, page_addr );
uint32_t *cur = buffer;
uint32_t i;
for( i = 0; i < flash->page_size_32; ++i )
{
*cur++ = alt_nand_read_indirect( addr );
}
}
int32_t alt_nand_full_page_write_with_map10(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, const uint32_t *buffer)
{
int32_t ret = 0;
int32_t res = -1;
const uint32_t PAGES_TO_READ = 1; // NOTE: Only 2 bits wide
const uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr( bank );
const uint32_t addr = alt_nand_compose_map10_cmd_addr( bank, block_addr, page_addr );
alt_write_word( interrup_status_register, ALT_HHP_UINT32_MASK );
// Sets up a pipeline read-ahead of “01” pages.“W” = 0 for a read (0x2001)
alt_nand_write_indirect( addr, ALT_HHP_NAND_10_OP_WRITE_PIPE | PAGES_TO_READ );
//
// don't forget this one, it is a hardware bug
//
for (int i = 0; i < 10000; i++) ;
// Temporarily disable status check as RTL doesn't seem to raise any flag.
// But, it works.
//res = alt_nand_poll_for_interrupt_status_register( interrup_status_register, INTR_STATUS0__TIME_OUT | INTR_STATUS0__PIPE_CPYBCK_CMD_COMP );
res = ALT_NAND_INT_STATUS_PIPE_CPYBCK_CMD_COMP;
if((res & ALT_NAND_INT_STATUS_PIPE_CPYBCK_CMD_COMP) != 0)
{
alt_nand_full_page_write_with_map10_post_write_with_map01( bank, block_addr, page_addr, buffer );
}
else
{
//printf("FAIL: Timeout loading NAND page. (%08lX,%ld,%ld)\n", res, bank, page_addr);
ret = 1;
}
return ret;
}
void alt_nand_full_page_write_with_map10_post_write_with_map01(const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, const uint32_t *buffer)
{
const uint32_t addr = alt_nand_compose_map01_cmd_addr( bank, block_addr, page_addr );
const uint32_t *cur = buffer;
uint32_t i;
for( i = 0; i < flash->page_size_32; ++i )
{
alt_nand_write_indirect( addr, *cur++ );
}
}
void alt_nand_set_sysmgr_bootstrap_value( uint32_t bootstrp_inhibit_init,
uint32_t bootstrp_inhibit_b0p0_load,
uint32_t bootstrp_512b_device,
uint32_t bootstrp_two_row_addr_cycles)
{
uint32_t settings = ALT_SYSMGR_NAND_BOOTSTRAP_NOINIT_SET(bootstrp_inhibit_init) |
ALT_SYSMGR_NAND_BOOTSTRAP_PAGE512_SET(bootstrp_512b_device) |
ALT_SYSMGR_NAND_BOOTSTRAP_NOLDB0P0_SET(bootstrp_inhibit_b0p0_load) |
ALT_SYSMGR_NAND_BOOTSTRAP_TWOROWADDR_SET(bootstrp_two_row_addr_cycles);
alt_write_word(ALT_SYSMGR_NAND_BOOTSTRAP_ADDR, settings);
}
uint32_t alt_nand_bank_get(void)
{
// on SOC, only bank 0 of physical memory is connected
return ALT_NAND_FLASH_MEM_BANK_0;
}
int32_t alt_nand_dma_page_write( const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, uint32_t mem_addr )
{
int32_t ret = 0;
uint32_t res;
const uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr( bank );
alt_write_word( interrup_status_register, ALT_HHP_UINT32_MASK );
alt_nand_dma_set_enabled( true );
alt_nand_dma_write_cmd_structure( bank, block_addr, page_addr, 1, mem_addr, false, 64);
res = alt_nand_poll_for_interrupt_status_register( interrup_status_register,
ALT_NAND_INT_STATUS_DMA_CMD_COMP |
ALT_NAND_INT_STATUS_PROGRAM_FAIL |
ALT_NAND_INT_STATUS_LOCKED_BLK );
// 10.8. Order of interrupt status bits assertion 8.
if ( !(res & ALT_NAND_STAT_INTR_EN0_DMA_CMD_COMP_SET_MSK) )
{
//printf( "Error: DMA command is incomplete: 0x%lx\n", res );
ret = res;
}
alt_nand_dma_set_enabled( false );
return ret;
}
int32_t alt_nand_dma_page_read( const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, uint32_t mem_addr )
{
int32_t ret = 0;
uint32_t res;
const uint32_t interrup_status_register = alt_nand_get_interrupt_status_register_addr( bank );
alt_write_word( interrup_status_register, ALT_HHP_UINT32_MASK );
alt_nand_dma_set_enabled( true );
alt_nand_dma_write_cmd_structure( bank, block_addr, page_addr, 1, mem_addr, true, 64 );
res = alt_nand_poll_for_interrupt_status_register( interrup_status_register,
ALT_NAND_INT_STATUS_DMA_CMD_COMP |
ALT_NAND_INT_STATUS_ECC_UNCOR_ERR);
// 10.8. Order of interrupt status bits assertion 9.
if ( !(res & ALT_NAND_INT_STATUS_DMA_CMD_COMP) )
{
//printf( "Error: DMA command is incomplete: 0x%lx\n", res );
ret = res;
}
alt_nand_dma_set_enabled( false );
return ret;
}
void alt_nand_dma_set_enabled( int32_t is_enabled )
{
ALT_NAND_DMA_raw_t * dma = (ALT_NAND_DMA_raw_t *)(nand->dma);
alt_write_word(&dma->dma_enable, ( is_enabled ? ALT_NAND_DMA_DMA_EN_FLAG_SET_MSK : 0 ) );
alt_read_word( &dma->dma_enable);
}
void alt_nand_dma_write_cmd_structure( const uint32_t bank, const uint32_t block_addr, const uint32_t page_addr, const uint32_t page_count, uint64_t mem_addr, int32_t is_read_op, const uint32_t burst_len )
{
const uint32_t MODE_BANK_MASK = 0xFF000000;
uint32_t addr = alt_nand_compose_map10_cmd_addr( bank, block_addr, page_addr );
// Transaction 1
// Table 7.2. Address Encoding
// Table 7.3. Data
alt_nand_write_indirect( addr, 0x2000 | (is_read_op ? 0x0 : 0x100) | page_count);
// Transaction 2
// Table 7.4. Address Encoding
// Table 7.5. Data
addr &= MODE_BANK_MASK;
addr |= ((uint16_t)(mem_addr >> 16) << 8);
alt_nand_write_indirect( addr, 0x2200 );
// Transaction 3
// Table 7.6. Address Encoding
// Table 7.7. Data
addr &= MODE_BANK_MASK;
addr |= ((uint16_t)mem_addr << 8);
alt_nand_write_indirect( addr, 0x2300 );
// Transaction 4
// Table 7.8. Address Encoding
// Table 7.9. Data
addr &= MODE_BANK_MASK;
addr |= 0x10000 | burst_len << 8; // Enable INTR_STATUS__DMA_CMD_COMP always.
alt_nand_write_indirect( addr, 0x2400);
}
ALT_STATUS_CODE alt_nand_flash_init_manual(void *user_arg)
{
//printf("Entered Custom Init Routine for NAND.\n");
return ALT_E_RESERVED;
}
void alt_nand_erase_block_callback(ALT_STATUS_CODE status, void *callback_arg)
{
//printf("NAND Block Erase Callback is successful.\n");
return;
}
void alt_nand_dma_page_callback(ALT_STATUS_CODE status, void *callback_arg)
{
//printf("NAND DMA read Callback is successful.\n");
return;
}
uint32_t nand_read_register(const uint32_t offset)
{
return alt_read_word(&nand->cfg + offset);
}