401 lines
11 KiB
C
401 lines
11 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "page_info.h"
|
|
|
|
struct boot_info *page_info;
|
|
|
|
unsigned char page_info_get_data_lanes_mode(void)
|
|
{
|
|
return page_info->dev_cfg0.bus_width & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_cmd_lanes_mode(void)
|
|
{
|
|
return page_info->dev_cfg1.ca_lanes & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_addr_lanes_mode(void)
|
|
{
|
|
return (page_info->dev_cfg1.ca_lanes >> 4) & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_frequency_index(void)
|
|
{
|
|
return page_info->host_cfg.frequency_index;
|
|
}
|
|
|
|
unsigned char page_info_get_adj_index(void)
|
|
{
|
|
return page_info->host_cfg.mode_rx_adj & 0x3f;
|
|
}
|
|
|
|
unsigned char page_info_get_work_mode(void)
|
|
{
|
|
return (page_info->host_cfg.mode_rx_adj >> 6) & 0x3;
|
|
}
|
|
|
|
unsigned char page_info_get_line_delay1(void)
|
|
{
|
|
return page_info->host_cfg.lines_delay[0];
|
|
}
|
|
|
|
unsigned char page_info_get_line_delay2(void)
|
|
{
|
|
return page_info->host_cfg.lines_delay[1];
|
|
}
|
|
|
|
unsigned char page_info_get_core_div(void)
|
|
{
|
|
return page_info->host_cfg.core_div;
|
|
}
|
|
|
|
unsigned char page_info_get_bus_cycle(void)
|
|
{
|
|
return page_info->host_cfg.bus_cycle;
|
|
}
|
|
|
|
unsigned char page_info_get_device_ecc_disable(void)
|
|
{
|
|
return page_info->host_cfg.device_ecc_disable & 0x01;
|
|
}
|
|
|
|
unsigned int page_info_get_n2m_command(void)
|
|
{
|
|
return page_info->host_cfg.n2m_cmd;
|
|
}
|
|
|
|
unsigned int page_info_get_page_size(void)
|
|
{
|
|
return page_info->dev_cfg0.page_size;
|
|
}
|
|
|
|
unsigned char page_info_get_planes(void)
|
|
{
|
|
return page_info->dev_cfg0.planes_per_lun & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_plane_shift(void)
|
|
{
|
|
return (page_info->dev_cfg0.planes_per_lun >> 4) & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_cache_plane_shift(void)
|
|
{
|
|
return (page_info->dev_cfg0.bus_width >> 4) & 0x0f;
|
|
}
|
|
|
|
unsigned char page_info_get_cs_deselect_time(void)
|
|
{
|
|
return page_info->dev_cfg1.cs_deselect_time;
|
|
}
|
|
|
|
unsigned char page_info_get_dummy_cycles(void)
|
|
{
|
|
return page_info->dev_cfg1.dummy_cycles;
|
|
}
|
|
|
|
unsigned int page_info_get_block_size(void)
|
|
{
|
|
return page_info->dev_cfg1.block_size;
|
|
}
|
|
|
|
unsigned short *page_info_get_bbt(void)
|
|
{
|
|
return &page_info->dev_cfg1.bbt[0];
|
|
}
|
|
|
|
unsigned char page_info_get_enable_bbt(void)
|
|
{
|
|
return page_info->dev_cfg1.enable_bbt;
|
|
}
|
|
|
|
unsigned char page_info_get_high_speed_mode(void)
|
|
{
|
|
return page_info->dev_cfg1.high_speed_mode;
|
|
}
|
|
|
|
unsigned char page_info_get_layout_method(void)
|
|
{
|
|
return page_info->boot_layout.layout_method;
|
|
}
|
|
|
|
unsigned int page_info_get_boot_size(void)
|
|
{
|
|
return page_info->boot_layout.boot_size;
|
|
}
|
|
|
|
unsigned int page_info_get_pages_in_block(void)
|
|
{
|
|
unsigned int block_size, page_size;
|
|
static unsigned int pages_in_block;
|
|
|
|
if (pages_in_block)
|
|
return pages_in_block;
|
|
|
|
block_size = page_info_get_block_size();
|
|
page_size = page_info_get_page_size();
|
|
pages_in_block = block_size / page_size;
|
|
|
|
return pages_in_block;
|
|
}
|
|
|
|
unsigned int page_info_get_pages_in_boot(void)
|
|
{
|
|
unsigned int page_size, boot_size;
|
|
|
|
page_size = page_info_get_page_size();
|
|
boot_size = page_info_get_boot_size();
|
|
|
|
return boot_size / page_size;
|
|
}
|
|
|
|
void page_info_initialize(unsigned int default_n2m,
|
|
unsigned char bus_width, unsigned char ca)
|
|
{
|
|
memset((unsigned char *)page_info,
|
|
0, sizeof(struct boot_info));
|
|
page_info->dev_cfg0.page_size = sizeof(struct boot_info);
|
|
page_info->dev_cfg0.planes_per_lun = 0;
|
|
page_info->dev_cfg0.bus_width = bus_width;
|
|
page_info->host_cfg.frequency_index = 0xFF;
|
|
page_info->host_cfg.n2m_cmd = default_n2m;
|
|
page_info->dev_cfg1.ca_lanes = ca;
|
|
page_info->dev_cfg1.cs_deselect_time = 0xFF;
|
|
page_info->dev_cfg1.dummy_cycles = 0xFF;
|
|
}
|
|
|
|
int page_info_version_init(unsigned char boot_layout)
|
|
{
|
|
cpu_id_t cpu_id = get_cpu_id();
|
|
|
|
switch (cpu_id.family_id) {
|
|
case MESON_CPU_MAJOR_ID_A4:
|
|
page_info->version = PAGE_INFO_V3;
|
|
break;
|
|
case MESON_CPU_MAJOR_ID_C3:
|
|
page_info->version = PAGE_INFO_V2;
|
|
break;
|
|
case MESON_CPU_MAJOR_ID_A1:
|
|
case MESON_CPU_MAJOR_ID_A5:
|
|
case MESON_CPU_MAJOR_ID_C1:
|
|
case MESON_CPU_MAJOR_ID_C2:
|
|
case MESON_CPU_MAJOR_ID_S4:
|
|
case MESON_CPU_MAJOR_ID_S4D:
|
|
case MESON_CPU_MAJOR_ID_S5:
|
|
case MESON_CPU_MAJOR_ID_SC2:
|
|
page_info->version = PAGE_INFO_V1;
|
|
break;
|
|
default:
|
|
page_info->version = PAGE_INFO_V3;
|
|
break;
|
|
}
|
|
page_info->version |= (boot_layout << 4);
|
|
|
|
return page_info->version & 0x0f;
|
|
}
|
|
|
|
static void apply_page_info_bbt(struct mtd_info *mtd)
|
|
{
|
|
struct nand_device *nand = mtd_to_nanddev(mtd);
|
|
struct spinand_device *spinand = nand_to_spinand(nand);
|
|
unsigned short *page_info_bbt = page_info_get_bbt();
|
|
int bad_count = 0;
|
|
int i = BOOT_TOTAL_PAGES >> (mtd->erasesize_shift - mtd->writesize_shift);
|
|
|
|
for (; bad_count < MAX_F_BAD_BLOCK_NUM &&
|
|
i < mtd->size >> mtd->erasesize_shift; i++) {
|
|
if (spinand->bbt[i] == 0)
|
|
continue;
|
|
page_info_bbt[bad_count++] = i;
|
|
}
|
|
}
|
|
|
|
static unsigned int do_checksum(unsigned char *buf, int len)
|
|
{
|
|
int i, checksum = 0;
|
|
|
|
for (i = 0; i < len; i++)
|
|
checksum += buf[i];
|
|
|
|
return checksum;
|
|
}
|
|
|
|
extern unsigned char disable_host_ecc;
|
|
static void page_info_init_from_mtd_and_dts(struct mtd_info *mtd,
|
|
struct udevice *udev)
|
|
{
|
|
struct nand_device *dev = mtd_to_nanddev(mtd);
|
|
struct dm_spi_slave_platdata *plat;
|
|
unsigned char ecc_steps;
|
|
unsigned int i, check_len = sizeof(struct boot_info);
|
|
enum PAGE_INFO_V page_info_ver;
|
|
#ifdef CONFIG_DDR_PARAMETER_SUPPORT
|
|
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
|
unsigned int pages_shift, ddr_param_page;
|
|
#endif
|
|
struct storage_startup_parameter *ssp = &g_ssp;
|
|
|
|
page_info_ver = page_info_version_init(ssp->sip.nsp.boot_layout_compat);
|
|
memcpy(page_info->magic, BOOTINFO_MAGIC, strlen(BOOTINFO_MAGIC));
|
|
page_info->dev_cfg0.page_size = mtd->writesize;
|
|
page_info->dev_cfg0.planes_per_lun = dev->memorg.planes_per_lun;
|
|
if (page_info->dev_cfg0.planes_per_lun > 1) {
|
|
page_info->dev_cfg0.planes_per_lun |= 6 << 4;
|
|
page_info->dev_cfg0.bus_width =
|
|
(mtd->writesize_shift + 1) << 4;
|
|
}
|
|
plat = dev_get_parent_platdata(udev);
|
|
page_info->dev_cfg0.bus_width &= ~0x03;
|
|
if (plat->mode & SPI_RX_QUAD)
|
|
page_info->dev_cfg0.bus_width |= 2;
|
|
else if (plat->mode & SPI_RX_DUAL)
|
|
page_info->dev_cfg0.bus_width |= 1;
|
|
NFC_Print("bus_width", page_info->dev_cfg0.bus_width);
|
|
if (page_info_ver == PAGE_INFO_V1) {
|
|
/* for compatible, a1/c1/c2 ... need to know fip's start and size */
|
|
#ifdef BOOT_TOTAL_PAGES
|
|
page_info->reserved[0] = BOOT_TOTAL_PAGES / 64 + NAND_RSV_BLOCK_NUM;
|
|
#endif
|
|
#ifdef CONFIG_TPL_SIZE_PER_COPY
|
|
page_info->reserved[1] =
|
|
CONFIG_TPL_SIZE_PER_COPY / mtd->erasesize;
|
|
#endif
|
|
#ifdef CONFIG_NAND_TPL_COPY_NUM
|
|
page_info->reserved[2] = CONFIG_NAND_TPL_COPY_NUM;
|
|
#endif
|
|
page_info->dev_cfg1.block_size = mtd->erasesize;
|
|
} else if (page_info_ver == PAGE_INFO_V2) {
|
|
/* for compatible, C3 use this field */
|
|
#ifdef CONFIG_DDR_PARAMETER_SUPPORT
|
|
pages_shift = mtd->erasesize_shift - mtd->writesize_shift;
|
|
if (spinand->rsv->ddr_para->valid) {
|
|
ddr_param_page = spinand->rsv->ddr_para->nvalid->page_addr +
|
|
(spinand->rsv->ddr_para->nvalid->blk_addr << pages_shift);
|
|
page_info->reserved[0] = ddr_param_page & 0xff;
|
|
page_info->reserved[1] = ((ddr_param_page >> 8) & 0xff);
|
|
printf("save ddr param page: 0x%x to info page!\n", ddr_param_page);
|
|
} else {
|
|
printf("ddr param is invalid!\n");
|
|
}
|
|
#endif
|
|
i = mtd->erasesize_shift + mtd->writesize_shift;
|
|
page_info->reserved[2] = ((mtd->size >> i) ? (mtd->size >> i) : 1) & 0x3;
|
|
/* reserved[2] bit4: block size = 128KB
|
|
* reserved[2] bit5: block size = 256KB
|
|
* reserved[2] bit6: block size = 512KB
|
|
*/
|
|
if (mtd->erasesize == 0x20000)
|
|
page_info->reserved[2] |= (unsigned char)(1 << 4);
|
|
else if (mtd->erasesize == 0x40000)
|
|
page_info->reserved[2] |= (unsigned char)(1 << 5);
|
|
else if (mtd->erasesize == 0x80000)
|
|
page_info->reserved[2] |= (unsigned char)(1 << 6);
|
|
/* bootinfo magic + version + reserved + dev_cfg0 */
|
|
check_len = 20;
|
|
}
|
|
|
|
if (page_info_ver != PAGE_INFO_V3)
|
|
goto _do_final;
|
|
|
|
ecc_steps = mtd->writesize >> 9;
|
|
page_info->host_cfg.n2m_cmd = (DEFAULT_ECC_MODE & (~0x3F)) | ecc_steps;
|
|
if (disable_host_ecc)
|
|
page_info->host_cfg.n2m_cmd = N2M_RAW | mtd->writesize;
|
|
page_info->host_cfg.frequency_index = 0xFF;
|
|
page_info->dev_cfg1.ca_lanes = 0;
|
|
page_info->dev_cfg1.cs_deselect_time = 0xFF;
|
|
page_info->dev_cfg1.dummy_cycles = 0xFF;
|
|
page_info->dev_cfg1.block_size = mtd->erasesize;
|
|
|
|
_do_final:
|
|
if (store_boot_layout_is_discrete_bl2()) {
|
|
page_info->dev_cfg1.enable_bbt = 1;
|
|
apply_page_info_bbt(mtd);
|
|
}
|
|
|
|
page_info->checksum = 0;
|
|
page_info->checksum = do_checksum((unsigned char *)page_info, check_len);
|
|
printf("page info updated checksum : 0x%x\n", page_info->checksum);
|
|
}
|
|
|
|
#ifdef __PXP_DEBUG__
|
|
static void page_info_dump_info(void)
|
|
{
|
|
unsigned char planes_per_lun, plane_shift, bus_width, cache_plane_shift;
|
|
unsigned char high_speed_mode, cmd_lanes, addr_lanes;
|
|
unsigned char enable_bbt;
|
|
unsigned int block_size, page_size;
|
|
unsigned char frequency_index, mode, rx_adj;
|
|
unsigned char device_ecc_disable = 0;
|
|
unsigned int n2m_cmd;
|
|
|
|
planes_per_lun = page_info_get_planes();
|
|
plane_shift = page_info_get_plane_shift();
|
|
cache_plane_shift = page_info_get_cache_plane_shift();
|
|
high_speed_mode = page_info_get_high_speed_mode();
|
|
page_size = page_info_get_page_size();
|
|
block_size = page_info_get_block_size();
|
|
enable_bbt = page_info_get_enable_bbt();
|
|
bus_width = page_info_get_data_lanes_mode();
|
|
cmd_lanes = page_info_get_cmd_lanes_mode();
|
|
addr_lanes = page_info_get_addr_lanes_mode();
|
|
|
|
frequency_index = page_info_get_frequency_index();
|
|
mode = page_info_get_work_mode();
|
|
rx_adj = page_info_get_adj_index();
|
|
device_ecc_disable = page_info_get_device_ecc_disable();
|
|
n2m_cmd = page_info_get_n2m_command();
|
|
|
|
pr_info("bus_width: 0x%x\n", bus_width);
|
|
pr_info("cmd_lanes: 0x%x\n", cmd_lanes);
|
|
pr_info("addr_lanes: 0x%x\n", addr_lanes);
|
|
pr_info("page_size: 0x%x\n", page_size);
|
|
pr_info("planes_per_lun: 0x%x\n", planes_per_lun);
|
|
pr_info("plane_shift: 0x%x\n", plane_shift);
|
|
pr_info("cache_plane_shift: 0x%x\n", cache_plane_shift);
|
|
pr_info("block_size: 0x%x\n", block_size);
|
|
pr_info("high_speed_mode: 0x%x\n", high_speed_mode);
|
|
pr_info("enable_bbt: 0x%x\n", enable_bbt);
|
|
|
|
pr_info("frequency_index: 0x%x\n", frequency_index);
|
|
pr_info("mode: 0x%x\n", mode);
|
|
pr_info("rx_adj: 0x%x\n", rx_adj);
|
|
pr_info("device_ecc_disable: 0x%x\n", device_ecc_disable);
|
|
pr_info("n2m_cmd: 0x%x\n", n2m_cmd);
|
|
}
|
|
#endif
|
|
|
|
unsigned char *page_info_post_init(struct mtd_info *mtd, struct udevice *dev)
|
|
{
|
|
page_info_init_from_mtd_and_dts(mtd, dev);
|
|
#ifdef __PXP_DEBUG__
|
|
page_info_dump_info();
|
|
#endif
|
|
return (unsigned char *)page_info;
|
|
}
|
|
|
|
int page_info_pre_init(void)
|
|
{
|
|
if (!page_info) {
|
|
page_info = kzalloc(MAX_BYTES_IN_BOOTINFO, GFP_KERNEL);
|
|
if (!page_info)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool page_info_is_page(int page)
|
|
{
|
|
enum PAGE_INFO_V page_info_ver;
|
|
|
|
page_info_ver = page_info->version & 0x0F;
|
|
if (page_info_ver == PAGE_INFO_V1)
|
|
return page % 128 == BL2_SIZE / 2048 && page < 1024;
|
|
|
|
return (!(page % 128) && (page < 1024));
|
|
}
|