152 lines
6.2 KiB
C
152 lines
6.2 KiB
C
#include "parallel_nand_gas.h"
|
|
|
|
static unsigned int pack_pair(const pnand_octet_t *data, unsigned int index)
|
|
{
|
|
return (unsigned int)((data[index * 2U] & 0xFFU) |
|
|
((data[index * 2U + 1U] & 0xFFU) << 8));
|
|
}
|
|
|
|
parallel_nand_gas_result_t parallel_nand_gas_read(
|
|
parallel_nand_gas_t *gas, unsigned int address, unsigned int *value)
|
|
{
|
|
unsigned int offset;
|
|
const parallel_nand_geometry_t *g;
|
|
if (gas == 0 || value == 0 || address < gas->base)
|
|
return PNAND_GAS_REJECTED;
|
|
g = &gas->nand->geometry;
|
|
offset = address - gas->base;
|
|
if (offset >= PNAND_GAS_REGION_REGS) return PNAND_GAS_NO_REG;
|
|
if (offset >= PNAND_GAS_DATA_OFFSET &&
|
|
offset < PNAND_GAS_DATA_OFFSET + PNAND_GAS_DATA_REGS) {
|
|
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U)
|
|
return PNAND_GAS_REJECTED;
|
|
*value = pack_pair(gas->main_data,
|
|
offset - PNAND_GAS_DATA_OFFSET);
|
|
return PNAND_GAS_OK;
|
|
}
|
|
if (offset >= PNAND_GAS_OOB_OFFSET &&
|
|
offset < PNAND_GAS_OOB_OFFSET + PNAND_GAS_OOB_REGS) {
|
|
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U)
|
|
return PNAND_GAS_REJECTED;
|
|
*value = pack_pair(gas->oob,
|
|
offset - PNAND_GAS_OOB_OFFSET);
|
|
return PNAND_GAS_OK;
|
|
}
|
|
switch (offset) {
|
|
case PNAND_GAS_REG_SIGNATURE: *value = 0x4E44U; break; /* ND */
|
|
case PNAND_GAS_REG_VERSION: *value = 0x0001U; break;
|
|
case PNAND_GAS_REG_STATUS: *value = gas->status; break;
|
|
case PNAND_GAS_REG_ERROR: *value = (unsigned int)gas->error & 0xFFFFU; break;
|
|
case PNAND_GAS_REG_COMMAND: *value = PNAND_GAS_CMD_NONE; break;
|
|
case PNAND_GAS_REG_PAGE_LO: *value = (unsigned int)gas->selected_page & 0xFFFFU; break;
|
|
case PNAND_GAS_REG_PAGE_HI: *value = (unsigned int)(gas->selected_page >> 16) & 0xFFFFU; break;
|
|
case PNAND_GAS_REG_PAGE_MAIN: *value = (unsigned int)g->page_main_size; break;
|
|
case PNAND_GAS_REG_PAGE_OOB: *value = (unsigned int)g->page_oob_size; break;
|
|
case PNAND_GAS_REG_PAGES_PER_BLOCK: *value = (unsigned int)g->pages_per_block; break;
|
|
case PNAND_GAS_REG_BLOCKS: *value = (unsigned int)g->block_count; break;
|
|
case PNAND_GAS_REG_MANUFACTURER_ID: *value = (unsigned int)gas->id[0]; break;
|
|
case PNAND_GAS_REG_DEVICE_ID: *value = (unsigned int)gas->id[1]; break;
|
|
case PNAND_GAS_REG_BAD_BLOCK:
|
|
*value = (gas->status & PNAND_GAS_STATUS_BAD_BLOCK) != 0U; break;
|
|
case PNAND_GAS_REG_GENERATION_LO: *value = (unsigned int)gas->generation & 0xFFFFU; break;
|
|
case PNAND_GAS_REG_GENERATION_HI: *value = (unsigned int)(gas->generation >> 16) & 0xFFFFU; break;
|
|
default: return PNAND_GAS_NO_REG;
|
|
}
|
|
*value &= 0xFFFFU;
|
|
return PNAND_GAS_OK;
|
|
}
|
|
|
|
static void finish(parallel_nand_gas_t *gas, int result)
|
|
{
|
|
gas->error = result;
|
|
gas->status &= (0xFFFFU ^ PNAND_GAS_STATUS_BUSY);
|
|
gas->status |= PNAND_GAS_STATUS_READY;
|
|
if (result != PNAND_OK) gas->status |= PNAND_GAS_STATUS_ERROR;
|
|
}
|
|
|
|
static parallel_nand_gas_result_t run_command(parallel_nand_gas_t *gas,
|
|
unsigned int command)
|
|
{
|
|
int result = PNAND_OK;
|
|
int bad = 0;
|
|
gas->status = PNAND_GAS_STATUS_BUSY;
|
|
if (command == PNAND_GAS_CMD_RESET) {
|
|
result = parallel_nand_reset(gas->nand);
|
|
} else if (command == PNAND_GAS_CMD_READ_ID) {
|
|
result = parallel_nand_read_id(gas->nand, 0U, gas->id, 8U);
|
|
} else if (command == PNAND_GAS_CMD_READ_PAGE) {
|
|
unsigned long block;
|
|
if (gas->selected_page >= parallel_nand_page_count(gas->nand)) {
|
|
finish(gas, PNAND_ERROR_RANGE);
|
|
return PNAND_GAS_REJECTED;
|
|
}
|
|
result = parallel_nand_read_page(gas->nand, gas->selected_page,
|
|
gas->main_data, PNAND_MT29F1G08_PAGE_MAIN,
|
|
gas->oob, PNAND_MT29F1G08_PAGE_OOB);
|
|
if (result == PNAND_OK) {
|
|
gas->status |= PNAND_GAS_STATUS_PAGE_VALID;
|
|
gas->generation++;
|
|
block = gas->selected_page /
|
|
gas->nand->geometry.pages_per_block;
|
|
if (!gas->block_cache_valid || gas->cached_block != block) {
|
|
result = parallel_nand_is_bad_block(gas->nand, block, &bad);
|
|
if (result == PNAND_OK) {
|
|
gas->cached_block = block;
|
|
gas->cached_bad_block = bad;
|
|
gas->block_cache_valid = 1;
|
|
}
|
|
} else {
|
|
bad = gas->cached_bad_block;
|
|
}
|
|
if (result == PNAND_OK && bad)
|
|
gas->status |= PNAND_GAS_STATUS_BAD_BLOCK;
|
|
}
|
|
} else {
|
|
gas->status = PNAND_GAS_STATUS_READY;
|
|
return PNAND_GAS_REJECTED;
|
|
}
|
|
finish(gas, result);
|
|
return result == PNAND_OK ? PNAND_GAS_OK : PNAND_GAS_REJECTED;
|
|
}
|
|
|
|
parallel_nand_gas_result_t parallel_nand_gas_write(
|
|
parallel_nand_gas_t *gas, unsigned int address, unsigned int value)
|
|
{
|
|
unsigned int offset;
|
|
if (gas == 0 || address < gas->base) return PNAND_GAS_REJECTED;
|
|
offset = address - gas->base;
|
|
if (offset >= PNAND_GAS_REGION_REGS) return PNAND_GAS_NO_REG;
|
|
if ((gas->status & PNAND_GAS_STATUS_BUSY) != 0U)
|
|
return PNAND_GAS_REJECTED;
|
|
value &= 0xFFFFU;
|
|
switch (offset) {
|
|
case PNAND_GAS_REG_COMMAND: return run_command(gas, value);
|
|
case PNAND_GAS_REG_PAGE_LO:
|
|
gas->selected_page = (gas->selected_page & 0xFFFF0000UL) | value;
|
|
gas->status &= (0xFFFFU ^ PNAND_GAS_STATUS_PAGE_VALID);
|
|
return PNAND_GAS_OK;
|
|
case PNAND_GAS_REG_PAGE_HI:
|
|
gas->selected_page = (gas->selected_page & 0x0000FFFFUL) |
|
|
((unsigned long)value << 16);
|
|
gas->status &= (0xFFFFU ^ PNAND_GAS_STATUS_PAGE_VALID);
|
|
return PNAND_GAS_OK;
|
|
default: return PNAND_GAS_READ_ONLY;
|
|
}
|
|
}
|
|
|
|
void parallel_nand_gas_init(parallel_nand_gas_t *gas,
|
|
parallel_nand_t *nand, unsigned int base)
|
|
{
|
|
unsigned int i;
|
|
gas->nand = nand;
|
|
gas->base = base;
|
|
gas->status = PNAND_GAS_STATUS_READY;
|
|
gas->error = PNAND_OK;
|
|
gas->selected_page = 0UL;
|
|
gas->generation = 0UL;
|
|
gas->cached_block = 0UL;
|
|
gas->cached_bad_block = 0;
|
|
gas->block_cache_valid = 0;
|
|
for (i = 0U; i < 8U; i++) gas->id[i] = 0U;
|
|
}
|