Skip to main content

nor_flash脱机烧录多型号兼容

一 概述

不同的nor flash指令有差异,目前是通过烧录工具识别到nor flash id再把对应的参数信息写入flash,flash参数匹配是在烧录工具完成. 如果需要脱机烧录,需使用烧录工具合并镜像功能,把flash参数信息和分区信息打包起来,或者直接修改代码将flash参数信息和分区信息写入uboot, 但是只支持指定一颗flash型号, 所以默认不支持一个固件兼容多款nor flash.我们通过把需要兼容的nor flash信息烧录到flash里面,上电后通过读取nor flash信息再对比nor flash id实现动态匹配参数.

二 配置

驱动程序会默认使用指定的nor flash信息,如果id匹配失败就会重新匹配扩展的nor flash信息.

rtos配置

1

在0x5c00的位置写入额外的flash信息, 可修改位置

uboot配置

以ad100为例修改 include/configs/ad100_base.h 文件添加

#define CONFIG_SPL_EXTRA_NOR_INFO_ENABLE
#define CONFIG_SPL_EXTRA_NOR_INFO_OFF 23552 //根据实际偏移配置添加,对应rtos的配置

kernel配置

2

根据实际偏移配置添加,对应rtos的配置

保存配置后, 重新编译rtos及linux

三 打包flash参数信息和分区信息

有两种方式打包:

四 生成额外兼容的nor flash信息

上面的默认方式只支持的一款nor flash参数,如果需要多款nor flash兼容, 需要生成兼容信息烧录到flash.

nor flash 信息用struct spi_nor_info结构体存储,可以通过烧录工具导出flash信息,添加代码的nor_info_array结构体,或者根据nor 芯片手册整理出结构体信息.

以新增GD25Q256DYIG为例:

3

导出会在烧录工具目录下生成norinfo.json文件,里面就包含nor flash结构体信息和分区信息.

以下是生成nor flash动态匹配信息的代码,需要手动修改nor_info_array数组, 添加需要适配的flash信息.

/* write_norinfo.c */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

struct spi_nor_cmd_info {
unsigned short cmd;
unsigned char dummy_byte;
unsigned char addr_nbyte;
unsigned char transfer_mode;
};

struct spi_nor_st_info {
unsigned short cmd;
unsigned char bit_shift;
unsigned char mask;
unsigned char val;
unsigned char len; /* length of byte to operate from register */
unsigned char dummy;
};

struct spi_nor_info {
unsigned char name[32];
unsigned int id;

struct spi_nor_cmd_info read_standard;
struct spi_nor_cmd_info read_quad;

struct spi_nor_cmd_info write_standard;
struct spi_nor_cmd_info write_quad;

struct spi_nor_cmd_info sector_erase;

struct spi_nor_cmd_info wr_en;
struct spi_nor_cmd_info en4byte;
struct spi_nor_st_info quad_set;
struct spi_nor_st_info quad_get;
struct spi_nor_st_info busy;

unsigned short quad_ops_mode;
unsigned short addr_ops_mode;

unsigned int tCHSH; /* hold */
unsigned int tSLCH; /* setup */
unsigned int tSHSL_RD; /* interval */
unsigned int tSHSL_WR;

unsigned int chip_size;
unsigned int page_size;
unsigned int erase_size;

unsigned char chip_erase_cmd;
};

struct mini_spi_nor_info {
unsigned char name[32];
unsigned int id;

struct spi_nor_cmd_info read_standard;
struct spi_nor_cmd_info read_quad;
struct spi_nor_cmd_info wr_en;
struct spi_nor_cmd_info en4byte;

struct spi_nor_st_info quad_set;
struct spi_nor_st_info quad_get;
struct spi_nor_st_info busy;

unsigned short quad_ops_mode;
unsigned short addr_ops_mode;

unsigned int chip_size;
unsigned int page_size;
unsigned int erase_size;

};

struct spi_nor_info nor_info_array[] =
{
{
.name = "GD25Q256DYIG",
.id = 0xc84019,
.read_standard ={.cmd=0x3,.dummy_byte=0x0,.addr_nbyte=0x4,.transfer_mode=0x0},
.read_quad ={.cmd=0x6b,.dummy_byte=0x8,.addr_nbyte=0x4,.transfer_mode=0x5},
.write_standard={.cmd=0x2,.dummy_byte=0x0,.addr_nbyte=0x4,.transfer_mode=0x0},
.write_quad ={.cmd=0x32,.dummy_byte=0x0,.addr_nbyte=0x4,.transfer_mode=0x5},
.sector_erase ={.cmd=0x52,.dummy_byte=0x0,.addr_nbyte=0x4,.transfer_mode=0x0},
.wr_en ={.cmd=0x6,.dummy_byte=0x0,.addr_nbyte=0x0,.transfer_mode=0x0},
.en4byte ={.cmd=0xb7,.dummy_byte=0x0,.addr_nbyte=0x0,.transfer_mode=0x0},
.quad_set={.cmd=0x31,.bit_shift=0x1,.mask=0x1,.val=0x1,.len=0x1,.dummy=0x0},
.quad_get={.cmd=0x35,.bit_shift=0x1,.mask=0x1,.val=0x1,.len=0x1,.dummy=0x0},
.busy ={.cmd=0x5,.bit_shift=0x0,.mask=0x1,.val=0x0,.len=0x1,.dummy=0x0},
.quad_ops_mode=1,
.addr_ops_mode=0,
.tCHSH=5,
.tSLCH=8,
.tSHSL_RD=20,
.tSHSL_WR=20,
.chip_size=33554432,
.page_size=256,
.erase_size=32768,
.chip_erase_cmd=0x60,
},
};

