linuxOS_D21X/source/uboot-2021.10/common/spl/spl_spi_nand_tiny.c
2025-08-14 15:17:16 +08:00

361 lines
8.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2011 OMICRON electronics GmbH
*
* based on drivers/mtd/nand/raw/nand_spl_load.c
*
* Copyright (C) 2011
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Copyright (c) 2022, ArtInChip Technology Co., Ltd
* Author: Hao Xiong <hao.xiong@artinchip.com>
*/
#include <common.h>
#include <config.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <image.h>
#include <spi.h>
#include <mtd.h>
#include <spi_flash.h>
#include <errno.h>
#include <spl.h>
#include <nand.h>
#include <ubispl.h>
#include <spl.h>
#include <asm/arch/boot_param.h>
#include <artinchip_spinand.h>
#include <linux/mtd/spinand.h>
#include <cpu_func.h>
#include <dm/uclass-internal.h>
#include <artinchip_ve.h>
#include <artinchip/artinchip_fb.h>
#ifdef CONFIG_AUTO_CALCULATE_PART_CONFIG
#include <generated/image_cfg_part_config.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
struct spinand_device *spl_spinand_init(void)
{
struct spinand_device *spinand;
struct udevice *dev;
int err = -1;
spinand = get_spinand();
if (spinand)
return spinand;
err = uclass_first_device(UCLASS_MTD, &dev);
if (err && !dev) {
pr_err("Find MTD device failed.\n");
return NULL;
}
device_probe(dev);
spinand = get_spinand();
return spinand;
}
static bool aligned_with_block_size(struct nand_device *nand, u64 size)
{
return !do_div(size, nand->info->erasesize);
}
int spl_spi_nand_read(struct spinand_device *spinand, size_t from, size_t len,
size_t *retlen, u_char *buf)
{
size_t remaining, off, rdlen;
int ret = 0;
struct nand_device *nand = spinand_to_nand(spinand);
/* Search for the first good block after the given offset */
off = from;
remaining = len;
while (spinand_block_isbad(spinand, off))
off += nand->info->erasesize;
while (remaining) {
/*
* aligned with block size and skip bad block
*/
if (aligned_with_block_size(nand, off) &&
spinand_block_isbad(spinand, off)) {
off += nand->info->erasesize;
continue;
}
/* Read one block per loop, so that it can skip bad blocks */
if (remaining > nand->info->erasesize)
rdlen = nand->info->erasesize;
else
rdlen = remaining;
/* Use continuous mode read all data at once */
#ifdef CONFIG_SPI_NAND_WINBOND_CONT_READ
rdlen = remaining;
#endif
spinand_read(spinand, off, rdlen, &rdlen, buf);
if (ret) {
pr_err("Failure while reading at offset 0x%lx\n",
(unsigned long)off);
break;
}
off += rdlen;
remaining -= rdlen;
buf += rdlen;
}
*retlen = len - remaining;
return ret;
}
static ulong spl_spi_nand_fit_read(struct spl_load_info *load, ulong offs,
ulong size, void *dst)
{
struct spinand_device *spinand;
size_t retlen;
int ret;
spinand = (struct spinand_device *)load->dev;
ret = spl_spi_nand_read(spinand, offs, size, &retlen, (u_char *)dst);
if (!ret)
return (ulong)retlen;
else
return 0;
}
static __maybe_unused
int spi_nand_load_image(struct spl_image_info *spl_image,
struct spinand_device *spinand, ulong offset)
{
int *src __attribute__((unused));
int *dst __attribute__((unused));
struct image_header *header;
size_t retlen;
int err = -1;
header = spl_get_load_buffer(-sizeof(*header), sizeof(*header));
err = spl_spi_nand_read(spinand, offset, sizeof(*header), &retlen,
(u_char *)header);
if (err) {
debug("Read image header failed.\n");
return err;
}
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
struct spl_load_info load;
debug("Found FIT\n");
load.dev = (void *)spinand;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
load.read = spl_spi_nand_fit_read;
return spl_load_simple_fit(spl_image, &load, offset, header);
} else {
err = spl_parse_image_header(spl_image, header);
if (err) {
debug("spl_parse_image_header failed.\n");
return err;
}
err = spl_spi_nand_read(spinand, offset, spl_image->size, &retlen,
(u_char *)spl_image->load_addr);
return err;
}
return 0;
}
#ifdef CONFIG_SPL_OS_BOOT
#ifdef CONFIG_VIDEO_ARTINCHIP
#define LOGO_OFFSET (0x340000)
#define IMAGE_HEADER_SIZE (4 << 10)
static int spi_nand_load_logo(struct spinand_device *spinand,
unsigned char **buf, size_t *len)
{
unsigned char *dst = NULL, *fit = NULL;
unsigned int header_len, first_block, other_block;
const struct fdt_property *fdt_prop;
int i, offset, next_offset;
int tag = FDT_PROP;
fdt32_t image_len;
size_t retlen;
int ret = 0;
fit = memalign(DECODE_ALIGN, IMAGE_HEADER_SIZE);
if (!fit) {
pr_err("Failed to malloc for logo header!\n");
ret = -ENOMEM;
goto out;
}
ret = spl_spi_nand_read(spinand, LOGO_OFFSET, IMAGE_HEADER_SIZE,
&retlen, fit);
if (ret) {
pr_err("Failed to read logo header\n");
goto out;
}
offset = fit_image_get_node(fit, "boot");
if (offset < 0) {
pr_err("Failed to find boot node in logo itb\n");
goto out;
}
/* Find data property */
for (i = 0; i < 4; i++) {
tag = fdt_next_tag(fit, offset, &next_offset);
if (tag == FDT_END)
goto out;
offset = next_offset;
}
fdt_prop = fdt_get_property_by_offset(fit, offset, NULL);
image_len = fdt32_to_cpu(fdt_prop->len);
header_len = (phys_addr_t)fdt_prop - (phys_addr_t)fit
+ sizeof(struct fdt_property);
dst = memalign(DECODE_ALIGN, ALIGN(image_len, IMAGE_HEADER_SIZE));
if (!dst) {
pr_err("Failed to malloc for boot logo image\n");
ret = -ENOMEM;
goto out;
}
if (image_len < IMAGE_HEADER_SIZE) {
first_block = image_len;
other_block = 0;
} else {
first_block = IMAGE_HEADER_SIZE - header_len;
other_block = ALIGN(image_len - first_block, IMAGE_HEADER_SIZE);
}
memcpy(dst, fit + header_len, first_block);
if (other_block) {
ret = spl_spi_nand_read(spinand, LOGO_OFFSET + IMAGE_HEADER_SIZE,
other_block, &retlen, dst + first_block);
if (ret) {
pr_err("Failed to read boot logo other block\n");
goto out;
}
}
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + image_len);
*buf = dst;
*len = image_len;
return 0;
out:
if (dst)
free(dst);
if (fit)
free(fit);
return ret;
}
static int spi_nand_show_logo(struct spinand_device *spinand)
{
struct udevice *dev;
unsigned char *dst = NULL;
size_t len = 0;
int ret;
ret = uclass_first_device(UCLASS_VIDEO, &dev);
if (ret) {
pr_err("Failed to find display udevice\n");
return ret;
}
ret = spi_nand_load_logo(spinand, &dst, &len);
if (ret)
goto out;
if (dst[1] == 'P' || dst[2] == 'N' || dst[3] == 'G')
aic_png_decode(dst, len);
else if (dst[0] == 0xff || dst[1] == 0xd8)
aic_jpeg_decode(dst, len);
else
pr_err("Invaild logo file format, need a png/jpeg image\n");
out:
aicfb_update_ui_layer(dev);
aicfb_startup_panel(dev);
return ret;
}
#endif
static int spi_nand_load_image_os(struct spl_image_info *spl_image,
struct spinand_device *spinand, ulong offset)
{
size_t retlen;
int err = -1;
/* load dtb from falcon partition in falcon mode */
spl_spi_nand_read(spinand, CONFIG_SYS_SPL_NAND_OFS,
CONFIG_SYS_SPL_WRITE_SIZE, &retlen, (void *)
CONFIG_SYS_SPL_ARGS_ADDR);
err = spi_nand_load_image(spl_image, spinand, offset);
if (err)
return err;
if (spl_image->os != IH_OS_LINUX && spl_image->os != IH_OS_TEE &&
spl_image->os != IH_OS_OPENSBI) {
puts("Expected image is not found. Trying to start U-boot\n");
return -ENOENT;
}
return 0;
}
#endif
static int spl_spi_nand_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
ulong offset = 0;
int *src __attribute__((unused));
int *dst __attribute__((unused));
struct spinand_device *spinand;
spinand = spl_spinand_init();
if (IS_ERR_OR_NULL(spinand)) {
pr_err("Tiny SPI NAND init failed., ret = %ld\n", PTR_ERR(spinand));
return -1;
}
#ifdef CONFIG_NAND_BBT_MANAGE
spl_nand_bbt_init(spinand);
#endif
#ifdef CONFIG_SPL_OS_BOOT
int ret = 0;
if (!spl_start_uboot()) {
/* load kernel addr in falcon mode */
offset = CONFIG_SYS_NAND_SPL_KERNEL_OFFS;
ret = spi_nand_load_image_os(spl_image, spinand, offset);
/* Double check after linux image is loaded. */
if (!ret && !spl_start_uboot()) {
#ifdef CONFIG_VIDEO_ARTINCHIP
spi_nand_show_logo(spinand);
#endif
return 0;
}
}
#endif
offset = CONFIG_SYS_SPI_NAND_U_BOOT_OFFS;
return spi_nand_load_image(spl_image, spinand, offset);
}
SPL_LOAD_IMAGE_METHOD("SPINAND", 0, BOOT_DEVICE_SPI, spl_spi_nand_load_image);