rockbox/firmware/target/mips/ingenic_x1000/nand-x1000.h
amachronic 28c89386af x1000: Improve NAND driver API
- Proper error codes are now returned from all functions. These codes will
  be used by a host-side flash tool for error reporting.

- nand_erase_block() was replaced by nand_erase_bytes(). The caller can't
  know how big an eraseblock is with the current API, so next best thing
  is to verify the correct alignment inside the call and reject the erase
  if it isn't properly aligned.

- Fixed typo in nandcmd_block_erase() which would cause an SFC error to be
  interpreted as success. Yikes.

Change-Id: Id4ac9b44fa7fc2fcb81ff19ba730df78457c0383
2021-04-06 17:27:12 +01:00

214 lines
8.5 KiB
C

/***************************************************************************
* __________ __ ___.
* 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_X1000_H__
#define __NAND_X1000_H__
/* NOTE: this is a very minimal API designed only to support a bootloader.
* Not suitable for general data storage. It doesn't have proper support for
* partial page writes, access to spare area, etc, which are all necessary
* for an effective flash translation layer.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "nand-x1000-err.h"
/* Chip supports quad I/O for page read/write */
#define NANDCHIP_FLAG_QUAD 0x01
/* Set/clear the BRWD bit when enabling/disabling write protection */
#define NANDCHIP_FLAG_USE_BRWD 0x02
typedef struct nand_drv nand_drv;
/* Defines some static information about a NAND chip */
typedef struct nand_chip_data {
const char* name; /* Name for debugging purposes */
uint8_t mf_id; /* Manufacturer ID */
uint8_t dev_id; /* Device ID */
uint8_t rowaddr_width; /* Number of bytes in row addresses */
uint8_t coladdr_width; /* Number of bytes in column addresses */
uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */
uint32_t clock_freq; /* Frequency to switch to after identification */
uint32_t block_size; /* Number of pages per eraseblock */
uint32_t page_size; /* Number of data bytes per page */
uint32_t spare_size; /* Number of spare bytes per page */
int flags; /* Various flags */
} nand_chip_data;
/* Defines high-level operations used to implement the public API.
* Chips may need to override operations if the default ones aren't suitable.
*
* Negative return codes return an error, while zero or positive codes are
* considered successful. This allows a function to return meaningful data,
* if applicable.
*/
typedef struct nand_chip_ops {
/* Called once after identifying the chip */
int(*open)(nand_drv* d);
/* Called once when the driver is closed */
void(*close)(nand_drv* d);
/* Read or write a complete page including both main and spare areas. */
int(*read_page)(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
int(*write_page)(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
/* Erase a block. */
int(*erase_block)(nand_drv* d, uint32_t blockaddr);
/* Enable or disable the chip's write protection. */
int(*set_wp_enable)(nand_drv* d, bool en);
/* Perform error correction and detection on a raw page (main + spare).
* Return the number of errors detected and corrected, or a negative value
* if errors were detected but could not be corrected.
*/
int(*ecc_read)(nand_drv* d, unsigned char* buf);
/* Generate ECC data for a page. The buffer main area is already filled
* and this function should write ECC data into the spare area.
*/
void(*ecc_write)(nand_drv* d, unsigned char* buf);
} nand_chip_ops;
/* Struct used to list all supported NAND chips in an array */
typedef struct nand_chip_desc {
const nand_chip_data* data;
const nand_chip_ops* ops;
} nand_chip_desc;
/* NAND driver structure. It can be accessed by chip ops, but they must not
* modify any fields except for "auxbuf", which is a small buffer that can
* be used for commands that need to read/write small amounts of data: often
* needed for polling status, etc.
*/
struct nand_drv {
const nand_chip_ops* chip_ops;
const nand_chip_data* chip_data;
unsigned char* pagebuf;
unsigned char* auxbuf;
uint32_t raw_page_size;
int flags;
};
/* Note: sfc_init() must be called prior to nand_open() */
extern int nand_open(void);
extern void nand_close(void);
/* Controls device-side write protection registers as well as software lock.
* Erase and program operations will fail unless you first enable writes.
*/
extern int nand_enable_writes(bool en);
/* Byte-based NAND operations */
extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf);
extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf);
extern int nand_erase_bytes(uint32_t byteaddr, int count);
/* NAND command numbers */
#define NAND_CMD_READ_ID 0x9f
#define NAND_CMD_WRITE_ENABLE 0x06
#define NAND_CMD_GET_FEATURE 0x0f
#define NAND_CMD_SET_FEATURE 0x1f
#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
#define NAND_CMD_READ_FROM_CACHE 0x0b
#define NAND_CMD_READ_FROM_CACHEx4 0x6b
#define NAND_CMD_PROGRAM_LOAD 0x02
#define NAND_CMD_PROGRAM_LOADx4 0x32
#define NAND_CMD_PROGRAM_EXECUTE 0x10
#define NAND_CMD_BLOCK_ERASE 0xd8
/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
#define NAND_FREG_PROTECTION 0xa0
#define NAND_FREG_FEATURE 0xb0
#define NAND_FREG_STATUS 0xc0
/* Protection register bits */
#define NAND_FREG_PROTECTION_BRWD 0x80
#define NAND_FREG_PROTECTION_BP2 0x20
#define NAND_FREG_PROTECTION_BP1 0x10
#define NAND_FREG_PROTECTION_BP0 0x80
/* Mask of BP bits 0-2 */
#define NAND_FREG_PROTECTION_ALLBP (0x38)
/* Feature register bits */
#define NAND_FREG_FEATURE_QE 0x01
/* Status register bits */
#define NAND_FREG_STATUS_OIP 0x01
#define NAND_FREG_STATUS_WEL 0x02
#define NAND_FREG_STATUS_E_FAIL 0x04
#define NAND_FREG_STATUS_P_FAIL 0x08
/* Standard implementations for low-level NAND commands. I'm not aware of any
* actual standard governing these, but it seems many vendors follow the same
* command numbering, status bits, and behavior so these implementations should
* work across a wide variety of chips.
*
* If adding a new NAND chip which only has a minor deviation from these
* standard implementations, consider adding a flag and modifying these
* functions to change their behavior based on the flag, instead of writing
* a whole new implementation.
*
* None of these functions are directly called by the high-level driver code,
* except for nandcmd_std_read_id(). They can be used to implement higher-level
* functions in a device's "nand_chip_ops".
*/
extern int nandcmd_read_id(nand_drv* d);
extern int nandcmd_write_enable(nand_drv* d);
extern int nandcmd_get_feature(nand_drv* d, int reg);
extern int nandcmd_set_feature(nand_drv* d, int reg, int val);
extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr);
extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf);
extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf);
extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr);
extern int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr);
/* Table filled with all the standard operations for chips which don't
* need to override any operations.
*/
extern const nand_chip_ops nand_chip_ops_std;
/* Standard NAND chip ops based on the standard "nandcmd" functions.
*
* Same advice applies here as there: if it's possible to support minor
* chip variations with a flag, modify these functions to do so.
*/
extern int nandop_std_open(nand_drv* d);
extern void nandop_std_close(nand_drv* d);
extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr);
extern int nandop_std_set_wp_enable(nand_drv* d, bool en);
/* The default ECC implementation is a no-op: reads always succeed without
* reporting errors and writes will fill the spare area with '0xff' bytes.
*
* For chips that support internal ECC, this often works because the chip will
* ignore writes to the ECC areas.
*/
extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf);
extern void nandop_ecc_none_write(nand_drv* d, unsigned char* buf);
#endif /* __NAND_X1000_H__ */