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:
parent
244aad750c
commit
15ad1c42db
9 changed files with 417 additions and 622 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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},
|
|
||||||
};
|
|
||||||
|
|
@ -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__ */
|
|
||||||
|
|
@ -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__ */
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
#else
|
#define NAND_FREG_STATUS_WEL 0x02
|
||||||
# define NANDBUFFER_ATTR CACHEALIGN_ATTR
|
#define NAND_FREG_STATUS_E_FAIL 0x04
|
||||||
#endif
|
#define NAND_FREG_STATUS_P_FAIL 0x08
|
||||||
|
|
||||||
/* Globals for the driver */
|
/* NAND chip config */
|
||||||
static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR;
|
const nand_chip_data target_nand_chip_data[] = {
|
||||||
static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR;
|
#ifdef FIIO_M3K
|
||||||
static nand_drv nand_driver;
|
|
||||||
|
|
||||||
static void nand_drv_reset(nand_drv* d)
|
|
||||||
{
|
{
|
||||||
d->chip_ops = NULL;
|
/* ATO25D1GA */
|
||||||
d->chip_data = NULL;
|
.mf_id = 0x9b,
|
||||||
d->pagebuf = &pagebuffer[0];
|
.dev_id = 0x12,
|
||||||
d->auxbuf = &auxbuffer[0];
|
.dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1),
|
||||||
d->raw_page_size = 0;
|
CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0),
|
||||||
d->flags = 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
|
||||||
|
/* Nobody will use this anyway if the device has no NAND flash */
|
||||||
|
{ 0 },
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t target_nand_chip_count =
|
||||||
|
sizeof(target_nand_chip_data) / sizeof(nand_chip_data);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
nand_drv.chip_data = NULL;
|
||||||
|
nand_drv.write_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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(reg & errbit)
|
||||||
|
return NAND_ERR_COMMAND;
|
||||||
|
|
||||||
|
return reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) {
|
static int nandop_read_page(uint32_t row_addr, uint8_t* buf)
|
||||||
status = NANDERR_ECC_FAILED;
|
|
||||||
goto _end;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nand_write_bytes(uint32_t byteaddr, int count, const void* 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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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__ */
|
||||||
|
|
|
||||||
|
|
@ -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))) {
|
||||||
rc = nand_read_bytes(addr, size, buffer);
|
|
||||||
nand_close();
|
nand_close();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nandwrite(uint32_t addr, uint32_t size, void* buffer)
|
rc = nand_read(addr, size, (uint8_t*)buffer);
|
||||||
|
nand_close();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue