Добавить переносимый драйвер parallel NAND
This commit is contained in:
191
src/parallel_nand.c
Normal file
191
src/parallel_nand.c
Normal 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
151
src/parallel_nand_gas.c
Normal 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;
|
||||
}
|
||||
43
src/parallel_nand_pcan_gas.c
Normal file
43
src/parallel_nand_pcan_gas.c
Normal 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";
|
||||
}
|
||||
Reference in New Issue
Block a user