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