1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Aidan MacDonald 2021-05-06 00:22:56 +01:00
parent 244aad750c
commit 15ad1c42db
9 changed files with 417 additions and 622 deletions

View file

@ -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/button-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-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 target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
#ifdef BOOTLOADER_SPL #ifdef BOOTLOADER_SPL
target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c

View file

@ -21,6 +21,7 @@
#include "installer.h" #include "installer.h"
#include "nand-x1000.h" #include "nand-x1000.h"
#include "system.h"
#include "core_alloc.h" #include "core_alloc.h"
#include "file.h" #include "file.h"
@ -40,22 +41,27 @@
static int install_from_buffer(const void* buf) static int install_from_buffer(const void* buf)
{ {
int status = INSTALL_SUCCESS;
int mf_id, dev_id;
if(nand_open()) if(nand_open())
return ERR_FLASH_OPEN_FAILED; return ERR_FLASH_OPEN_FAILED;
if(nand_identify(&mf_id, &dev_id)) {
int status = INSTALL_SUCCESS; status = ERR_FLASH_OPEN_FAILED;
goto _exit;
}
if(nand_enable_writes(true)) { if(nand_enable_writes(true)) {
status = ERR_FLASH_DISABLE_WP_FAILED; status = ERR_FLASH_DISABLE_WP_FAILED;
goto _exit; goto _exit;
} }
if(nand_erase_bytes(0, BOOT_IMAGE_SIZE)) { if(nand_erase(0, BOOT_IMAGE_SIZE)) {
status = ERR_FLASH_ERASE_FAILED; status = ERR_FLASH_ERASE_FAILED;
goto _exit; 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; status = ERR_FLASH_WRITE_FAILED;
goto _exit; goto _exit;
} }
@ -72,12 +78,17 @@ static int install_from_buffer(const void* buf)
static int dump_to_buffer(void* buf) static int dump_to_buffer(void* buf)
{ {
int status = INSTALL_SUCCESS;
int mf_id, dev_id;
if(nand_open()) if(nand_open())
return ERR_FLASH_OPEN_FAILED; 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(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) {
if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) {
status = ERR_FLASH_READ_FAILED; status = ERR_FLASH_READ_FAILED;
goto _exit; goto _exit;
} }
@ -90,12 +101,14 @@ static int dump_to_buffer(void* buf)
int install_bootloader(const char* path) int install_bootloader(const char* path)
{ {
/* Allocate memory to hold image */ /* 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) if(handle < 0)
return ERR_OUT_OF_MEMORY; return ERR_OUT_OF_MEMORY;
int status = INSTALL_SUCCESS; int status = INSTALL_SUCCESS;
void* buffer = core_get_data(handle); void* buffer = core_get_data(handle);
CACHEALIGN_BUFFER(buffer, bufsize);
/* Open the boot image */ /* Open the boot image */
int fd = open(path, O_RDONLY); int fd = open(path, O_RDONLY);
@ -132,13 +145,15 @@ int install_bootloader(const char* path)
int dump_bootloader(const char* path) int dump_bootloader(const char* path)
{ {
/* Allocate memory to hold image */ /* 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) if(handle < 0)
return -1; return -1;
/* Read data from flash */ /* Read data from flash */
int fd = -1; int fd = -1;
void* buffer = core_get_data(handle); void* buffer = core_get_data(handle);
CACHEALIGN_BUFFER(buffer, bufsize);
int status = dump_to_buffer(buffer); int status = dump_to_buffer(buffer);
if(status) if(status)
goto _exit; goto _exit;

View file

@ -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},
};

View file

@ -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__ */

View file

@ -15,4 +15,13 @@
#define NANDERR_COMMAND_FAILED (-9) #define NANDERR_COMMAND_FAILED (-9)
#define NANDERR_OTHER (-99) #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__ */ #endif /* __NAND_X1000_ERR_H__ */

View file

@ -20,253 +20,350 @@
****************************************************************************/ ****************************************************************************/
#include "nand-x1000.h" #include "nand-x1000.h"
#include "nand-target.h"
#include "sfc-x1000.h" #include "sfc-x1000.h"
#include "system.h" #include "system.h"
#include <string.h> #include <stddef.h>
#if !defined(NAND_MAX_PAGE_SIZE) || \ /* NAND command numbers */
!defined(NAND_INIT_SFC_DEV_CONF) || \ #define NAND_CMD_READ_ID 0x9f
!defined(NAND_INIT_CLOCK_SPEED) #define NAND_CMD_WRITE_ENABLE 0x06
# error "Target needs to specify NAND driver parameters" #define NAND_CMD_GET_FEATURE 0x0f
#endif #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 */ /* NAND device register addresses for GET_FEATURE / SET_FEATURE */
#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE #define NAND_FREG_PROTECTION 0xa0
#define NAND_FREG_FEATURE 0xb0
#define NAND_FREG_STATUS 0xc0
/* Writes have been enabled */ /* Protection register bits */
#define NAND_DRV_FLAG_WRITEABLE 0x01 #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 */ /* Feature register bits */
extern const nand_chip_desc target_nand_chip_descs[]; #define NAND_FREG_FEATURE_QE 0x01
#ifdef BOOTLOADER_SPL /* Status register bits */
# define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR #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 #else
# define NANDBUFFER_ATTR CACHEALIGN_ATTR /* Nobody will use this anyway if the device has no NAND flash */
{ 0 },
#endif #endif
};
/* Globals for the driver */ const size_t target_nand_chip_count =
static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; sizeof(target_nand_chip_data) / sizeof(nand_chip_data);
static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR;
static nand_drv nand_driver;
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; nand_drv.chip_data = NULL;
d->chip_data = NULL; nand_drv.write_enabled = false;
d->pagebuf = &pagebuffer[0];
d->auxbuf = &auxbuffer[0];
d->raw_page_size = 0;
d->flags = 0;
} }
/* Driver code */
int nand_open(void) int nand_open(void)
{ {
sfc_init(); sfc_init();
sfc_lock(); sfc_lock();
/* Reset driver state */ nand_drv_reset();
nand_drv_reset(&nand_driver);
/* Init hardware */
sfc_open(); sfc_open();
sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF);
sfc_set_clock(NAND_INIT_CLOCK_SPEED);
/* Identify NAND chip */ const nand_chip_data* chip_data = &target_nand_chip_data[0];
int status = 0; sfc_set_dev_conf(chip_data->dev_conf);
int nandid = nandcmd_read_id(&nand_driver); sfc_set_clock(chip_data->clock_freq);
if(nandid < 0) {
status = NANDERR_CHIP_UNSUPPORTED;
goto _err;
}
unsigned char mf_id = nandid >> 8; return NAND_SUCCESS;
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;
} }
void nand_close(void) void nand_close(void)
{ {
sfc_lock(); sfc_lock();
nand_driver.chip_ops->close(&nand_driver);
nand_drv_reset(&nand_driver);
sfc_close(); sfc_close();
nand_drv_reset();
sfc_unlock(); sfc_unlock();
} }
int nand_enable_writes(bool en) int nand_identify(int* mf_id, int* dev_id)
{ {
sfc_lock(); sfc_lock();
int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en); int status = nandcmd_read_id(mf_id, dev_id);
if(st >= 0) { if(status < 0)
if(en) goto error;
nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE;
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 else
nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE; rc = nandop_read_page(addr, buf);
if(rc)
break;
} }
sfc_unlock(); 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 nand_rdwr(false, addr, size, buf);
return 0; }
nand_drv* d = &nand_driver; int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf)
uint32_t rowaddr = byteaddr / d->chip_data->page_size; {
uint32_t coladdr = byteaddr % d->chip_data->page_size; return nand_rdwr(true, addr, size, (uint8_t*)buf);
unsigned char* dstbuf = (unsigned char*)buf; }
int status = 0;
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(); sfc_lock();
for(; size > 0; --size, addr += pages_per_block)
if((rc = nandop_erase_block(addr)))
break;
sfc_unlock();
return rc;
}
/*
* NAND ops
*/
static int nandop_wait_status(int errbit)
{
int reg;
do { do {
if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) { reg = nandcmd_get_feature(NAND_FREG_STATUS);
status = NANDERR_READ_FAILED; if(reg < 0)
goto _end; return reg;
} } while(reg & NAND_FREG_STATUS_OIP);
if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) { if(reg & errbit)
status = NANDERR_ECC_FAILED; return NAND_ERR_COMMAND;
goto _end;
}
int amount = d->chip_data->page_size - coladdr; return reg;
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;
} }
int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) 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) if((status = nandcmd_page_read_to_cache(row_addr)) < 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();
do {
memcpy(d->pagebuf, srcbuf, d->chip_data->page_size);
d->chip_ops->ecc_write(d, d->pagebuf);
if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) {
status = NANDERR_PROGRAM_FAILED;
goto _end;
}
rowaddr += 1;
srcbuf += d->chip_data->page_size;
count -= d->chip_data->page_size;
} while(count > 0);
_end:
sfc_unlock();
return status; return status;
if((status = nandop_wait_status(0)) < 0)
return status;
if((status = nandcmd_read_from_cache(buf)) < 0)
return status;
return NAND_SUCCESS;
} }
int nand_erase_bytes(uint32_t byteaddr, int count) static int nandop_write_page(uint32_t row_addr, const uint8_t* buf)
{ {
nand_drv* d = &nand_driver; int status;
if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) if((status = nandcmd_write_enable()) < 0)
return NANDERR_WRITE_PROTECTED;
/* Ensure address is aligned to a block boundary */
if(byteaddr % d->chip_data->page_size)
return NANDERR_UNALIGNED_ADDRESS;
uint32_t blockaddr = byteaddr / d->chip_data->page_size;
if(blockaddr % d->chip_data->block_size)
return NANDERR_UNALIGNED_ADDRESS;
/* Ensure length is also aligned to the size of a block */
if(count % d->chip_data->page_size)
return NANDERR_UNALIGNED_LENGTH;
count /= d->chip_data->page_size;
if(count % d->chip_data->block_size)
return NANDERR_UNALIGNED_LENGTH;
count /= d->chip_data->block_size;
int status = 0;
sfc_lock();
for(int i = 0; i < count; ++i) {
if(d->chip_ops->erase_block(d, blockaddr)) {
status = NANDERR_ERASE_FAILED;
goto _end;
}
/* Advance to next block */
blockaddr += d->chip_data->block_size;
}
_end:
sfc_unlock();
return status; 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;
return NAND_SUCCESS;
} }
int nandcmd_read_id(nand_drv* d) static int nandop_erase_block(uint32_t block_addr)
{
int status;
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;
return NAND_SUCCESS;
}
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;
}
/* 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;
}
/*
* Low-level NAND commands
*/
static int nandcmd_read_id(int* mf_id, int* dev_id)
{ {
sfc_op op = {0}; sfc_op op = {0};
op.command = NAND_CMD_READ_ID; op.command = NAND_CMD_READ_ID;
@ -274,72 +371,72 @@ int nandcmd_read_id(nand_drv* d)
op.addr_bytes = 1; op.addr_bytes = 1;
op.addr_lo = 0; op.addr_lo = 0;
op.data_bytes = 2; op.data_bytes = 2;
op.buffer = d->auxbuf; op.buffer = nand_auxbuf;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_WRITE_ENABLE; op.command = NAND_CMD_WRITE_ENABLE;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_GET_FEATURE; op.command = NAND_CMD_GET_FEATURE;
op.flags = SFC_FLAG_READ; op.flags = SFC_FLAG_READ;
op.addr_bytes = 1; op.addr_bytes = 1;
op.addr_lo = reg & 0xff; op.addr_lo = reg;
op.data_bytes = 1; op.data_bytes = 1;
op.buffer = d->auxbuf; op.buffer = nand_auxbuf;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_SET_FEATURE; op.command = NAND_CMD_SET_FEATURE;
op.flags = SFC_FLAG_READ; op.flags = SFC_FLAG_READ;
op.addr_bytes = 1; op.addr_bytes = 1;
op.addr_lo = reg & 0xff; op.addr_lo = reg;
op.data_bytes = 1; op.data_bytes = 1;
op.buffer = d->auxbuf; op.buffer = nand_auxbuf;
d->auxbuf[0] = val & 0xff; nand_auxbuf[0] = val;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_PAGE_READ_TO_CACHE; op.command = NAND_CMD_PAGE_READ_TO_CACHE;
op.addr_bytes = d->chip_data->rowaddr_width; op.addr_bytes = nand_drv.chip_data->rowaddr_width;
op.addr_lo = rowaddr; op.addr_lo = row_addr;
if(sfc_exec(&op)) 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}; 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.command = NAND_CMD_READ_FROM_CACHEx4;
op.mode = SFC_MODE_QUAD_IO; op.mode = SFC_MODE_QUAD_IO;
} else { } else {
@ -348,21 +445,21 @@ int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf)
} }
op.flags = SFC_FLAG_READ; 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.addr_lo = 0;
op.dummy_bits = 8; op.dummy_bits = 8; // NOTE: this may need a chip_data parameter
op.data_bytes = d->raw_page_size; op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
op.buffer = buf; op.buffer = buf;
if(sfc_exec(&op)) 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}; 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.command = NAND_CMD_PROGRAM_LOADx4;
op.mode = SFC_MODE_QUAD_IO; op.mode = SFC_MODE_QUAD_IO;
} else { } else {
@ -371,156 +468,36 @@ int nandcmd_program_load(nand_drv* d, const unsigned char* buf)
} }
op.flags = SFC_FLAG_WRITE; 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.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; op.buffer = (void*)buf;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_PROGRAM_EXECUTE; op.command = NAND_CMD_PROGRAM_EXECUTE;
op.addr_bytes = d->chip_data->rowaddr_width; op.addr_bytes = nand_drv.chip_data->rowaddr_width;
op.addr_lo = rowaddr; op.addr_lo = row_addr;
if(sfc_exec(&op)) 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}; sfc_op op = {0};
op.command = NAND_CMD_BLOCK_ERASE; op.command = NAND_CMD_BLOCK_ERASE;
op.addr_bytes = d->chip_data->rowaddr_width; op.addr_bytes = nand_drv.chip_data->rowaddr_width;
op.addr_lo = blockaddr; op.addr_lo = block_addr;
if(sfc_exec(&op)) if(sfc_exec(&op))
return NANDERR_COMMAND_FAILED; return NAND_ERR_CONTROLLER;
return 0; return NAND_SUCCESS;
}
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);
} }

