feat: sync current parallel NAND implementation from templates
This commit is contained in:
@@ -4,8 +4,15 @@
|
||||
#define PNAND_CMD_READ_2 0x30U
|
||||
#define PNAND_CMD_READ_ID 0x90U
|
||||
#define PNAND_CMD_READ_STATUS 0x70U
|
||||
#define PNAND_CMD_PROGRAM_1 0x80U
|
||||
#define PNAND_CMD_PROGRAM_2 0x10U
|
||||
#define PNAND_CMD_ERASE_1 0x60U
|
||||
#define PNAND_CMD_ERASE_2 0xD0U
|
||||
#define PNAND_CMD_RESET 0xFFU
|
||||
|
||||
#define PNAND_STATUS_FAIL 0x01U
|
||||
#define PNAND_STATUS_WP 0x80U
|
||||
|
||||
const parallel_nand_geometry_t parallel_nand_mt29f1g08_geometry = {
|
||||
PNAND_MT29F1G08_PAGE_MAIN,
|
||||
PNAND_MT29F1G08_PAGE_OOB,
|
||||
@@ -67,6 +74,8 @@ int parallel_nand_reset(parallel_nand_t *nand)
|
||||
nand->port->select();
|
||||
nand->port->write_command(PNAND_CMD_RESET);
|
||||
nand->port->deselect();
|
||||
/* R/B# may not fall until tWB after the command cycle. */
|
||||
nand->port->delay_us(1UL);
|
||||
return wait_ready(nand);
|
||||
}
|
||||
|
||||
@@ -80,7 +89,7 @@ int parallel_nand_init(parallel_nand_t *nand,
|
||||
nand->geometry = *geometry;
|
||||
if (port->init() != 0) return PNAND_ERROR_PORT;
|
||||
port->deselect();
|
||||
/* Ядро пока только читает: аппаратно запрещаем PROGRAM/ERASE. */
|
||||
/* WP# stays asserted except for the shortest possible mutation window. */
|
||||
port->set_write_protect(1);
|
||||
return parallel_nand_reset(nand);
|
||||
}
|
||||
@@ -132,6 +141,7 @@ static int begin_page_read(parallel_nand_t *nand, unsigned long page,
|
||||
}
|
||||
nand->port->write_command(PNAND_CMD_READ_2);
|
||||
nand->port->deselect();
|
||||
nand->port->delay_us(1UL); /* Cover tWB before polling R/B#. */
|
||||
return wait_ready(nand);
|
||||
}
|
||||
|
||||
@@ -177,6 +187,101 @@ int parallel_nand_read_page(parallel_nand_t *nand, unsigned long page,
|
||||
return PNAND_OK;
|
||||
}
|
||||
|
||||
static void write_address_cycles(parallel_nand_t *nand,
|
||||
unsigned long value,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0U; i < count; i++) {
|
||||
nand->port->write_address((pnand_octet_t)(value & 0xFFUL));
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int mutation_supported(const parallel_nand_t *nand)
|
||||
{
|
||||
return nand != 0 && port_valid(nand->port) &&
|
||||
nand->port->write_data != 0 &&
|
||||
nand->port->write_supported != 0;
|
||||
}
|
||||
|
||||
static int finish_mutation(parallel_nand_t *nand, int failure_result)
|
||||
{
|
||||
pnand_octet_t status = 0U;
|
||||
int result = wait_ready(nand);
|
||||
if (result == PNAND_OK)
|
||||
result = parallel_nand_read_status(nand, &status);
|
||||
nand->port->set_write_protect(1);
|
||||
if (result != PNAND_OK) return result;
|
||||
if ((status & PNAND_STATUS_WP) == 0U)
|
||||
return PNAND_ERROR_WRITE_PROTECTED;
|
||||
return (status & PNAND_STATUS_FAIL) != 0U ? failure_result : PNAND_OK;
|
||||
}
|
||||
|
||||
int parallel_nand_program_page(parallel_nand_t *nand, unsigned long page,
|
||||
const pnand_octet_t *main_data,
|
||||
unsigned long main_count,
|
||||
const pnand_octet_t *oob,
|
||||
unsigned long oob_count)
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned long block;
|
||||
int bad = 0;
|
||||
int result;
|
||||
if (nand == 0 || main_data == 0) return PNAND_ERROR_ARGUMENT;
|
||||
if (!mutation_supported(nand)) return PNAND_ERROR_WRITE_PROTECTED;
|
||||
if (page >= parallel_nand_page_count(nand)) return PNAND_ERROR_RANGE;
|
||||
if (main_count != nand->geometry.page_main_size)
|
||||
return PNAND_ERROR_BUFFER;
|
||||
if ((oob == 0 && oob_count != 0UL) ||
|
||||
(oob != 0 && oob_count != nand->geometry.page_oob_size))
|
||||
return PNAND_ERROR_BUFFER;
|
||||
|
||||
block = page / nand->geometry.pages_per_block;
|
||||
result = parallel_nand_is_bad_block(nand, block, &bad);
|
||||
if (result != PNAND_OK) return result;
|
||||
if (bad) return PNAND_ERROR_BAD_BLOCK;
|
||||
|
||||
nand->port->set_write_protect(0);
|
||||
nand->port->delay_us(1UL);
|
||||
nand->port->select();
|
||||
nand->port->write_command(PNAND_CMD_PROGRAM_1);
|
||||
write_address_cycles(nand, 0UL, nand->geometry.column_cycles);
|
||||
write_address_cycles(nand, page, nand->geometry.row_cycles);
|
||||
for (i = 0UL; i < main_count; i++)
|
||||
nand->port->write_data(main_data[i] & 0xFFU);
|
||||
for (i = 0UL; i < oob_count; i++)
|
||||
nand->port->write_data(oob[i] & 0xFFU);
|
||||
nand->port->write_command(PNAND_CMD_PROGRAM_2);
|
||||
nand->port->deselect();
|
||||
nand->port->delay_us(1UL); /* Cover tWB before polling R/B#. */
|
||||
return finish_mutation(nand, PNAND_ERROR_PROGRAM_FAILED);
|
||||
}
|
||||
|
||||
int parallel_nand_erase_block(parallel_nand_t *nand, unsigned long block)
|
||||
{
|
||||
unsigned long first_page;
|
||||
int bad = 0;
|
||||
int result;
|
||||
if (nand == 0) return PNAND_ERROR_ARGUMENT;
|
||||
if (!mutation_supported(nand)) return PNAND_ERROR_WRITE_PROTECTED;
|
||||
if (block >= nand->geometry.block_count) return PNAND_ERROR_RANGE;
|
||||
result = parallel_nand_is_bad_block(nand, block, &bad);
|
||||
if (result != PNAND_OK) return result;
|
||||
if (bad) return PNAND_ERROR_BAD_BLOCK;
|
||||
|
||||
first_page = block * nand->geometry.pages_per_block;
|
||||
nand->port->set_write_protect(0);
|
||||
nand->port->delay_us(1UL);
|
||||
nand->port->select();
|
||||
nand->port->write_command(PNAND_CMD_ERASE_1);
|
||||
write_address_cycles(nand, first_page, nand->geometry.row_cycles);
|
||||
nand->port->write_command(PNAND_CMD_ERASE_2);
|
||||
nand->port->deselect();
|
||||
nand->port->delay_us(1UL); /* Cover tWB before polling R/B#. */
|
||||
return finish_mutation(nand, PNAND_ERROR_ERASE_FAILED);
|
||||
}
|
||||
|
||||
int parallel_nand_is_bad_block(parallel_nand_t *nand, unsigned long block,
|
||||
int *is_bad)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,20 @@ static unsigned int pack_pair(const pnand_octet_t *data, unsigned int index)
|
||||
((data[index * 2U + 1U] & 0xFFU) << 8));
|
||||
}
|
||||
|
||||
static void unpack_pair(pnand_octet_t *data, unsigned int index,
|
||||
unsigned int value)
|
||||
{
|
||||
data[index * 2U] = (pnand_octet_t)(value & 0xFFU);
|
||||
data[index * 2U + 1U] = (pnand_octet_t)((value >> 8) & 0xFFU);
|
||||
}
|
||||
|
||||
static unsigned int persistent_status(const parallel_nand_gas_t *gas)
|
||||
{
|
||||
return gas->nand->port->write_supported != 0
|
||||
? PNAND_GAS_STATUS_WRITE_SUPPORTED
|
||||
: 0U;
|
||||
}
|
||||
|
||||
parallel_nand_gas_result_t parallel_nand_gas_read(
|
||||
parallel_nand_gas_t *gas, unsigned int address, unsigned int *value)
|
||||
{
|
||||
@@ -13,11 +27,16 @@ parallel_nand_gas_result_t parallel_nand_gas_read(
|
||||
const parallel_nand_geometry_t *g;
|
||||
if (gas == 0 || value == 0 || address < gas->base)
|
||||
return PNAND_GAS_REJECTED;
|
||||
/* Confirmation authorizes exactly the immediately following write. */
|
||||
gas->destructive_armed = 0;
|
||||
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 ((unsigned long)(offset - PNAND_GAS_DATA_OFFSET) * 2UL >=
|
||||
g->page_main_size)
|
||||
return PNAND_GAS_NO_REG;
|
||||
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U)
|
||||
return PNAND_GAS_REJECTED;
|
||||
*value = pack_pair(gas->main_data,
|
||||
@@ -26,6 +45,9 @@ parallel_nand_gas_result_t parallel_nand_gas_read(
|
||||
}
|
||||
if (offset >= PNAND_GAS_OOB_OFFSET &&
|
||||
offset < PNAND_GAS_OOB_OFFSET + PNAND_GAS_OOB_REGS) {
|
||||
if ((unsigned long)(offset - PNAND_GAS_OOB_OFFSET) * 2UL >=
|
||||
g->page_oob_size)
|
||||
return PNAND_GAS_NO_REG;
|
||||
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U)
|
||||
return PNAND_GAS_REJECTED;
|
||||
*value = pack_pair(gas->oob,
|
||||
@@ -34,7 +56,7 @@ parallel_nand_gas_result_t parallel_nand_gas_read(
|
||||
}
|
||||
switch (offset) {
|
||||
case PNAND_GAS_REG_SIGNATURE: *value = 0x4E44U; break; /* ND */
|
||||
case PNAND_GAS_REG_VERSION: *value = 0x0001U; break;
|
||||
case PNAND_GAS_REG_VERSION: *value = 0x0002U; 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;
|
||||
@@ -50,6 +72,7 @@ parallel_nand_gas_result_t parallel_nand_gas_read(
|
||||
*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;
|
||||
case PNAND_GAS_REG_CONFIRM: *value = 0U; break;
|
||||
default: return PNAND_GAS_NO_REG;
|
||||
}
|
||||
*value &= 0xFFFFU;
|
||||
@@ -60,7 +83,7 @@ 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;
|
||||
gas->status |= PNAND_GAS_STATUS_READY | persistent_status(gas);
|
||||
if (result != PNAND_OK) gas->status |= PNAND_GAS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
@@ -69,7 +92,11 @@ static parallel_nand_gas_result_t run_command(parallel_nand_gas_t *gas,
|
||||
{
|
||||
int result = PNAND_OK;
|
||||
int bad = 0;
|
||||
gas->status = PNAND_GAS_STATUS_BUSY;
|
||||
int armed = gas->destructive_armed;
|
||||
unsigned int i;
|
||||
unsigned int previous_status = gas->status;
|
||||
gas->destructive_armed = 0;
|
||||
gas->status = PNAND_GAS_STATUS_BUSY | persistent_status(gas);
|
||||
if (command == PNAND_GAS_CMD_RESET) {
|
||||
result = parallel_nand_reset(gas->nand);
|
||||
} else if (command == PNAND_GAS_CMD_READ_ID) {
|
||||
@@ -101,8 +128,59 @@ static parallel_nand_gas_result_t run_command(parallel_nand_gas_t *gas,
|
||||
if (result == PNAND_OK && bad)
|
||||
gas->status |= PNAND_GAS_STATUS_BAD_BLOCK;
|
||||
}
|
||||
} else if (command == PNAND_GAS_CMD_CLEAR_BUFFER) {
|
||||
for (i = 0U; i < PNAND_MT29F1G08_PAGE_MAIN; i++)
|
||||
gas->main_data[i] = 0xFFU;
|
||||
for (i = 0U; i < PNAND_MT29F1G08_PAGE_OOB; i++)
|
||||
gas->oob[i] = 0xFFU;
|
||||
gas->status |= PNAND_GAS_STATUS_PAGE_VALID |
|
||||
PNAND_GAS_STATUS_BUFFER_DIRTY;
|
||||
gas->generation++;
|
||||
} else if (command == PNAND_GAS_CMD_PROGRAM_PAGE) {
|
||||
if (!armed ||
|
||||
(previous_status & PNAND_GAS_STATUS_PAGE_VALID) == 0U) {
|
||||
gas->status |= previous_status &
|
||||
(PNAND_GAS_STATUS_PAGE_VALID |
|
||||
PNAND_GAS_STATUS_BUFFER_DIRTY |
|
||||
PNAND_GAS_STATUS_BAD_BLOCK);
|
||||
finish(gas, PNAND_ERROR_CONFIRMATION);
|
||||
return PNAND_GAS_REJECTED;
|
||||
}
|
||||
result = parallel_nand_program_page(
|
||||
gas->nand, gas->selected_page,
|
||||
gas->main_data, gas->nand->geometry.page_main_size,
|
||||
gas->oob, gas->nand->geometry.page_oob_size);
|
||||
gas->status |= PNAND_GAS_STATUS_PAGE_VALID;
|
||||
if (result == PNAND_OK) {
|
||||
gas->generation++;
|
||||
gas->block_cache_valid = 0;
|
||||
} else {
|
||||
gas->status |= PNAND_GAS_STATUS_BUFFER_DIRTY;
|
||||
}
|
||||
} else if (command == PNAND_GAS_CMD_ERASE_BLOCK) {
|
||||
unsigned long block;
|
||||
if (!armed ||
|
||||
gas->selected_page >= parallel_nand_page_count(gas->nand)) {
|
||||
finish(gas, !armed ? PNAND_ERROR_CONFIRMATION :
|
||||
PNAND_ERROR_RANGE);
|
||||
return PNAND_GAS_REJECTED;
|
||||
}
|
||||
block = gas->selected_page / gas->nand->geometry.pages_per_block;
|
||||
result = parallel_nand_erase_block(gas->nand, block);
|
||||
if (result == PNAND_OK) {
|
||||
gas->status &= (0xFFFFU ^ (PNAND_GAS_STATUS_PAGE_VALID |
|
||||
PNAND_GAS_STATUS_BUFFER_DIRTY |
|
||||
PNAND_GAS_STATUS_BAD_BLOCK));
|
||||
gas->generation++;
|
||||
gas->block_cache_valid = 0;
|
||||
} else {
|
||||
gas->status |= previous_status &
|
||||
(PNAND_GAS_STATUS_PAGE_VALID |
|
||||
PNAND_GAS_STATUS_BUFFER_DIRTY |
|
||||
PNAND_GAS_STATUS_BAD_BLOCK);
|
||||
}
|
||||
} else {
|
||||
gas->status = PNAND_GAS_STATUS_READY;
|
||||
gas->status = PNAND_GAS_STATUS_READY | persistent_status(gas);
|
||||
return PNAND_GAS_REJECTED;
|
||||
}
|
||||
finish(gas, result);
|
||||
@@ -119,18 +197,47 @@ parallel_nand_gas_result_t parallel_nand_gas_write(
|
||||
if ((gas->status & PNAND_GAS_STATUS_BUSY) != 0U)
|
||||
return PNAND_GAS_REJECTED;
|
||||
value &= 0xFFFFU;
|
||||
if (offset >= PNAND_GAS_DATA_OFFSET &&
|
||||
offset < PNAND_GAS_DATA_OFFSET + PNAND_GAS_DATA_REGS) {
|
||||
unsigned int index = offset - PNAND_GAS_DATA_OFFSET;
|
||||
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U ||
|
||||
(unsigned long)index * 2UL >= gas->nand->geometry.page_main_size)
|
||||
return PNAND_GAS_REJECTED;
|
||||
gas->destructive_armed = 0;
|
||||
unpack_pair(gas->main_data, index, value);
|
||||
gas->status |= PNAND_GAS_STATUS_BUFFER_DIRTY;
|
||||
return PNAND_GAS_OK;
|
||||
}
|
||||
if (offset >= PNAND_GAS_OOB_OFFSET &&
|
||||
offset < PNAND_GAS_OOB_OFFSET + PNAND_GAS_OOB_REGS) {
|
||||
unsigned int index = offset - PNAND_GAS_OOB_OFFSET;
|
||||
if ((gas->status & PNAND_GAS_STATUS_PAGE_VALID) == 0U ||
|
||||
(unsigned long)index * 2UL >= gas->nand->geometry.page_oob_size)
|
||||
return PNAND_GAS_REJECTED;
|
||||
gas->destructive_armed = 0;
|
||||
unpack_pair(gas->oob, index, value);
|
||||
gas->status |= PNAND_GAS_STATUS_BUFFER_DIRTY;
|
||||
return PNAND_GAS_OK;
|
||||
}
|
||||
switch (offset) {
|
||||
case PNAND_GAS_REG_COMMAND: return run_command(gas, value);
|
||||
case PNAND_GAS_REG_CONFIRM:
|
||||
gas->destructive_armed = value == PNAND_GAS_CONFIRM_MAGIC;
|
||||
return gas->destructive_armed ? PNAND_GAS_OK : PNAND_GAS_REJECTED;
|
||||
case PNAND_GAS_REG_PAGE_LO:
|
||||
gas->destructive_armed = 0;
|
||||
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->destructive_armed = 0;
|
||||
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;
|
||||
default:
|
||||
gas->destructive_armed = 0;
|
||||
return PNAND_GAS_READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,12 +247,17 @@ void parallel_nand_gas_init(parallel_nand_gas_t *gas,
|
||||
unsigned int i;
|
||||
gas->nand = nand;
|
||||
gas->base = base;
|
||||
gas->status = PNAND_GAS_STATUS_READY;
|
||||
gas->status = PNAND_GAS_STATUS_READY | persistent_status(gas);
|
||||
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;
|
||||
gas->destructive_armed = 0;
|
||||
for (i = 0U; i < 8U; i++) gas->id[i] = 0U;
|
||||
for (i = 0U; i < PNAND_MT29F1G08_PAGE_MAIN; i++)
|
||||
gas->main_data[i] = 0xFFU;
|
||||
for (i = 0U; i < PNAND_MT29F1G08_PAGE_OOB; i++)
|
||||
gas->oob[i] = 0xFFU;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user