Добавить переносимый драйвер parallel NAND

This commit is contained in:
2026-09-04 12:22:21 +03:00
commit 1f1006bf2a
21 changed files with 1429 additions and 0 deletions

191
src/parallel_nand.c Normal file
View File

@@ -0,0 +1,191 @@
#include "parallel_nand.h"
#define PNAND_CMD_READ_1 0x00U
#define PNAND_CMD_READ_2 0x30U
#define PNAND_CMD_READ_ID 0x90U
#define PNAND_CMD_READ_STATUS 0x70U
#define PNAND_CMD_RESET 0xFFU
const parallel_nand_geometry_t parallel_nand_mt29f1g08_geometry = {
PNAND_MT29F1G08_PAGE_MAIN,
PNAND_MT29F1G08_PAGE_OOB,
PNAND_MT29F1G08_PAGES_PER_BLOCK,
PNAND_MT29F1G08_BLOCKS,
PNAND_MT29F1G08_COLUMN_CYCLES,
PNAND_MT29F1G08_ROW_CYCLES,
1000000UL
};
static int port_valid(const parallel_nand_port_t *port)
{
return port != 0 && port->init != 0 && port->select != 0 &&
port->deselect != 0 && port->write_command != 0 &&
port->write_address != 0 && port->read_data != 0 &&
port->is_ready != 0 && port->set_write_protect != 0 &&
port->delay_us != 0;
}
static int geometry_valid(const parallel_nand_geometry_t *g)
{
return g != 0 && g->page_main_size != 0UL &&
g->pages_per_block != 0UL && g->block_count != 0UL &&
g->column_cycles != 0U && g->row_cycles != 0U &&
g->ready_timeout_us != 0UL;
}
unsigned long parallel_nand_page_count(const parallel_nand_t *nand)
{
if (nand == 0) return 0UL;
return nand->geometry.pages_per_block * nand->geometry.block_count;
}
static int wait_ready(parallel_nand_t *nand)
{
unsigned long elapsed = 0UL;
while (!nand->port->is_ready()) {
if (elapsed >= nand->geometry.ready_timeout_us)
return PNAND_ERROR_TIMEOUT;
nand->port->delay_us(1UL);
elapsed++;
}
return PNAND_OK;
}
int parallel_nand_reset(parallel_nand_t *nand)
{
if (nand == 0 || !port_valid(nand->port)) return PNAND_ERROR_ARGUMENT;
nand->port->select();
nand->port->write_command(PNAND_CMD_RESET);
nand->port->deselect();
return wait_ready(nand);
}
int parallel_nand_init(parallel_nand_t *nand,
const parallel_nand_port_t *port,
const parallel_nand_geometry_t *geometry)
{
if (nand == 0 || !port_valid(port) || !geometry_valid(geometry))
return PNAND_ERROR_ARGUMENT;
nand->port = port;
nand->geometry = *geometry;
if (port->init() != 0) return PNAND_ERROR_PORT;
port->deselect();
/* Ядро пока только читает: аппаратно запрещаем PROGRAM/ERASE. */
port->set_write_protect(1);
return parallel_nand_reset(nand);
}
int parallel_nand_read_status(parallel_nand_t *nand, pnand_octet_t *status)
{
if (nand == 0 || status == 0) return PNAND_ERROR_ARGUMENT;
nand->port->select();
nand->port->write_command(PNAND_CMD_READ_STATUS);
*status = nand->port->read_data() & 0xFFU;
nand->port->deselect();
return PNAND_OK;
}
int parallel_nand_read_id(parallel_nand_t *nand, pnand_octet_t address,
pnand_octet_t *id, unsigned int count)
{
unsigned int i;
if (nand == 0 || id == 0 || count == 0U) return PNAND_ERROR_ARGUMENT;
nand->port->select();
nand->port->write_command(PNAND_CMD_READ_ID);
nand->port->write_address(address & 0xFFU);
for (i = 0U; i < count; i++) id[i] = nand->port->read_data() & 0xFFU;
nand->port->deselect();
return PNAND_OK;
}
static int begin_page_read(parallel_nand_t *nand, unsigned long page,
unsigned long column)
{
unsigned int i;
unsigned long value;
unsigned long total = nand->geometry.page_main_size +
nand->geometry.page_oob_size;
if (page >= parallel_nand_page_count(nand) || column >= total)
return PNAND_ERROR_RANGE;
nand->port->select();
nand->port->write_command(PNAND_CMD_READ_1);
value = column;
for (i = 0U; i < nand->geometry.column_cycles; i++) {
nand->port->write_address((pnand_octet_t)(value & 0xFFUL));
value >>= 8;
}
value = page;
for (i = 0U; i < nand->geometry.row_cycles; i++) {
nand->port->write_address((pnand_octet_t)(value & 0xFFUL));
value >>= 8;
}
nand->port->write_command(PNAND_CMD_READ_2);
nand->port->deselect();
return wait_ready(nand);
}
int parallel_nand_read_page_slice(parallel_nand_t *nand, unsigned long page,
unsigned long column, pnand_octet_t *out,
unsigned long count)
{
unsigned long i;
unsigned long total;
int result;
if (nand == 0 || out == 0 || count == 0UL) return PNAND_ERROR_ARGUMENT;
total = nand->geometry.page_main_size + nand->geometry.page_oob_size;
if (column >= total || count > total - column) return PNAND_ERROR_RANGE;
result = begin_page_read(nand, page, column);
if (result != PNAND_OK) return result;
nand->port->select();
for (i = 0UL; i < count; i++) out[i] = nand->port->read_data() & 0xFFU;
nand->port->deselect();
return PNAND_OK;
}
int parallel_nand_read_page(parallel_nand_t *nand, unsigned long page,
pnand_octet_t *main_data,
unsigned long main_capacity,
pnand_octet_t *oob,
unsigned long oob_capacity)
{
unsigned long i;
int result;
if (nand == 0 || main_data == 0 || oob == 0)
return PNAND_ERROR_ARGUMENT;
if (main_capacity < nand->geometry.page_main_size ||
oob_capacity < nand->geometry.page_oob_size)
return PNAND_ERROR_BUFFER;
result = begin_page_read(nand, page, 0UL);
if (result != PNAND_OK) return result;
nand->port->select();
for (i = 0UL; i < nand->geometry.page_main_size; i++)
main_data[i] = nand->port->read_data() & 0xFFU;
for (i = 0UL; i < nand->geometry.page_oob_size; i++)
oob[i] = nand->port->read_data() & 0xFFU;
nand->port->deselect();
return PNAND_OK;
}
int parallel_nand_is_bad_block(parallel_nand_t *nand, unsigned long block,
int *is_bad)
{
unsigned long first_page;
pnand_octet_t marker;
int result;
if (nand == 0 || is_bad == 0) return PNAND_ERROR_ARGUMENT;
if (block >= nand->geometry.block_count) return PNAND_ERROR_RANGE;
first_page = block * nand->geometry.pages_per_block;
result = parallel_nand_read_page_slice(nand, first_page,
nand->geometry.page_main_size, &marker, 1UL);
if (result != PNAND_OK) return result;
if ((marker & 0xFFU) != 0xFFU) {
*is_bad = 1;
return PNAND_OK;
}
result = parallel_nand_read_page_slice(nand, first_page + 1UL,
nand->geometry.page_main_size, &marker, 1UL);
if (result != PNAND_OK) return result;
*is_bad = ((marker & 0xFFU) != 0xFFU);
return PNAND_OK;
}

