192 lines
6.6 KiB
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;
|
|
}
|