struct spi_nor_info_tag {
char tag[8];
int array_size;
};

int main(int argc, char *argv[])
{
FILE *file = stdout;
int ret = 0;

if (argc > 2) {
fprintf(stderr, "%s: error: too many args!\n"
"usage: %s [output_file]\n"
" if output_file not set, write to stdout\n",
argv[0], argv[0]);
return -1;
}

if (argv[1]) {
file = fopen(argv[1], "w");
if (!file) {
fprintf(stderr, "%s: failed to open file: %s [%s]\n",
argv[0], strerror(errno), argv[1]);
return -1;
}
}

struct spi_nor_info_tag nor_tag = {
.tag = "nor_tag",
.array_size = sizeof(nor_info_array)/sizeof(nor_info_array[0]),
};

ret = fwrite(&nor_tag, 1, sizeof(nor_tag), file);
if (ret != sizeof(nor_tag)) {
fprintf(stderr, "%s: failed to write nor tag: %s [%s]\n",
argv[0], strerror(errno), argv[1]);
ret = -1;
goto close_file;
}

ret = fwrite(nor_info_array, 1, sizeof(nor_info_array), file);
if (ret != sizeof(nor_info_array)) {
fprintf(stderr, "%s: failed to write nor info: %s [%s]\n",
argv[0], strerror(errno), argv[1]);
ret = -1;
goto close_file;
}

ret = 0;

close_file:
if (file != stdout)
fclose(file);

return ret;
}
gcc write_norinfo.c -o write_norinfo  #编译代码
./write_norinfo norinfo.bin #生成nor info信息

五 烧录额外兼容的nor flash信息

烧录额外的兼容的nor flash一般有三种方式.

1. 烧录到spl固件里面的后1K的位置,需要注意SPL固件是否使用到这个位置.

#/bin/sh

#dd_to_file.sh

# 获得字符串中的第几个word
get_word()
{
local str="$1"
local n=$2
local i=0

for word in $str
do
if [ $i = $n ]; then
echo $word
return 0
fi
let i=i+1
done

return 1
}

# 获得文件大小
size_file()
{
local file=$1
local result

result="`ls -l -n -L $file`"
if [ "$?" != "0" ]; then
echo "ls failed: $file" 1>&2
return 1
fi

result=`get_word "$result" 4`
if [ "result" = "" ]; then
echo "ls failed 2: $file" 1>&2
return 1
fi
echo $result
}

infile=$1
outfile=$2
offset=$3

if [ "$3" == "" ] || [ "$4" != "" ]; then
echo "error: too few or too many args" 1>&2
echo "usage: $0 infile outfile offset" 1>&2
exit 1
fi

if [ ! -e $infile ];then
echo "error: $infile not exist" 1>&2
exit 1
fi
if [ ! -e $outfile ];then
touch $outfile
if [ ! -e $outfile ]; then
echo "error: can't create outfile $outfile" 1>&2
exit 1
fi
fi

infile_size=`size_file "$infile"`
outfile_size=`size_file "$outfile"`
tmp_file=`basename $outfile`

if [ $offset -gt $outfile_size ]; then
echo "error: $offset is greater than outfile size" 1>&2
exit 1
fi

dd if=$outfile of=/tmp/$tmp_file.file0 bs=1 count=$offset status=none
if [ $? != 0 ]; then
echo "error failed to create file0: /tmp/$tmp_file.file0" 1>&2
exit 1
fi

let skip_size=$offset+$infile_size
if [ $skip_size -gt $outfile_size ]; then
let skip_size=$outfile_size
fi
let file2_size=$outfile_size-$skip_size

dd if=$outfile of=/tmp/$tmp_file.file2 skip=$skip_size bs=1 count=$file2_size status=none
if [ $? != 0 ]; then
echo "error failed to create file0: /tmp/$tmp_file.file2" 1>&2
exit 1
fi

cat /tmp/$tmp_file.file0 $infile /tmp/$tmp_file.file2 > $outfile

rm /tmp/$tmp_file.file0 /tmp/$tmp_file.file2
#以ad100为例  spl固件是24K大小 用上面的shell脚本把额外norinfo信息写到spl的23K的位置
./dd_to_file.sh norinfo.bin u-boot-spl-pad.bin 23552

2. 拼接到SPL的后面存储.

#以ad100为例  spl固件是24K大小   那么偏移位置就是24576
cat u-boot-spl-pad.bin norinfo.bin > u-boot-spl-pad-nor-info.bin

3. 烧录工具定义多一个分区单独存储.

偏移位置注意修改, 即第二章节的三个偏移配置