151
src/parallel_nand_gas.c Normal file
View File

@@ -0,0 +1,151 @@
#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;
}

View File

@@ -0,0 +1,43 @@
#include "parallel_nand_pcan_gas.h"
static pcan_gas_status_t convert(parallel_nand_gas_result_t result)
{
switch (result) {
case PNAND_GAS_OK: return PCAN_GAS_OK;
case PNAND_GAS_NO_REG: return PCAN_GAS_NO_REG;
case PNAND_GAS_READ_ONLY: return PCAN_GAS_READ_ONLY;
default: return PCAN_GAS_REJECTED;
}
}
static pcan_gas_status_t read_reg(const pcan_gas_region_t *region,
uint16_t offset, uint16_t *value)
{
parallel_nand_gas_t *gas = (parallel_nand_gas_t *)region->user;
unsigned int portable_value;
parallel_nand_gas_result_t result = parallel_nand_gas_read(
gas, (unsigned int)region->base + offset, &portable_value);
if (result == PNAND_GAS_OK) *value = (uint16_t)portable_value;
return convert(result);
}
static pcan_gas_status_t write_reg(const pcan_gas_region_t *region,
uint16_t offset, uint16_t value)
{
parallel_nand_gas_t *gas = (parallel_nand_gas_t *)region->user;
return convert(parallel_nand_gas_write(
gas, (unsigned int)region->base + offset, value));
}
void parallel_nand_pcan_gas_region_init(pcan_gas_region_t *region,
parallel_nand_gas_t *gas)
{
region->base = (uint16_t)gas->base;
region->count = PNAND_GAS_REGION_REGS;
region->storage = 0;
region->read = read_reg;
region->write = write_reg;
region->flags = 0U;
region->user = gas;
region->name = "parallel-nand";
}