View file

@ -26,6 +26,11 @@
* Not suitable for general data storage. It doesn't have proper support for * 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 * partial page writes, access to spare area, etc, which are all necessary
* for an effective flash translation layer. * 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 <stdint.h> #include <stdint.h>
@ -39,176 +44,57 @@
/* Set/clear the BRWD bit when enabling/disabling write protection */ /* Set/clear the BRWD bit when enabling/disabling write protection */
#define NANDCHIP_FLAG_USE_BRWD 0x02 #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 { typedef struct nand_chip_data {
const char* name; /* Name for debugging purposes */ /* Chip manufacturer / device ID */
uint8_t mf_id; /* Manufacturer ID */ uint8_t mf_id;
uint8_t dev_id; /* Device ID */ uint8_t dev_id;
uint8_t rowaddr_width; /* Number of bytes in row addresses */
uint8_t coladdr_width; /* Number of bytes in column addresses */ /* Width of row/column addresses in bytes */
uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */ uint8_t rowaddr_width;
uint32_t clock_freq; /* Frequency to switch to after identification */ uint8_t coladdr_width;
uint32_t block_size; /* Number of pages per eraseblock */
uint32_t page_size; /* Number of data bytes per page */ /* SFC dev conf and clock frequency to use for this device */
uint32_t spare_size; /* Number of spare bytes per page */ uint32_t dev_conf;
int flags; /* Various flags */ 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; } nand_chip_data;
/* Defines high-level operations used to implement the public API. /* Open or close the NAND driver. The NAND driver takes control of the SFC,
* Chips may need to override operations if the default ones aren't suitable. * so that driver must be in the closed state before opening the NAND driver.
*
* Negative return codes return an error, while zero or positive codes are
* considered successful. This allows a function to return meaningful data,
* if applicable.
*/ */
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 int nand_open(void);
extern void nand_close(void); extern void nand_close(void);
/* Controls device-side write protection registers as well as software lock. /* Identify the NAND chip. This must be done after opening the driver and
* Erase and program operations will fail unless you first enable writes. * 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); extern int nand_enable_writes(bool en);
/* Byte-based NAND operations */ /* Reading and writing operates on whole pages at a time. If the address or
extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf); * size is not aligned to a multiple of the page size, no data will be read
extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf); * or written and an error code is returned. */
extern int nand_erase_bytes(uint32_t byteaddr, int count); 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 */ /* Ereas eoperates on whole blocks. Like the page read/write operations,
#define NAND_CMD_READ_ID 0x9f * the address and size must be aligned to a multiple of the block size.
#define NAND_CMD_WRITE_ENABLE 0x06 * If not, no blocks are erased and an error code is returned. */
#define NAND_CMD_GET_FEATURE 0x0f extern int nand_erase(uint32_t addr, uint32_t size);
#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);
#endif /* __NAND_X1000_H__ */ #endif /* __NAND_X1000_H__ */

