From 15ad1c42db95b178b6125b8084148ed4463f1472 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Thu, 6 May 2021 00:22:56 +0100 Subject: [PATCH] X1000: simplify NAND driver - Removed unnecessary layers of generic code - Software ECC is gone since we don't need it now (and maybe not ever) - Removed bounce buffering, so callers must now align buffers Change-Id: I33fbac9d9d12a4657980b8618c7d62bfa91e2ea0 --- firmware/SOURCES | 1 - .../ingenic_x1000/fiiom3k/installer-fiiom3k.c | 33 +- .../mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c | 53 -- .../mips/ingenic_x1000/fiiom3k/nand-target.h | 37 - .../mips/ingenic_x1000/nand-x1000-err.h | 9 + .../target/mips/ingenic_x1000/nand-x1000.c | 667 +++++++++--------- .../target/mips/ingenic_x1000/nand-x1000.h | 208 ++---- .../target/mips/ingenic_x1000/spl-x1000.c | 20 +- firmware/target/mips/ingenic_x1000/spl.lds | 11 - 9 files changed, 417 insertions(+), 622 deletions(-) delete mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c delete mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h diff --git a/firmware/SOURCES b/firmware/SOURCES index dd12c4953f..8995e6a5b4 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1698,7 +1698,6 @@ target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c -target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c #ifdef BOOTLOADER_SPL target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c index cdd7276bee..154785ee0b 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c @@ -21,6 +21,7 @@ #include "installer.h" #include "nand-x1000.h" +#include "system.h" #include "core_alloc.h" #include "file.h" @@ -40,22 +41,27 @@ static int install_from_buffer(const void* buf) { + int status = INSTALL_SUCCESS; + int mf_id, dev_id; + if(nand_open()) return ERR_FLASH_OPEN_FAILED; - - int status = INSTALL_SUCCESS; + if(nand_identify(&mf_id, &dev_id)) { + status = ERR_FLASH_OPEN_FAILED; + goto _exit; + } if(nand_enable_writes(true)) { status = ERR_FLASH_DISABLE_WP_FAILED; goto _exit; } - if(nand_erase_bytes(0, BOOT_IMAGE_SIZE)) { + if(nand_erase(0, BOOT_IMAGE_SIZE)) { status = ERR_FLASH_ERASE_FAILED; goto _exit; } - if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) { + if(nand_write(0, BOOT_IMAGE_SIZE, (const uint8_t*)buf)) { status = ERR_FLASH_WRITE_FAILED; goto _exit; } @@ -72,12 +78,17 @@ static int install_from_buffer(const void* buf) static int dump_to_buffer(void* buf) { + int status = INSTALL_SUCCESS; + int mf_id, dev_id; + if(nand_open()) return ERR_FLASH_OPEN_FAILED; + if(nand_identify(&mf_id, &dev_id)) { + status = ERR_FLASH_OPEN_FAILED; + goto _exit; + } - int status = INSTALL_SUCCESS; - - if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) { + if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) { status = ERR_FLASH_READ_FAILED; goto _exit; } @@ -90,12 +101,14 @@ static int dump_to_buffer(void* buf) int install_bootloader(const char* path) { /* Allocate memory to hold image */ - int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); + size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; + int handle = core_alloc("boot_image", bufsize); if(handle < 0) return ERR_OUT_OF_MEMORY; int status = INSTALL_SUCCESS; void* buffer = core_get_data(handle); + CACHEALIGN_BUFFER(buffer, bufsize); /* Open the boot image */ int fd = open(path, O_RDONLY); @@ -132,13 +145,15 @@ int install_bootloader(const char* path) int dump_bootloader(const char* path) { /* Allocate memory to hold image */ - int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); + size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; + int handle = core_alloc("boot_image", bufsize); if(handle < 0) return -1; /* Read data from flash */ int fd = -1; void* buffer = core_get_data(handle); + CACHEALIGN_BUFFER(buffer, bufsize); int status = dump_to_buffer(buffer); if(status) goto _exit; diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c deleted file mode 100644 index 7c8a306bae..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include "nand-x1000.h" -#include "nand-target.h" -#include "sfc-x1000.h" - -/* Unbelievably FiiO has completely disabled the use of ECC for this chip - * in their Linux kernel, even though it has perfectly good spare areas. - * There's no internal ECC either. - * - * Using nanddump to read the spare areas reveals they're filled with 0xff, - * and the publicly released Linux source has the ecc_strength set to 0. - */ -static const nand_chip_data ato25d1ga = { - .name = "ATO25D1GA", - .mf_id = 0x9b, - .dev_id = 0x12, - .dev_conf = NAND_INIT_SFC_DEV_CONF, - /* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz. - * Didn't find any issues doing this so might as well keep the behavior. - */ - .clock_freq = NAND_INIT_CLOCK_SPEED, - .block_size = 64, - .page_size = 2048, - .spare_size = 64, - .rowaddr_width = 3, - .coladdr_width = 2, - .flags = NANDCHIP_FLAG_QUAD, -}; - -const nand_chip_desc target_nand_chip_descs[] = { - {&ato25d1ga, &nand_chip_ops_std}, - {NULL, NULL}, -}; diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h deleted file mode 100644 index 1238d7e452..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#ifndef __NAND_TARGET_H__ -#define __NAND_TARGET_H__ - -/* The max page size (main + spare) of all NAND chips used by this target */ -#define NAND_MAX_PAGE_SIZE (2048 + 64) - -/* The clock speed to use for the SFC controller during chip identification */ -#define NAND_INIT_CLOCK_SPEED 150000000 - -/* Initial value to program SFC_DEV_CONF register with */ -#define NAND_INIT_SFC_DEV_CONF \ - jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \ - CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \ - STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)) - -#endif /* __NAND_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h index 252d591062..869fe73ac9 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h +++ b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h @@ -15,4 +15,13 @@ #define NANDERR_COMMAND_FAILED (-9) #define NANDERR_OTHER (-99) +/* TEMPORARY -- compatibility hack for jztool's sake. + * This will go away once the new bootloader gets merged */ +#define NAND_SUCCESS 0 +#define NAND_ERR_UNKNOWN_CHIP NANDERR_CHIP_UNSUPPORTED +#define NAND_ERR_UNALIGNED NANDERR_UNALIGNED_ADDRESS +#define NAND_ERR_WRITE_PROTECT NANDERR_WRITE_PROTECTED +#define NAND_ERR_CONTROLLER NANDERR_OTHER +#define NAND_ERR_COMMAND NANDERR_COMMAND_FAILED + #endif /* __NAND_X1000_ERR_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index fc8d471559..1770324fb3 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c @@ -20,253 +20,350 @@ ****************************************************************************/ #include "nand-x1000.h" -#include "nand-target.h" #include "sfc-x1000.h" #include "system.h" -#include +#include -#if !defined(NAND_MAX_PAGE_SIZE) || \ - !defined(NAND_INIT_SFC_DEV_CONF) || \ - !defined(NAND_INIT_CLOCK_SPEED) -# error "Target needs to specify NAND driver parameters" -#endif +/* NAND command numbers */ +#define NAND_CMD_READ_ID 0x9f +#define NAND_CMD_WRITE_ENABLE 0x06 +#define NAND_CMD_GET_FEATURE 0x0f +#define NAND_CMD_SET_FEATURE 0x1f +#define NAND_CMD_PAGE_READ_TO_CACHE 0x13 +#define NAND_CMD_READ_FROM_CACHE 0x0b +#define NAND_CMD_READ_FROM_CACHEx4 0x6b +#define NAND_CMD_PROGRAM_LOAD 0x02 +#define NAND_CMD_PROGRAM_LOADx4 0x32 +#define NAND_CMD_PROGRAM_EXECUTE 0x10 +#define NAND_CMD_BLOCK_ERASE 0xd8 -/* Must be at least as big as a cacheline */ -#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE +/* NAND device register addresses for GET_FEATURE / SET_FEATURE */ +#define NAND_FREG_PROTECTION 0xa0 +#define NAND_FREG_FEATURE 0xb0 +#define NAND_FREG_STATUS 0xc0 -/* Writes have been enabled */ -#define NAND_DRV_FLAG_WRITEABLE 0x01 +/* Protection register bits */ +#define NAND_FREG_PROTECTION_BRWD 0x80 +#define NAND_FREG_PROTECTION_BP2 0x20 +#define NAND_FREG_PROTECTION_BP1 0x10 +#define NAND_FREG_PROTECTION_BP0 0x80 +/* Mask of BP bits 0-2 */ +#define NAND_FREG_PROTECTION_ALLBP (0x38) -/* Defined by target */ -extern const nand_chip_desc target_nand_chip_descs[]; +/* Feature register bits */ +#define NAND_FREG_FEATURE_QE 0x01 -#ifdef BOOTLOADER_SPL -# define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR +/* Status register bits */ +#define NAND_FREG_STATUS_OIP 0x01 +#define NAND_FREG_STATUS_WEL 0x02 +#define NAND_FREG_STATUS_E_FAIL 0x04 +#define NAND_FREG_STATUS_P_FAIL 0x08 + +/* NAND chip config */ +const nand_chip_data target_nand_chip_data[] = { +#ifdef FIIO_M3K + { + /* ATO25D1GA */ + .mf_id = 0x9b, + .dev_id = 0x12, + .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), + CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), + STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)), + .clock_freq = 150000000, + .log2_page_size = 11, /* = 2048 bytes */ + .log2_block_size = 6, /* = 64 pages */ + .rowaddr_width = 3, + .coladdr_width = 2, + .flags = NANDCHIP_FLAG_QUAD, + } #else -# define NANDBUFFER_ATTR CACHEALIGN_ATTR + /* Nobody will use this anyway if the device has no NAND flash */ + { 0 }, #endif +}; -/* Globals for the driver */ -static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; -static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR; -static nand_drv nand_driver; +const size_t target_nand_chip_count = + sizeof(target_nand_chip_data) / sizeof(nand_chip_data); -static void nand_drv_reset(nand_drv* d) +/* NAND ops -- high level primitives used by the driver */ +static int nandop_wait_status(int errbit); +static int nandop_read_page(uint32_t row_addr, uint8_t* buf); +static int nandop_write_page(uint32_t row_addr, const uint8_t* buf); +static int nandop_erase_block(uint32_t block_addr); +static int nandop_set_write_protect(bool en); + +/* NAND commands -- 1-to-1 mapping between chip commands and functions */ +static int nandcmd_read_id(int* mf_id, int* dev_id); +static int nandcmd_write_enable(void); +static int nandcmd_get_feature(uint8_t reg); +static int nandcmd_set_feature(uint8_t reg, uint8_t val); +static int nandcmd_page_read_to_cache(uint32_t row_addr); +static int nandcmd_read_from_cache(uint8_t* buf); +static int nandcmd_program_load(const uint8_t* buf); +static int nandcmd_program_execute(uint32_t row_addr); +static int nandcmd_block_erase(uint32_t block_addr); + +struct nand_drv { + const nand_chip_data* chip_data; + bool write_enabled; +}; + +static struct nand_drv nand_drv; +static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; + +static void nand_drv_reset(void) { - d->chip_ops = NULL; - d->chip_data = NULL; - d->pagebuf = &pagebuffer[0]; - d->auxbuf = &auxbuffer[0]; - d->raw_page_size = 0; - d->flags = 0; + nand_drv.chip_data = NULL; + nand_drv.write_enabled = false; } -/* Driver code */ int nand_open(void) { sfc_init(); sfc_lock(); - /* Reset driver state */ - nand_drv_reset(&nand_driver); - - /* Init hardware */ + nand_drv_reset(); sfc_open(); - sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF); - sfc_set_clock(NAND_INIT_CLOCK_SPEED); - /* Identify NAND chip */ - int status = 0; - int nandid = nandcmd_read_id(&nand_driver); - if(nandid < 0) { - status = NANDERR_CHIP_UNSUPPORTED; - goto _err; - } + const nand_chip_data* chip_data = &target_nand_chip_data[0]; + sfc_set_dev_conf(chip_data->dev_conf); + sfc_set_clock(chip_data->clock_freq); - unsigned char mf_id = nandid >> 8; - unsigned char dev_id = nandid & 0xff; - const nand_chip_desc* desc = &target_nand_chip_descs[0]; - while(1) { - if(desc->data == NULL || desc->ops == NULL) { - status = NANDERR_CHIP_UNSUPPORTED; - goto _err; - } - - if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id) - break; - } - - /* Fill driver parameters */ - nand_driver.chip_ops = desc->ops; - nand_driver.chip_data = desc->data; - nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size; - - /* Configure hardware and run init op */ - sfc_set_dev_conf(desc->data->dev_conf); - sfc_set_clock(desc->data->clock_freq); - - if((status = desc->ops->open(&nand_driver)) < 0) - goto _err; - - _exit: - sfc_unlock(); - return status; - _err: - nand_drv_reset(&nand_driver); - sfc_close(); - goto _exit; + return NAND_SUCCESS; } void nand_close(void) { sfc_lock(); - nand_driver.chip_ops->close(&nand_driver); - nand_drv_reset(&nand_driver); sfc_close(); + nand_drv_reset(); sfc_unlock(); } -int nand_enable_writes(bool en) +int nand_identify(int* mf_id, int* dev_id) { sfc_lock(); - int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en); - if(st >= 0) { - if(en) - nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE; + int status = nandcmd_read_id(mf_id, dev_id); + if(status < 0) + goto error; + + for(size_t i = 0; i < target_nand_chip_count; ++i) { + const nand_chip_data* data = &target_nand_chip_data[i]; + if(data->mf_id == *mf_id && data->dev_id == *dev_id) { + nand_drv.chip_data = data; + break; + } + } + + if(!nand_drv.chip_data) { + status = NAND_ERR_UNKNOWN_CHIP; + goto error; + } + + /* Set parameters according to new chip data */ + sfc_set_dev_conf(nand_drv.chip_data->dev_conf); + sfc_set_clock(nand_drv.chip_data->clock_freq); + status = NAND_SUCCESS; + + error: + sfc_unlock(); + return status; +} + +const nand_chip_data* nand_get_chip_data(void) +{ + return nand_drv.chip_data; +} + +extern int nand_enable_writes(bool en) +{ + if(en == nand_drv.write_enabled) + return NAND_SUCCESS; + + int rc = nandop_set_write_protect(!en); + if(rc == NAND_SUCCESS) + nand_drv.write_enabled = en; + + return rc; +} + +static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) +{ + const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size); + + if(addr & (page_size - 1)) + return NAND_ERR_UNALIGNED; + if(size & (page_size - 1)) + return NAND_ERR_UNALIGNED; + if(size <= 0) + return NAND_SUCCESS; + if(write && !nand_drv.write_enabled) + return NAND_ERR_WRITE_PROTECT; + /* FIXME: re-enable this check after merging new SPL+bootloader. + * It's only necessary for DMA, which is currently not used, but it's a + * good practice anyway. Disable for now due to SPL complications. */ + /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) + return NAND_ERR_UNALIGNED;*/ + + addr >>= nand_drv.chip_data->log2_page_size; + size >>= nand_drv.chip_data->log2_page_size; + + int rc = NAND_SUCCESS; + sfc_lock(); + + for(; size > 0; --size, ++addr, buf += page_size) { + if(write) + rc = nandop_write_page(addr, buf); else - nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE; + rc = nandop_read_page(addr, buf); + + if(rc) + break; } sfc_unlock(); - return st; + return rc; } -extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf) +int nand_read(uint32_t addr, uint32_t size, uint8_t* buf) { - if(count <= 0) - return 0; + return nand_rdwr(false, addr, size, buf); +} - nand_drv* d = &nand_driver; - uint32_t rowaddr = byteaddr / d->chip_data->page_size; - uint32_t coladdr = byteaddr % d->chip_data->page_size; - unsigned char* dstbuf = (unsigned char*)buf; - int status = 0; +int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf) +{ + return nand_rdwr(true, addr, size, (uint8_t*)buf); +} +int nand_erase(uint32_t addr, uint32_t size) +{ + const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size; + const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size; + const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_page_size; + + if(addr & (block_size - 1)) + return NAND_ERR_UNALIGNED; + if(size & (block_size - 1)) + return NAND_ERR_UNALIGNED; + if(size <= 0) + return NAND_SUCCESS; + if(!nand_drv.write_enabled) + return NAND_ERR_WRITE_PROTECT; + + addr >>= nand_drv.chip_data->log2_page_size; + size >>= nand_drv.chip_data->log2_page_size; + size >>= nand_drv.chip_data->log2_block_size; + + int rc = NAND_SUCCESS; sfc_lock(); - do { - if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) { - status = NANDERR_READ_FAILED; - goto _end; - } - if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) { - status = NANDERR_ECC_FAILED; - goto _end; - } + for(; size > 0; --size, addr += pages_per_block) + if((rc = nandop_erase_block(addr))) + break; - int amount = d->chip_data->page_size - coladdr; - if(amount > count) - amount = count; - - memcpy(dstbuf, d->pagebuf, amount); - dstbuf += amount; - count -= amount; - rowaddr += 1; - coladdr = 0; - } while(count > 0); - - _end: sfc_unlock(); - return status; + return rc; } -int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) +/* + * NAND ops + */ + +static int nandop_wait_status(int errbit) { - nand_drv* d = &nand_driver; - - if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) - return NANDERR_WRITE_PROTECTED; - - if(count <= 0) - return 0; - - uint32_t rowaddr = byteaddr / d->chip_data->page_size; - uint32_t coladdr = byteaddr % d->chip_data->page_size; - - /* Only support whole page writes right now */ - if(coladdr != 0) - return NANDERR_UNALIGNED_ADDRESS; - if(count % d->chip_data->page_size) - return NANDERR_UNALIGNED_LENGTH; - - const unsigned char* srcbuf = (const unsigned char*)buf; - int status = 0; - - sfc_lock(); + int reg; do { - memcpy(d->pagebuf, srcbuf, d->chip_data->page_size); - d->chip_ops->ecc_write(d, d->pagebuf); + reg = nandcmd_get_feature(NAND_FREG_STATUS); + if(reg < 0) + return reg; + } while(reg & NAND_FREG_STATUS_OIP); - if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) { - status = NANDERR_PROGRAM_FAILED; - goto _end; - } + if(reg & errbit) + return NAND_ERR_COMMAND; - rowaddr += 1; - srcbuf += d->chip_data->page_size; - count -= d->chip_data->page_size; - } while(count > 0); - - _end: - sfc_unlock(); - return status; + return reg; } -int nand_erase_bytes(uint32_t byteaddr, int count) +static int nandop_read_page(uint32_t row_addr, uint8_t* buf) { - nand_drv* d = &nand_driver; + int status; - if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) - return NANDERR_WRITE_PROTECTED; + if((status = nandcmd_page_read_to_cache(row_addr)) < 0) + return status; + if((status = nandop_wait_status(0)) < 0) + return status; + if((status = nandcmd_read_from_cache(buf)) < 0) + return status; - /* Ensure address is aligned to a block boundary */ - if(byteaddr % d->chip_data->page_size) - return NANDERR_UNALIGNED_ADDRESS; + return NAND_SUCCESS; +} - uint32_t blockaddr = byteaddr / d->chip_data->page_size; - if(blockaddr % d->chip_data->block_size) - return NANDERR_UNALIGNED_ADDRESS; +static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) +{ + int status; - /* Ensure length is also aligned to the size of a block */ - if(count % d->chip_data->page_size) - return NANDERR_UNALIGNED_LENGTH; + if((status = nandcmd_write_enable()) < 0) + return status; + if((status = nandcmd_program_load(buf)) < 0) + return status; + if((status = nandcmd_program_execute(row_addr)) < 0) + return status; + if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0) + return status; - count /= d->chip_data->page_size; - if(count % d->chip_data->block_size) - return NANDERR_UNALIGNED_LENGTH; + return NAND_SUCCESS; +} - count /= d->chip_data->block_size; +static int nandop_erase_block(uint32_t block_addr) +{ + int status; - int status = 0; - sfc_lock(); + if((status = nandcmd_write_enable()) < 0) + return status; + if((status = nandcmd_block_erase(block_addr)) < 0) + return status; + if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0) + return status; - for(int i = 0; i < count; ++i) { - if(d->chip_ops->erase_block(d, blockaddr)) { - status = NANDERR_ERASE_FAILED; - goto _end; - } + return NAND_SUCCESS; +} - /* Advance to next block */ - blockaddr += d->chip_data->block_size; +static int nandop_set_write_protect(bool en) +{ + int val = nandcmd_get_feature(NAND_FREG_PROTECTION); + if(val < 0) + return val; + + if(en) { + val &= ~NAND_FREG_PROTECTION_ALLBP; + if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) + val &= ~NAND_FREG_PROTECTION_BRWD; + } else { + val |= NAND_FREG_PROTECTION_ALLBP; + if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) + val |= NAND_FREG_PROTECTION_BRWD; } - _end: - sfc_unlock(); - return status; + /* NOTE: The WP pin typically only protects changes to the protection + * register -- it doesn't actually prevent writing to the chip. That's + * why it should be re-enabled after setting the new protection status. + */ + sfc_set_wp_enable(false); + int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val); + sfc_set_wp_enable(true); + + if(status < 0) + return status; + + return NAND_SUCCESS; } -int nandcmd_read_id(nand_drv* d) +/* + * Low-level NAND commands + */ + +static int nandcmd_read_id(int* mf_id, int* dev_id) { sfc_op op = {0}; op.command = NAND_CMD_READ_ID; @@ -274,72 +371,72 @@ int nandcmd_read_id(nand_drv* d) op.addr_bytes = 1; op.addr_lo = 0; op.data_bytes = 2; - op.buffer = d->auxbuf; + op.buffer = nand_auxbuf; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return (d->auxbuf[0] << 8) | d->auxbuf[1]; + *mf_id = nand_auxbuf[0]; + *dev_id = nand_auxbuf[1]; + return NAND_SUCCESS; } -int nandcmd_write_enable(nand_drv* d) +static int nandcmd_write_enable(void) { - (void)d; - sfc_op op = {0}; op.command = NAND_CMD_WRITE_ENABLE; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_get_feature(nand_drv* d, int reg) +static int nandcmd_get_feature(uint8_t reg) { sfc_op op = {0}; op.command = NAND_CMD_GET_FEATURE; op.flags = SFC_FLAG_READ; op.addr_bytes = 1; - op.addr_lo = reg & 0xff; + op.addr_lo = reg; op.data_bytes = 1; - op.buffer = d->auxbuf; + op.buffer = nand_auxbuf; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return d->auxbuf[0]; + return nand_auxbuf[0]; } -int nandcmd_set_feature(nand_drv* d, int reg, int val) +static int nandcmd_set_feature(uint8_t reg, uint8_t val) { sfc_op op = {0}; op.command = NAND_CMD_SET_FEATURE; op.flags = SFC_FLAG_READ; op.addr_bytes = 1; - op.addr_lo = reg & 0xff; + op.addr_lo = reg; op.data_bytes = 1; - op.buffer = d->auxbuf; - d->auxbuf[0] = val & 0xff; + op.buffer = nand_auxbuf; + nand_auxbuf[0] = val; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr) +static int nandcmd_page_read_to_cache(uint32_t row_addr) { sfc_op op = {0}; op.command = NAND_CMD_PAGE_READ_TO_CACHE; - op.addr_bytes = d->chip_data->rowaddr_width; - op.addr_lo = rowaddr; + op.addr_bytes = nand_drv.chip_data->rowaddr_width; + op.addr_lo = row_addr; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) +static int nandcmd_read_from_cache(uint8_t* buf) { sfc_op op = {0}; - if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { + if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { op.command = NAND_CMD_READ_FROM_CACHEx4; op.mode = SFC_MODE_QUAD_IO; } else { @@ -348,21 +445,21 @@ int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) } op.flags = SFC_FLAG_READ; - op.addr_bytes = d->chip_data->coladdr_width; + op.addr_bytes = nand_drv.chip_data->coladdr_width; op.addr_lo = 0; - op.dummy_bits = 8; - op.data_bytes = d->raw_page_size; + op.dummy_bits = 8; // NOTE: this may need a chip_data parameter + op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); op.buffer = buf; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_program_load(nand_drv* d, const unsigned char* buf) +static int nandcmd_program_load(const uint8_t* buf) { sfc_op op = {0}; - if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { + if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { op.command = NAND_CMD_PROGRAM_LOADx4; op.mode = SFC_MODE_QUAD_IO; } else { @@ -371,156 +468,36 @@ int nandcmd_program_load(nand_drv* d, const unsigned char* buf) } op.flags = SFC_FLAG_WRITE; - op.addr_bytes = d->chip_data->coladdr_width; + op.addr_bytes = nand_drv.chip_data->coladdr_width; op.addr_lo = 0; - op.data_bytes = d->raw_page_size; + op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); op.buffer = (void*)buf; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr) +static int nandcmd_program_execute(uint32_t row_addr) { sfc_op op = {0}; op.command = NAND_CMD_PROGRAM_EXECUTE; - op.addr_bytes = d->chip_data->rowaddr_width; - op.addr_lo = rowaddr; + op.addr_bytes = nand_drv.chip_data->rowaddr_width; + op.addr_lo = row_addr; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; + return NAND_SUCCESS; } -int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr) +static int nandcmd_block_erase(uint32_t block_addr) { sfc_op op = {0}; op.command = NAND_CMD_BLOCK_ERASE; - op.addr_bytes = d->chip_data->rowaddr_width; - op.addr_lo = blockaddr; + op.addr_bytes = nand_drv.chip_data->rowaddr_width; + op.addr_lo = block_addr; if(sfc_exec(&op)) - return NANDERR_COMMAND_FAILED; + return NAND_ERR_CONTROLLER; - return 0; -} - -const nand_chip_ops nand_chip_ops_std = { - .open = nandop_std_open, - .close = nandop_std_close, - .read_page = nandop_std_read_page, - .write_page = nandop_std_write_page, - .erase_block = nandop_std_erase_block, - .set_wp_enable = nandop_std_set_wp_enable, - .ecc_read = nandop_ecc_none_read, - .ecc_write = nandop_ecc_none_write, -}; - -/* Helper needed by other ops */ -static int nandop_std_wait_status(nand_drv* d, int errbit) -{ - int reg; - do { - reg = nandcmd_get_feature(d, NAND_FREG_STATUS); - if(reg < 0) - return reg; - } while(reg & NAND_FREG_STATUS_OIP); - - if(reg & errbit) - return NANDERR_OTHER; - - return reg; -} - -int nandop_std_open(nand_drv* d) -{ - (void)d; - return 0; -} - -void nandop_std_close(nand_drv* d) -{ - (void)d; -} - -int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf) -{ - int status; - - if((status = nandcmd_page_read_to_cache(d, rowaddr)) < 0) - return status; - if((status = nandop_std_wait_status(d, 0)) < 0) - return status; - if((status = nandcmd_read_from_cache(d, buf)) < 0) - return status; - - return 0; -} - -int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf) -{ - int status; - - if((status = nandcmd_write_enable(d)) < 0) - return status; - if((status = nandcmd_program_load(d, buf)) < 0) - return status; - if((status = nandcmd_program_execute(d, rowaddr)) < 0) - return status; - if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL)) < 0) - return status; - - return 0; -} - -int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr) -{ - int status; - - if((status = nandcmd_write_enable(d)) < 0) - return status; - if((status = nandcmd_block_erase(d, blockaddr)) < 0) - return status; - if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0)) - return status; - - return 0; -} - -int nandop_std_set_wp_enable(nand_drv* d, bool en) -{ - int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION); - if(val < 0) - return val; - - if(en) { - val &= ~NAND_FREG_PROTECTION_ALLBP; - if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD) - val &= ~NAND_FREG_PROTECTION_BRWD; - } else { - val |= NAND_FREG_PROTECTION_ALLBP; - if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD) - val |= NAND_FREG_PROTECTION_BRWD; - } - - sfc_set_wp_enable(false); - int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val); - sfc_set_wp_enable(true); - - if(status < 0) - return status; - - return 0; -} - -int nandop_ecc_none_read(nand_drv* d, unsigned char* buf) -{ - (void)d; - (void)buf; - return 0; -} - -void nandop_ecc_none_write(nand_drv* d, unsigned char* buf) -{ - memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size); + return NAND_SUCCESS; } diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h index f6709aaaf9..6c415b1170 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.h +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h @@ -26,6 +26,11 @@ * Not suitable for general data storage. It doesn't have proper support for * partial page writes, access to spare area, etc, which are all necessary * for an effective flash translation layer. + * + * There's no ECC support. This can be added if necessary, but it's unlikely + * the boot area on any X1000 device uses software ECC as Ingenic's SPL simply + * doesn't have much room for more code (theirs programmed to work on multiple + * hardware configurations, so it's bigger than ours). */ #include @@ -39,176 +44,57 @@ /* Set/clear the BRWD bit when enabling/disabling write protection */ #define NANDCHIP_FLAG_USE_BRWD 0x02 -typedef struct nand_drv nand_drv; - -/* Defines some static information about a NAND chip */ typedef struct nand_chip_data { - const char* name; /* Name for debugging purposes */ - uint8_t mf_id; /* Manufacturer ID */ - uint8_t dev_id; /* Device ID */ - uint8_t rowaddr_width; /* Number of bytes in row addresses */ - uint8_t coladdr_width; /* Number of bytes in column addresses */ - uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */ - uint32_t clock_freq; /* Frequency to switch to after identification */ - uint32_t block_size; /* Number of pages per eraseblock */ - uint32_t page_size; /* Number of data bytes per page */ - uint32_t spare_size; /* Number of spare bytes per page */ - int flags; /* Various flags */ + /* Chip manufacturer / device ID */ + uint8_t mf_id; + uint8_t dev_id; + + /* Width of row/column addresses in bytes */ + uint8_t rowaddr_width; + uint8_t coladdr_width; + + /* SFC dev conf and clock frequency to use for this device */ + uint32_t dev_conf; + uint32_t clock_freq; + + /* Page size in bytes = 1 << log2_page_size */ + uint32_t log2_page_size; + + /* Block size in number of pages = 1 << log2_block_size */ + uint32_t log2_block_size; + + /* Chip flags */ + uint32_t flags; } nand_chip_data; -/* Defines high-level operations used to implement the public API. - * Chips may need to override operations if the default ones aren't suitable. - * - * Negative return codes return an error, while zero or positive codes are - * considered successful. This allows a function to return meaningful data, - * if applicable. +/* Open or close the NAND driver. The NAND driver takes control of the SFC, + * so that driver must be in the closed state before opening the NAND driver. */ -typedef struct nand_chip_ops { - /* Called once after identifying the chip */ - int(*open)(nand_drv* d); - - /* Called once when the driver is closed */ - void(*close)(nand_drv* d); - - /* Read or write a complete page including both main and spare areas. */ - int(*read_page)(nand_drv* d, uint32_t rowaddr, unsigned char* buf); - int(*write_page)(nand_drv* d, uint32_t rowaddr, const unsigned char* buf); - - /* Erase a block. */ - int(*erase_block)(nand_drv* d, uint32_t blockaddr); - - /* Enable or disable the chip's write protection. */ - int(*set_wp_enable)(nand_drv* d, bool en); - - /* Perform error correction and detection on a raw page (main + spare). - * Return the number of errors detected and corrected, or a negative value - * if errors were detected but could not be corrected. - */ - int(*ecc_read)(nand_drv* d, unsigned char* buf); - - /* Generate ECC data for a page. The buffer main area is already filled - * and this function should write ECC data into the spare area. - */ - void(*ecc_write)(nand_drv* d, unsigned char* buf); -} nand_chip_ops; - -/* Struct used to list all supported NAND chips in an array */ -typedef struct nand_chip_desc { - const nand_chip_data* data; - const nand_chip_ops* ops; -} nand_chip_desc; - -/* NAND driver structure. It can be accessed by chip ops, but they must not - * modify any fields except for "auxbuf", which is a small buffer that can - * be used for commands that need to read/write small amounts of data: often - * needed for polling status, etc. - */ -struct nand_drv { - const nand_chip_ops* chip_ops; - const nand_chip_data* chip_data; - unsigned char* pagebuf; - unsigned char* auxbuf; - uint32_t raw_page_size; - int flags; -}; - -/* Note: sfc_init() must be called prior to nand_open() */ extern int nand_open(void); extern void nand_close(void); -/* Controls device-side write protection registers as well as software lock. - * Erase and program operations will fail unless you first enable writes. - */ +/* Identify the NAND chip. This must be done after opening the driver and + * prior to any data access, in order to set the chip parameters. */ +extern int nand_identify(int* mf_id, int* dev_id); + +/* Return the chip data for the identified NAND chip. + * Returns NULL if the chip is not identified. */ +const nand_chip_data* nand_get_chip_data(void); + +/* Controls the chip's write protect features. The driver also keeps track of + * this flag and refuses to perform write or erase operations unless you have + * enabled writes. Writes should be disabled again when you finish writing. */ extern int nand_enable_writes(bool en); -/* Byte-based NAND operations */ -extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf); -extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf); -extern int nand_erase_bytes(uint32_t byteaddr, int count); +/* Reading and writing operates on whole pages at a time. If the address or + * size is not aligned to a multiple of the page size, no data will be read + * or written and an error code is returned. */ +extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf); +extern int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf); -/* NAND command numbers */ -#define NAND_CMD_READ_ID 0x9f -#define NAND_CMD_WRITE_ENABLE 0x06 -#define NAND_CMD_GET_FEATURE 0x0f -#define NAND_CMD_SET_FEATURE 0x1f -#define NAND_CMD_PAGE_READ_TO_CACHE 0x13 -#define NAND_CMD_READ_FROM_CACHE 0x0b -#define NAND_CMD_READ_FROM_CACHEx4 0x6b -#define NAND_CMD_PROGRAM_LOAD 0x02 -#define NAND_CMD_PROGRAM_LOADx4 0x32 -#define NAND_CMD_PROGRAM_EXECUTE 0x10 -#define NAND_CMD_BLOCK_ERASE 0xd8 - -/* NAND device register addresses for GET_FEATURE / SET_FEATURE */ -#define NAND_FREG_PROTECTION 0xa0 -#define NAND_FREG_FEATURE 0xb0 -#define NAND_FREG_STATUS 0xc0 - -/* Protection register bits */ -#define NAND_FREG_PROTECTION_BRWD 0x80 -#define NAND_FREG_PROTECTION_BP2 0x20 -#define NAND_FREG_PROTECTION_BP1 0x10 -#define NAND_FREG_PROTECTION_BP0 0x80 -/* Mask of BP bits 0-2 */ -#define NAND_FREG_PROTECTION_ALLBP (0x38) - -/* Feature register bits */ -#define NAND_FREG_FEATURE_QE 0x01 - -/* Status register bits */ -#define NAND_FREG_STATUS_OIP 0x01 -#define NAND_FREG_STATUS_WEL 0x02 -#define NAND_FREG_STATUS_E_FAIL 0x04 -#define NAND_FREG_STATUS_P_FAIL 0x08 - -/* Standard implementations for low-level NAND commands. I'm not aware of any - * actual standard governing these, but it seems many vendors follow the same - * command numbering, status bits, and behavior so these implementations should - * work across a wide variety of chips. - * - * If adding a new NAND chip which only has a minor deviation from these - * standard implementations, consider adding a flag and modifying these - * functions to change their behavior based on the flag, instead of writing - * a whole new implementation. - * - * None of these functions are directly called by the high-level driver code, - * except for nandcmd_std_read_id(). They can be used to implement higher-level - * functions in a device's "nand_chip_ops". - */ -extern int nandcmd_read_id(nand_drv* d); -extern int nandcmd_write_enable(nand_drv* d); -extern int nandcmd_get_feature(nand_drv* d, int reg); -extern int nandcmd_set_feature(nand_drv* d, int reg, int val); -extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr); -extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf); -extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf); -extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr); -extern int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr); - -/* Table filled with all the standard operations for chips which don't - * need to override any operations. - */ -extern const nand_chip_ops nand_chip_ops_std; - -/* Standard NAND chip ops based on the standard "nandcmd" functions. - * - * Same advice applies here as there: if it's possible to support minor - * chip variations with a flag, modify these functions to do so. - */ -extern int nandop_std_open(nand_drv* d); -extern void nandop_std_close(nand_drv* d); -extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf); -extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf); -extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr); -extern int nandop_std_set_wp_enable(nand_drv* d, bool en); - -/* The default ECC implementation is a no-op: reads always succeed without - * reporting errors and writes will fill the spare area with '0xff' bytes. - * - * For chips that support internal ECC, this often works because the chip will - * ignore writes to the ECC areas. - */ -extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf); -extern void nandop_ecc_none_write(nand_drv* d, unsigned char* buf); +/* Ereas eoperates on whole blocks. Like the page read/write operations, + * the address and size must be aligned to a multiple of the block size. + * If not, no blocks are erased and an error code is returned. */ +extern int nand_erase(uint32_t addr, uint32_t size); #endif /* __NAND_X1000_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c index 36ef25f60d..6505cabbef 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c @@ -229,29 +229,39 @@ static void init(void) static int nandread(uint32_t addr, uint32_t size, void* buffer) { int rc; + int mf_id, dev_id; if((rc = nand_open())) return rc; + if((rc = nand_identify(&mf_id, &dev_id))) { + nand_close(); + return rc; + } - rc = nand_read_bytes(addr, size, buffer); + rc = nand_read(addr, size, (uint8_t*)buffer); nand_close(); return rc; } -static int nandwrite(uint32_t addr, uint32_t size, void* buffer) +static int nandwrite(uint32_t addr, uint32_t size, const void* buffer) { int rc; + int mf_id, dev_id; if((rc = nand_open())) return rc; + if((rc = nand_identify(&mf_id, &dev_id))) { + nand_close(); + return rc; + } if((rc = nand_enable_writes(true))) goto _end; - if((rc = nand_erase_bytes(addr, size))) + if((rc = nand_erase(addr, size))) goto _end1; - rc = nand_write_bytes(addr, size, buffer); + rc = nand_write(addr, size, (const uint8_t*)buffer); _end1: /* an error here is very unlikely, so ignore it */ @@ -309,7 +319,7 @@ void main(void) case SPL_CMD_FLASH_WRITE: SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, SPL_ARGUMENTS->param2, - (void*)SPL_BUFFER_ADDRESS); + (const void*)SPL_BUFFER_ADDRESS); return; } } diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds index e8bf9d4700..36ae8c34d1 100644 --- a/firmware/target/mips/ingenic_x1000/spl.lds +++ b/firmware/target/mips/ingenic_x1000/spl.lds @@ -11,12 +11,6 @@ MEMORY { * and the next 2k are occupied by SPL header */ TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800, LENGTH = X1000_TCSM_SIZE - 0x1800 - - /* Small area of DRAM is required for NAND bounce buffers, - * though not strictly necessary as ECC isn't really practical - * this early in the boot */ - DRAM : ORIGIN = X1000_DRAM_END - 16K, - LENGTH = 16K } SECTIONS @@ -52,11 +46,6 @@ SECTIONS _bssend = .; } > TCSM - .sdram (NOLOAD) : - { - *(.sdram); - } > DRAM - /DISCARD/ : { *(.MIPS.abiflags);