Files
luban-lite/bsp/examples_bare/test-spinand/spinand_bbt_example.c
刘可亮 3e10f578d3 v1.2.2
2025-10-21 13:59:50 +08:00

361 lines
11 KiB
C

/*
* Copyright (c) 2025, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: Cui Jiawei <jiawei.cui@artinchip.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <console.h>
#include <aic_common.h>
#include <aic_core.h>
#include <aic_errno.h>
#include <aic_utils.h>
#include <spinand_port.h>
#include <spinand_bbt.h>
#define SPI_BUS_ID 0
#define SPINAND_BLOCK_SIZE 0x20000
#define SPINAND_PAGE_SIZE 0x800
/* Reserved blocks should account for 10%~20% of the bad blocks managed by the bad block table. */
#define BBT_BLOCK_NUM 0x005
/*
* It is best for the scope of bad block table management to lie within the data partition,
* so as not to compromise the system firmware.
*/
#define BBT_OFFSET 0x3A00000 /* start offset of spinand which data will be operation */
#define BBT_SIZE 0x600000 /* length of spinand which data will operation */
/* Attention:The range of read/write erasure should be within the bbt table. */
#define TEST_READ_WRITE_SIZE 0x8000
#define TEST_OFFSET 0x3A00000
static struct aic_spinand_bbt *s_spinand_bbt_t = NULL;
static u8 *s_write_buf = NULL;
static u8 *s_read_buf = NULL;
static void address_map(u8 *data, u32 start_address, u32 len)
{
u32 i;
for (i = 0; i < len; i++) {
if (i && (i % 16) == 0)
printf("\n");
if ((i % 16) == 0) {
printf("0x%08x : ", start_address);
start_address += 16;
}
printf("%02x ", data[i]);
}
printf("\n");
}
static int verify_data(u8 *data_1, u8 *data_2, u32 offset, u32 len)
{
if (memcmp(data_1, data_2, len) != 0) {
printf("expected:-----------------------\n");
address_map(data_1, offset, len);
printf("actual:-------------------------\n");
address_map(data_2, offset, len);
return -1;
} else {
if (len > 0x200) {
address_map(data_2, offset, 0x100);
printf("......\n");
address_map(data_2 + len - 0x50, offset + len - 0x50, 0x50);
} else {
address_map(data_2, offset, len);
}
return 0;
}
}
static int data_init(u32 malloc_len)
{
/* Allocate test data buffer */
s_write_buf = aicos_malloc_align(0, malloc_len, CACHE_LINE_SIZE);
if (!s_write_buf) {
printf("Error: No memory for test data!\n");
return -1;
}
/* Allocate read buffer */
s_read_buf = aicos_malloc_align(0, malloc_len, CACHE_LINE_SIZE);
if (!s_read_buf) {
printf("Error: No memory for read buffer!\n");
aicos_free_align(0, s_write_buf);
return -1;
}
/* prepare some data */
for (int i = 0; i < malloc_len; i++) {
s_write_buf[i] = i % 256;
}
return 0;
}
static void check_adjust(u32 *offset, u32 *len, u32 *erase_len)
{
if (*len % SPINAND_BLOCK_SIZE != 0) {
printf("Warning: len is not aligned with erase size! "
"Adjusting to a larger erase block.\n");
*erase_len = (*len / SPINAND_BLOCK_SIZE + 1) * SPINAND_BLOCK_SIZE;
}
if (*len % SPINAND_PAGE_SIZE != 0) {
printf("Warning: len is not aligned with write size! "
"Adjusting read and write len to a smaller write page.\n");
*len = (*len / SPINAND_PAGE_SIZE) * SPINAND_PAGE_SIZE;
}
if (*offset % SPINAND_BLOCK_SIZE != 0) {
printf("Warning: Offset is not aligned with erase size! "
"Adjusting to the smaller erase block boundary.\n");
*offset = (*offset / SPINAND_BLOCK_SIZE) * SPINAND_BLOCK_SIZE;
}
}
/**
* @brief Initialize SPI NAND device and build Bad Block Table (BBT)
*
* @param[in] spi_bus SPI bus number to use
*
* @return Returns 0 on success, -1 on failure
*/
static int spinand_init(int spi_bus)
{
s_spinand_bbt_t = malloc(sizeof(struct aic_spinand_bbt));
if (!s_spinand_bbt_t) {
printf("malloc failed\n");
return -1;
}
s_spinand_bbt_t->spinand_flash = spinand_probe(spi_bus);
if (s_spinand_bbt_t->spinand_flash == NULL) {
printf("Failed to probe spinand flash.\n");
return -1;
}
return spinand_bbt_init(s_spinand_bbt_t, BBT_BLOCK_NUM, BBT_OFFSET, BBT_SIZE);
}
/**
* @brief Performs a full cycle of operations on a SPINAND flash memory region:
* 1. Checks and adjusts alignment of length and offset.
* 2. Erases the target blocks.
* 3. Writes test data to the pages within those blocks.
* 4. Reads back the data.
* 5. Verifies the integrity of the written data.
*
* @param[in] offset The starting address in flash memory where the operation begins.
* Will be adjusted to align with a block boundary if necessary.
* @param[in] len The total number of bytes to process (erase/write/read).
* Both 'len' and 'offset' may get modified by this function for alignment.
*
* @return An integer error code (0 on success, non-zero otherwise).
*/
static int spinand_write_read(u32 offset, u32 len)
{
int err = 0;
u32 erase_len = len;
printf("\n === SPINAND bbt write read example ===\n");
/* Check whether the address range and offset are correct */
check_adjust(&offset, &len, &erase_len);
/*
* Erase the specified region of the SPINAND device.
* Note: The erase operation can only be performed in blocks,
* so ensure that the offset and len parameters are integer multiples of the block size.
*/
err = spinand_bbt_erase(s_spinand_bbt_t, offset, erase_len);
if (err) {
printf("Error: Erase failed (code=%d)!\n", err);
goto exit;
}
/*
* Write test data,it must be erased first.
* Note: The write operation can only be performed in pages,
* so ensure that the offset and len parameters are integer multiples of the page size.
*/
err = spinand_bbt_write(s_spinand_bbt_t, s_write_buf, offset, len);
if (err) {
printf("Error: Write failed (code=%d)!\n", err);
goto exit;
}
memset(s_read_buf, 0, len);
/* Read the specified region of the SPINAND device. */
err = spinand_bbt_read(s_spinand_bbt_t, s_read_buf, offset, len);
if (err) {
printf("Error: Read failed (code=%d)!\n", err);
goto exit;
}
/* Data verification */
if (verify_data(s_read_buf, s_write_buf, offset, len))
printf("Error: Data verification failed!\n");
else
printf("SUCCESS: SPINAND bbt read/write test passed!\n");
exit:
return err;
}
/**
* @brief Perform refresh operation on SPI NAND flash memory
* Writes provided data buffer to specified memory region of the device.
*
* @param[in] offset Starting address in bytes from memory origin
* @param[in] data Pointer to source data buffer containing update payload
* @param[in] data_len Length of data to write (number of bytes)
*
* @return An integer error code (0 on success, non-zero otherwise).
*/
static int spinand_refresh(u32 offset, u8 *data, u32 data_len)
{
u32 len, total_len;
u32 start_address, offset_in_block;
int err;
printf("\n === SPINAND bbt refresh example ===\n");
/* Check whether the offset are correct. */
if (offset % SPINAND_BLOCK_SIZE != 0) {
printf("Warning: Offset is not aligned with erase size! "
"Adjusting to the smaller erase block boundary.\n");
start_address = (offset / SPINAND_BLOCK_SIZE) * SPINAND_BLOCK_SIZE;
} else {
start_address = offset;
}
/* Calculate the offset within the block and the actual length of the update. */
offset_in_block = offset % SPINAND_BLOCK_SIZE;
total_len = offset_in_block + data_len;
if (total_len % SPINAND_BLOCK_SIZE != 0) {
len = (total_len / SPINAND_BLOCK_SIZE + 1) * SPINAND_BLOCK_SIZE;
} else {
len = total_len;
}
/* Allocate buffer */
u8 *buffer = aicos_malloc_align(0, len, CACHE_LINE_SIZE);
if (!buffer) {
printf("Error: No memory for buffer!\n");
return -1;
}
/*
* Read the specified region of the SPINAND device.
*/
err = spinand_bbt_read(s_spinand_bbt_t, buffer, start_address, len);
if (err) {
printf("Error: Read failed (code=%d)!\n", err);
goto exit;
}
/* Replace data */
memcpy(buffer + offset_in_block, data, data_len);
/*
* Erase the specified region of the SPINAND device.
* Note: The erase operation can only be performed in blocks,
* so ensure that the offset and len parameters are integer multiples of the block size.
*/
err = spinand_bbt_erase(s_spinand_bbt_t, start_address, len);
if (err) {
printf("Error: Erase failed (code=%d)!\n", err);
goto exit;
}
/*
* Write test data,it must be erased first.
* Note: The write operation can only be performed in pages,
* so ensure that the offset and len parameters are integer multiples of the page size.
*/
err = spinand_bbt_write(s_spinand_bbt_t, buffer, start_address, len);
if (err) {
printf("Error: Write failed (code=%d)!\n", err);
goto exit;
}
printf("The position of the data in the block:\n");
memset(buffer, 0, len);
err = spinand_bbt_read(s_spinand_bbt_t, buffer, start_address, len);
if (err) {
printf("Error: Read failed (code=%d)!\n", err);
goto exit;
}
address_map(buffer + offset_in_block - 16, start_address + offset_in_block - 16, data_len + 32);
printf("SUCCESS: SPINAND bbt refresh data test passed!\n");
exit:
if (buffer)
aicos_free_align(0, buffer);
return err;
}
static int cmd_spinand_example(int argc, char *argv[])
{
int err = 0;
/* Allocate memory for the test array and prepare test data. */
err = data_init(SPINAND_BLOCK_SIZE);
if (err) {
pr_err("data init failed!\n");
goto exit;
}
/* Initialize the bad block table */
err = spinand_init(SPI_BUS_ID);
if (err) {
pr_err("SPINAND init failed!\n");
goto exit;
}
/* Erase the data, rewrite it, and then read back to verify data consistency. */
err = spinand_write_read(TEST_OFFSET, SPINAND_BLOCK_SIZE);
if (err) {
pr_err("SPINAND write read failed!\n");
goto exit;
}
/* Update the data in the specified area. */
u8 data[10] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 };
err = spinand_refresh(TEST_OFFSET, data, 10);
if (err)
pr_err("SPINAND refresh failed!\n");
exit:
printf("spinand bbt usage end\n");
/* Free resources */
if (s_spinand_bbt_t) {
spinand_bbt_deinit(s_spinand_bbt_t);
free(s_spinand_bbt_t);
s_spinand_bbt_t = NULL;
}
if (s_write_buf)
aicos_free_align(0, s_write_buf);
if (s_read_buf)
aicos_free_align(0, s_read_buf);
return err;
}
CONSOLE_CMD(spinand_bbt_example, cmd_spinand_example, "spinand bbt example");