View file

@ -229,29 +229,39 @@ static void init(void)
static int nandread(uint32_t addr, uint32_t size, void* buffer) static int nandread(uint32_t addr, uint32_t size, void* buffer)
{ {
int rc; int rc;
int mf_id, dev_id;
if((rc = nand_open())) if((rc = nand_open()))
return rc; 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(); nand_close();
return rc; 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 rc;
int mf_id, dev_id;
if((rc = nand_open())) if((rc = nand_open()))
return rc; return rc;
if((rc = nand_identify(&mf_id, &dev_id))) {
nand_close();
return rc;
}
if((rc = nand_enable_writes(true))) if((rc = nand_enable_writes(true)))
goto _end; goto _end;
if((rc = nand_erase_bytes(addr, size))) if((rc = nand_erase(addr, size)))
goto _end1; goto _end1;
rc = nand_write_bytes(addr, size, buffer); rc = nand_write(addr, size, (const uint8_t*)buffer);
_end1: _end1:
/* an error here is very unlikely, so ignore it */ /* an error here is very unlikely, so ignore it */
@ -309,7 +319,7 @@ void main(void)
case SPL_CMD_FLASH_WRITE: case SPL_CMD_FLASH_WRITE:
SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1,
SPL_ARGUMENTS->param2, SPL_ARGUMENTS->param2,
(void*)SPL_BUFFER_ADDRESS); (const void*)SPL_BUFFER_ADDRESS);
return; return;
} }
} }

View file

@ -11,12 +11,6 @@ MEMORY {
* and the next 2k are occupied by SPL header */ * and the next 2k are occupied by SPL header */
TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800, TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800,
LENGTH = X1000_TCSM_SIZE - 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 SECTIONS
@ -52,11 +46,6 @@ SECTIONS
_bssend = .; _bssend = .;
} > TCSM } > TCSM
.sdram (NOLOAD) :
{
*(.sdram);
} > DRAM
/DISCARD/ : /DISCARD/ :
{ {
*(.MIPS.abiflags); *(.MIPS.abiflags);