Files
templates/c/parallel-nand/src/parallel_nand.c

192 lines
6.6 KiB
C

#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;
}