Commit 3a09583fdfc5af012a2979d2b31e3ff3900c80aa

Authored by Han Xu
Committed by Ye Li
1 parent ad7b74b415

MLK-25271: new qspihdr subsystem for u-boot q(f)spi boot

qspihdr is a new subsystem in u-boot to check/updat q(f)spi boot config
headers. It's already integrated with uuu and can be used to burn
q(f)spi boot images for i.MX6/7/8 families.

Basic usage:
check [addr]: check if exists valid q(f)spi boot config header at
	      spcified memory addr, or check the nor chip without addr
dump [addr] : dump q(f)spi boot config header content from spcified
	      memory addr, or from nor chip without addr
init addr len safe: burn boot image from memory addr with size of len to
	      q(f)spi, with safe boot config header
update safe : only update header in q(f)spi to a safe boot config

Signed-off-by: Han Xu <han.xu@nxp.com>
(cherry picked from commit dc0ba70f5ba04425e9562c1dd4f6dcb7db322f4b)

Showing 3 changed files with 617 additions and 0 deletions Side-by-side Diff

arch/arm/mach-imx/Kconfig
... ... @@ -157,6 +157,14 @@
157 157 This is similar to kobs-ng, which is used in Linux as separate
158 158 rootfs package.
159 159  
  160 +config CMD_QSPIHDR
  161 + bool "Q(F)SPI Boot Config Header command"
  162 + depends on DM_SPI_FLASH
  163 + default y
  164 + help
  165 + Boot from Q(F)SPI need a boot config header, this command can
  166 + help to check if header already exists or add one if not.
  167 +
160 168 config FSL_MFGPROT
161 169 bool "Support the 'mfgprot' command"
162 170 depends on IMX_HAB || AHAB_BOOT
arch/arm/mach-imx/Makefile
... ... @@ -78,6 +78,7 @@
78 78 obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
79 79 obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
80 80 obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
  81 +obj-$(CONFIG_CMD_QSPIHDR) += cmd_qspihdr.o
81 82 obj-$(CONFIG_IMX_VSERVICE) += imx_vservice.o
82 83 endif
83 84  
arch/arm/mach-imx/cmd_qspihdr.c
  1 +// SPDX-License-Identifier: GPL-2.0+
  2 +/*
  3 + * Copyright (C) 2021 NXP
  4 + */
  5 +#include <common.h>
  6 +#include <dm.h>
  7 +#include <mapmem.h>
  8 +#include <asm/io.h>
  9 +#include <spi.h>
  10 +#include <spi_flash.h>
  11 +#include <dm/device-internal.h>
  12 +
  13 +static struct spi_flash *flash;
  14 +
  15 +#define QSPI_HDR_TAG 0xc0ffee01 /* c0ffee01 */
  16 +#define QSPI_HDR_TAG_OFF 0x1fc
  17 +#define FSPI_HDR_TAG 0x42464346/* FCFB, bigendian */
  18 +#define FSPI_HDR_TAG_OFF 0x0
  19 +
  20 +#define HDR_LEN 0x200
  21 +
  22 +#ifdef CONFIG_MX7
  23 +#define QSPI_HDR_OFF 0x0
  24 +#define QSPI_DATA_OFF 0x400
  25 +#else
  26 +#define QSPI_HDR_OFF 0x400
  27 +#define QSPI_DATA_OFF 0x1000
  28 +#endif
  29 +
  30 +#ifdef CONFIG_IMX8MM
  31 +#define FSPI_HDR_OFF 0x0
  32 +#define FSPI_DATA_OFF 0x1000
  33 +#else
  34 +#define FSPI_HDR_OFF 0x400
  35 +#define FSPI_DATA_OFF 0x1000
  36 +#endif
  37 +
  38 +#define FLAG_VERBOSE 1
  39 +
  40 +struct qspi_config_parameter {
  41 + u32 dqs_loopback; /* Sets DQS LoopBack Mode to enable Dummy Pad MCR[24] */
  42 + u32 hold_delay; /* No needed on ULT1 */
  43 + u32 hsphs; /* Half Speed Phase Shift */
  44 + u32 hsdly; /* Half Speed Delay Selection */
  45 + u32 device_quad_mode_en; /* Write Command to Device */
  46 + u32 device_cmd; /* Cmd to xfer to device */
  47 + u32 write_cmd_ipcr; /* IPCR value of Write Cmd */
  48 + u32 write_enable_ipcr; /* IPCR value of Write enable */
  49 + u32 cs_hold_time; /* CS hold time in terms of serial clock.(for example 1 serial clock cyle) */
  50 + u32 cs_setup_time; /* CS setup time in terms of serial clock.(for example 1 serial clock cyle) */
  51 + u32 sflash_A1_size; /* interms of Bytes */
  52 + u32 sflash_A2_size; /* interms of Bytes */
  53 + u32 sflash_B1_size; /* interms of Bytes */
  54 + u32 sflash_B2_size; /* interms of Bytes */
  55 + u32 sclk_freq; /* 0 - 18MHz, 1 - 49MHz, 2 - 55MHz, 3 - 60MHz, 4 - 66Mhz, 5 - 76MHz, 6 - 99MHz (only for SDR Mode) */
  56 + u16 busy_bit_offset; /* Flash device busy bit offset in status register */
  57 + u16 busy_bit_polarity; /* Polarity of busy bit, 0 means the busy bit is 1 while busy and vice versa. */
  58 + u32 sflash_type; /* 1 - Single, 2 - Dual, 4 - Quad */
  59 + u32 sflash_port; /* 0 - Only Port-A, 1 - Both PortA and PortB */
  60 + u32 ddr_mode_enable; /* Enable DDR mode if set to TRUE */
  61 + u32 dqs_enable; /* Enable DQS mode if set to TRUE. Bit 0 represents DQS_EN, bit 1 represents DQS_LAT_EN */
  62 + u32 parallel_mode_enable; /* Enable Individual or parrallel mode. */
  63 + u32 portA_cs1; /* Enable Port A CS1 */
  64 + u32 portB_cs1; /* Enable Port B CS1 */
  65 + u32 fsphs; /* Full Speed Phase Selection */
  66 + u32 fsdly; /* Full Speed Phase Selection */
  67 + u32 ddrsmp; /* Select the sampling point for incoming data when serial flash is in DDR mode. */
  68 + u32 command_seq[64]; /* Set of seq to perform optimum read on SFLASH as as per vendor SFLASH */
  69 + u32 read_status_ipcr; /* IPCR value of Read Status Reg */
  70 + u32 enable_dqs_phase; /* Enable DQS phase */
  71 + u32 config_cmds_en; /* Enable config commands */
  72 + u32 config_cmds[4]; /* config commands, used to configure nor flash */
  73 + u32 config_cmds_args[4]; /* config commands argu */
  74 + u32 dqs_pad_setting_override; /* DQS pin pad setting override */
  75 + u32 sclk_pad_setting_override; /* SCLK pin pad setting override */
  76 + u32 data_pad_setting_override; /* DATA pins pad setting override */
  77 + u32 cs_pad_setting_override; /* CS pins pad setting override */
  78 + u32 dqs_loopback_internal; /* 0: dqs loopback from pad, 1: dqs loopback internally */
  79 + u32 dqs_phase_sel; /* dqs phase sel */
  80 + u32 dqs_fa_delay_chain_sel; /* dqs fa delay chain selection */
  81 + u32 dqs_fb_delay_chain_sel; /* dqs fb delay chain selection */
  82 + u32 sclk_fa_delay_chain_sel; /* sclk fa delay chain selection */
  83 + u32 sclk_fb_delay_chain_sel; /* sclk fb delay chain selection */
  84 + u32 misc_clock_enable; /* Misc clock enable, bit 0 means differential clock enable, bit 1 means CK2 clock enable. */
  85 + u32 reserve[15]; /* Reserved area, the total size of configuration structure should be 512 bytes */
  86 + u32 tag; /* QSPI configuration TAG, should be 0xc0ffee01 */
  87 +};
  88 +
  89 +struct fspi_config_parameter {
  90 + u32 tag; /* tag, 0x46434642 ascii 'FCFB' */
  91 + u32 version; /* 0x00000156 ascii bugfix | minor | major | 'V' */
  92 + u16 reserved;
  93 + u8 reserved0[2];
  94 + u8 readSampleClkSrc; /* 0 - internal loopback, 1 - loopback from DQS pad, 2 - loopback from SCK pad, 3 - Flash provided DQS */
  95 + u8 dataHoldTime; /* CS hold time */
  96 + u8 dataSetupTime; /* CS setup time */
  97 + u8 columnAddressWidth; /* 3 - for HyperFlash, 0 - other devices */
  98 + u8 deviceModeCfgEnable; /* device mode configuration enable feature, 0 - disable, 1- enable */
  99 + u8 reserved1[3];
  100 + u32 deviceModeSeq; /* sequence parameter for device mode configuration */
  101 + u32 deviceModeArg; /* device mode argument, effective only when deviceModeCfgEnable = 1 */
  102 + u8 configCmdEnable; /* config command enable feature, 0 - disable, 1 - enable */
  103 + u8 reserved2[3];
  104 + u32 configCmdSeqs[4]; /* sequences for config command, allow 4 separate configuration command sequences */
  105 + u32 configCmdArgs[4]; /* arguments for each separate configuration command sequence */
  106 + u32 controllerMiscOption;
  107 + /*
  108 + *
  109 + * +--------+----------------------------------------------------------+
  110 + * | offset | description |
  111 + * +--------+----------------------------------------------------------+
  112 + * | | differential clock enable |
  113 + * | 0 | |
  114 + * | | 0 - differential clock is not supported |
  115 + * | | 1 - differential clock is supported |
  116 + * +--------+----------------------------------------------------------+
  117 + * | | CK2 enable |
  118 + * | 1 | |
  119 + * | | must set 0 for this silicon |
  120 + * | | |
  121 + * +--------+----------------------------------------------------------+
  122 + * | | parallel mode enable |
  123 + * | 2 | |
  124 + * | | must set 0 for this silicon |
  125 + * | | |
  126 + * +--------+----------------------------------------------------------+
  127 + * | | word addressable enable |
  128 + * | 3 | |
  129 + * | | 0 - device is not word addressable |
  130 + * | | 1 - device is word addressable |
  131 + * +--------+----------------------------------------------------------+
  132 + * | | safe configuration frequency enable |
  133 + * | 4 | |
  134 + * | | 0 - configure external device using specified frequency |
  135 + * | | 1 - configure external device using 30MHz |
  136 + * +--------+----------------------------------------------------------+
  137 + * | 5 | reserved |
  138 + * +--------+----------------------------------------------------------+
  139 + * | | ddr mode enable |
  140 + * | 6 | |
  141 + * | | 0 - external device works using SDR commands |
  142 + * | | 1 - external device works using DDR commands |
  143 + * +--------+----------------------------------------------------------+
  144 + */
  145 + u8 deviceType; /* 1 - serial NOR */
  146 + u8 sflashPadType; /* 1 - single pad, 2 - dual pads, 4 - quad pads, 8 - octal pads */
  147 + u8 serialClkFreq; /* 1 - 20MHz, 2 - 50MHz, 3 - 60MHz, 4 - 80MHz, 5 - 100MHz, 6 - 133MHz, 7 - 166MHz, other values - 20MHz*/
  148 + u8 lutCustomSeqEnable; /* 0 - use pre-defined LUT sequence index and number, 1 - use LUT sequence parameters provided in this block */
  149 + u32 reserved3[2];
  150 + u32 sflashA1Size; /* For SPI NOR, need to fill with actual size, in terms of bytes */
  151 + u32 sflashA2Size; /* same as above */
  152 + u32 sflashB1Size; /* same as above */
  153 + u32 sflashB2Size; /* same as above */
  154 + u32 csPadSettingOverride; /* set to 0 if it is not supported */
  155 + u32 sclkPadSettingOverride; /* set to 0 if it is not supported */
  156 + u32 dataPadSettingOverride; /* set to 0 if it is not supported */
  157 + u32 dqsPadSettingOverride; /* set to 0 if it is not supported */
  158 + u32 timeoutInMs; /* maximum wait time during dread busy status, not used in ROM */
  159 + u32 commandInterval; /* interval of CS deselected period, set to 0 */
  160 + u16 dataValidTime[2]; /* time from clock edge to data valid edge */
  161 + /* This field is used when the FlexSPI root clock is less than 100MHz and the read sample */
  162 + /* clock source is device provided DQS signal without CK2 support. */
  163 + /* [31:16] - data valid time for DLLB in terms of 0.1ns */
  164 + /* [15:0] - data valid time for DLLA in terms of 0.1ns */
  165 + u16 busyOffset; /* busy bit offset, valid range: 0 - 31 */
  166 + u16 busyBitPolarity; /* 0 - busy bit is 1 if device is busy, 1 - busy bit is 0 if device is busy */
  167 + u32 lookupTable[64]; /* lookup table */
  168 + u32 lutCustomSeq[12]; /* customized LUT sequence */
  169 + u32 reserved4[4];
  170 + u32 pageSize; /* page size of serial NOR flash, not used in ROM */
  171 + u32 sectorSize; /* sector size of serial NOR flash, not used in ROM */
  172 + u32 reserved5[14];
  173 +};
  174 +
  175 +struct header_config {
  176 + union {
  177 + struct qspi_config_parameter qspi_hdr_config;
  178 + struct fspi_config_parameter fspi_hdr_config;
  179 + };
  180 +};
  181 +
  182 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  183 +static struct qspi_config_parameter qspi_safe_config = {
  184 + .cs_hold_time = 3,
  185 + .cs_setup_time = 3,
  186 + .sflash_A1_size = 0x4000000,
  187 + .sflash_B1_size = 0x4000000,
  188 + .sflash_type = 1,
  189 + .command_seq[0] = 0x08180403,
  190 + .command_seq[1] = 0x24001c00,
  191 + .tag = 0xc0ffee01,
  192 +};
  193 +
  194 +static struct header_config *safe_config = (struct header_config *)&qspi_safe_config;
  195 +#else
  196 +static struct fspi_config_parameter fspi_safe_config = {
  197 + .tag = 0x42464346,
  198 + .version = 0x56010000,
  199 + .dataHoldTime = 0x3,
  200 + .dataSetupTime = 0x3,
  201 + .deviceType = 0x1,
  202 + .sflashPadType = 0x1,
  203 + .serialClkFreq = 0x2,
  204 + .sflashA1Size = 0x10000000,
  205 + .lookupTable[0] = 0x0818040b,
  206 + .lookupTable[1] = 0x24043008,
  207 +};
  208 +
  209 +static struct header_config *safe_config = (struct header_config *)&fspi_safe_config;
  210 +#endif
  211 +
  212 +static int qspi_erase_update(struct spi_flash *flash, int off, int len, void *buf)
  213 +{
  214 + int size;
  215 + int ret;
  216 +
  217 + size = ROUND(len, flash->sector_size);
  218 + ret = spi_flash_erase(flash, off, size);
  219 + printf("Erase %#x bytes @ %#x %s\n",
  220 + size, off, ret ? "ERROR" : "OK");
  221 + if (ret)
  222 + return ret;
  223 +
  224 + ret = spi_flash_write(flash, off, len, buf);
  225 + printf("Write %#x bytes @ %#x %s\n",
  226 + len, off, ret ? "ERROR" : "OK");
  227 +
  228 + return ret;
  229 +}
  230 +
  231 +static int do_qspihdr_check(int argc, char * const argv[], int flag)
  232 +{
  233 + u32 buf;
  234 + unsigned long addr;
  235 + char *endp;
  236 + void *tmp;
  237 +
  238 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  239 + int off = QSPI_HDR_OFF + QSPI_HDR_TAG_OFF;
  240 + int tag = QSPI_HDR_TAG;
  241 +#else
  242 + int off = FSPI_HDR_OFF + FSPI_HDR_TAG_OFF;
  243 + int tag = FSPI_HDR_TAG;
  244 +#endif
  245 +
  246 + if (argc == 3) {
  247 + /* check data in memory */
  248 + addr = simple_strtoul(argv[2], &endp, 16);
  249 +
  250 + tmp = map_physmem(addr + off, 4, MAP_WRBACK);
  251 + if (!tmp && addr) {
  252 + printf("Failed to map physical memory\n");
  253 + return 1;
  254 + }
  255 +
  256 + if (*(u32 *)tmp == tag) {
  257 + if (flag & FLAG_VERBOSE)
  258 + printf("Found boot config header in memory\n");
  259 + unmap_physmem(tmp, 4);
  260 + return 0;
  261 + } else {
  262 + if (flag & FLAG_VERBOSE)
  263 + printf("NO boot config header in memory\n");
  264 + unmap_physmem(tmp, 4);
  265 + return 1;
  266 + }
  267 + } else {
  268 + spi_flash_read(flash, off, 4, &buf);
  269 +
  270 + if (buf == tag) {
  271 + if (flag & FLAG_VERBOSE)
  272 + printf("Found boot config header in Q(F)SPI\n");
  273 + return 0;
  274 + } else {
  275 + if (flag & FLAG_VERBOSE)
  276 + printf("NO boot config header in Q(F)SPI\n");
  277 + return 1;
  278 + }
  279 + }
  280 +}
  281 +
  282 +static void hdr_dump(void *data)
  283 +{
  284 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  285 + struct qspi_config_parameter *hdr =
  286 + (struct qspi_config_parameter *)data;
  287 +#else
  288 + struct fspi_config_parameter *hdr =
  289 + (struct fspi_config_parameter *)data;
  290 +#endif
  291 + int i;
  292 +
  293 +#define PH(mem, cnt) ( \
  294 +{ \
  295 + if (cnt > 1) { \
  296 + int len = strlen(#mem); \
  297 + char *sub = strchr(#mem, '['); \
  298 + *sub = '\0'; \
  299 + for (i = 0; i < cnt; ++i) \
  300 + printf(" %s[%02d%-*s = %08x\n", \
  301 + #mem, i, 25 - len, "]", \
  302 + (u32)*(&hdr->mem + i)); \
  303 + } else { \
  304 + printf(" %-25s = %0*x\n", \
  305 + #mem, (int)sizeof(hdr->mem), hdr->mem); \
  306 + } \
  307 +} \
  308 +)
  309 +
  310 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  311 + PH(dqs_loopback, 1);
  312 + PH(hold_delay, 1);
  313 + PH(hsphs, 1);
  314 + PH(hsdly, 1);
  315 + PH(device_quad_mode_en, 1);
  316 + PH(write_cmd_ipcr, 1);
  317 + PH(write_enable_ipcr, 1);
  318 + PH(cs_hold_time, 1);
  319 + PH(cs_setup_time, 1);
  320 + PH(sflash_A1_size, 1);
  321 + PH(sflash_A2_size, 1);
  322 + PH(sflash_B1_size, 1);
  323 + PH(sflash_B2_size, 1);
  324 + PH(sclk_freq, 1);
  325 + PH(busy_bit_offset, 1);
  326 + PH(busy_bit_polarity, 1);
  327 + PH(sflash_type, 1);
  328 + PH(sflash_port, 1);
  329 + PH(ddr_mode_enable, 1);
  330 + PH(dqs_enable, 1);
  331 + PH(parallel_mode_enable, 1);
  332 + PH(portA_cs1, 1);
  333 + PH(portB_cs1, 1);
  334 + PH(fsphs, 1);
  335 + PH(fsdly, 1);
  336 + PH(ddrsmp, 1);
  337 + PH(command_seq[0], 64);
  338 + PH(read_status_ipcr, 1);
  339 + PH(enable_dqs_phase, 1);
  340 + PH(config_cmds_en, 1);
  341 + PH(config_cmds[0], 4);
  342 + PH(config_cmds_args[0], 4);
  343 + PH(dqs_pad_setting_override, 1);
  344 + PH(sclk_pad_setting_override, 1);
  345 + PH(data_pad_setting_override, 1);
  346 + PH(cs_pad_setting_override, 1);
  347 + PH(dqs_loopback_internal, 1);
  348 + PH(dqs_phase_sel, 1);
  349 + PH(dqs_fa_delay_chain_sel, 1);
  350 + PH(dqs_fb_delay_chain_sel, 1);
  351 + PH(sclk_fa_delay_chain_sel, 1);
  352 + PH(sclk_fb_delay_chain_sel, 1);
  353 + PH(misc_clock_enable, 1);
  354 + PH(tag, 1);
  355 +#else
  356 + PH(tag, 1);
  357 + PH(version, 1);
  358 + PH(readSampleClkSrc, 1);
  359 + PH(dataHoldTime, 1);
  360 + PH(dataSetupTime, 1);
  361 + PH(columnAddressWidth, 1);
  362 + PH(deviceModeCfgEnable, 1);
  363 + PH(deviceModeSeq, 1);
  364 + PH(deviceModeArg, 1);
  365 + PH(configCmdEnable, 1);
  366 + PH(configCmdSeqs[0], 4);
  367 + PH(configCmdArgs[0], 4);
  368 + PH(controllerMiscOption, 1);
  369 + PH(deviceType, 1);
  370 + PH(sflashPadType, 1);
  371 + PH(serialClkFreq, 1);
  372 + PH(lutCustomSeqEnable, 1);
  373 + PH(sflashA1Size, 1);
  374 + PH(sflashA2Size, 1);
  375 + PH(sflashB1Size, 1);
  376 + PH(sflashB2Size, 1);
  377 + PH(csPadSettingOverride, 1);
  378 + PH(sclkPadSettingOverride, 1);
  379 + PH(dataPadSettingOverride, 1);
  380 + PH(dqsPadSettingOverride, 1);
  381 + PH(timeoutInMs, 1);
  382 + PH(commandInterval, 1);
  383 + PH(dataValidTime[0], 2);
  384 + PH(busyOffset, 1);
  385 + PH(busyBitPolarity, 1);
  386 + PH(lookupTable[0], 64);
  387 + PH(lutCustomSeq[0], 12);
  388 + PH(pageSize, 1);
  389 + PH(sectorSize, 1);
  390 +#endif
  391 +}
  392 +
  393 +static int do_qspihdr_dump(int argc, char * const argv[])
  394 +{
  395 + unsigned long addr;
  396 + char *endp;
  397 + void *tmp;
  398 + void *buf;
  399 +
  400 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  401 + int off = QSPI_HDR_OFF;
  402 +#else
  403 + int off = FSPI_HDR_OFF;
  404 +#endif
  405 +
  406 + if (argc == 3) {
  407 + /* check data in memory */
  408 + if (do_qspihdr_check(3, argv, FLAG_VERBOSE)) {
  409 + /* return 0 in any cases */
  410 + return 0;
  411 + }
  412 +
  413 + addr = simple_strtoul(argv[2], &endp, 16);
  414 +
  415 + tmp = map_physmem(addr + off, HDR_LEN, MAP_WRBACK);
  416 + if (!tmp && addr) {
  417 + printf("Failed to map physical memory\n");
  418 + return 1;
  419 + }
  420 +
  421 + hdr_dump(tmp);
  422 + unmap_physmem(tmp, HDR_LEN);
  423 + } else {
  424 + /* check data in Q(F)SPI */
  425 + buf = malloc(HDR_LEN);
  426 + if (!buf) {
  427 + printf("Failed to alloc memory\n");
  428 + /* return 0 in any cases */
  429 + return 0;
  430 + }
  431 +
  432 + spi_flash_read(flash, off, HDR_LEN, buf);
  433 +
  434 + hdr_dump(buf);
  435 + free(buf);
  436 + }
  437 +
  438 + return 0;
  439 +}
  440 +
  441 +static int do_qspihdr_init(int argc, char * const argv[])
  442 +{
  443 + unsigned long addr, len;
  444 + char *endp;
  445 + int total_len;
  446 + void *tmp;
  447 + void *buf;
  448 + bool hdr_flag = false;
  449 + int ret;
  450 +
  451 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  452 + int hdr_off = QSPI_HDR_OFF;
  453 + int data_off = QSPI_DATA_OFF;
  454 +#else
  455 + int hdr_off = FSPI_HDR_OFF;
  456 + int data_off = FSPI_DATA_OFF;
  457 +
  458 + safe_config->fspi_hdr_config.pageSize = flash->page_size;
  459 + safe_config->fspi_hdr_config.sectorSize = flash->sector_size;
  460 +#endif
  461 +
  462 + addr = simple_strtoul(argv[2], &endp, 16);
  463 + len = simple_strtoul(argv[3], &endp, 16);
  464 +
  465 + total_len = data_off + len;
  466 + if (total_len > flash->size) {
  467 + printf("Error: length %lx over flash size (%#x)\n",
  468 + len, flash->size);
  469 + return 1;
  470 + }
  471 +
  472 + /* check if header exists in this memory area*/
  473 + if (do_qspihdr_check(3, argv, 0) == 0)
  474 + hdr_flag = true;
  475 +
  476 + tmp = map_physmem(addr, len, MAP_WRBACK);
  477 + if (!tmp && addr) {
  478 + printf("Failed to map physical memory\n");
  479 + return 1;
  480 + }
  481 +
  482 + if (hdr_flag)
  483 + goto burn_image;
  484 +
  485 + buf = malloc(total_len);
  486 + if (!buf) {
  487 + printf("Failed to alloc memory\n");
  488 + unmap_physmem(tmp, total_len);
  489 + return 1;
  490 + }
  491 +
  492 + memset(buf, 0xff, total_len);
  493 + memcpy(buf + hdr_off, safe_config, HDR_LEN);
  494 + memcpy(buf + data_off, tmp, len);
  495 +
  496 +burn_image:
  497 + if (hdr_flag) {
  498 + ret = qspi_erase_update(flash, 0, len, tmp);
  499 + } else {
  500 + ret = qspi_erase_update(flash, 0, total_len, buf);
  501 + free(buf);
  502 + }
  503 +
  504 + unmap_physmem(tmp, total_len);
  505 + return ret;
  506 +}
  507 +
  508 +static int do_qspihdr_update(int argc, char * const argv[])
  509 +{
  510 + int len;
  511 + int size;
  512 + void *buf;
  513 + int ret;
  514 +
  515 +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
  516 + int hdr_off = QSPI_HDR_OFF;
  517 +#else
  518 + int hdr_off = FSPI_HDR_OFF;
  519 +#endif
  520 +
  521 + len = hdr_off + HDR_LEN;
  522 + size = ROUND(len, flash->sector_size);
  523 +
  524 + buf = malloc(size);
  525 + if (!buf) {
  526 + printf("Failed to alloc memory\n");
  527 + return 1;
  528 + }
  529 +
  530 + spi_flash_read(flash, 0, size, buf);
  531 + memcpy(buf + hdr_off, safe_config, HDR_LEN);
  532 +
  533 + ret = qspi_erase_update(flash, 0, size, buf);
  534 + free(buf);
  535 +
  536 + return ret;
  537 +}
  538 +
  539 +static int do_qspihdr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  540 +{
  541 + char *cmd;
  542 + unsigned int bus = CONFIG_SF_DEFAULT_BUS;
  543 + unsigned int cs = CONFIG_SF_DEFAULT_CS;
  544 + unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
  545 + unsigned int mode = CONFIG_SF_DEFAULT_MODE;
  546 + int flags = 0;
  547 + int ret;
  548 +
  549 + if (argc < 2)
  550 + goto usage;
  551 +
  552 +#ifdef CONFIG_DM_SPI_FLASH
  553 + struct udevice *new, *bus_dev;
  554 +
  555 + ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
  556 + if (!ret)
  557 + device_remove(new, DM_REMOVE_NORMAL);
  558 + flash = NULL;
  559 + ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
  560 + if (ret) {
  561 + printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
  562 + bus, cs, ret);
  563 + return 1;
  564 + }
  565 + flash = dev_get_uclass_priv(new);
  566 +#endif
  567 +
  568 + cmd = argv[1];
  569 +
  570 + if (strcmp(cmd, "check") == 0)
  571 + return do_qspihdr_check(argc, argv, flags | FLAG_VERBOSE);
  572 +
  573 + if (strcmp(cmd, "dump") == 0)
  574 + return do_qspihdr_dump(argc, argv);
  575 +
  576 + if (strcmp(cmd, "init") == 0) {
  577 + if (argc < 5)
  578 + goto usage;
  579 + return do_qspihdr_init(argc, argv);
  580 + }
  581 +
  582 + if (strcmp(cmd, "update") == 0) {
  583 + if (argc < 3)
  584 + goto usage;
  585 + return do_qspihdr_update(argc, argv);
  586 + }
  587 +
  588 + return 0;
  589 +usage:
  590 + return CMD_RET_USAGE;
  591 +}
  592 +
  593 +static char qspihdr_help_text[] =
  594 + "check [addr] - check if boot config already exists, 0-yes, 1-no\n"
  595 + " with addr, it will check data in memory of this addr\n"
  596 + " without addr, it will check data in Q(F)SPI chip\n"
  597 + "qspihdr dump [addr] - dump the header information, if exists\n"
  598 + " with addr, it will check data in memory of this addr\n"
  599 + " without addr, it will check data in Q(F)SPI chip\n"
  600 + "qspihdr init addr len safe - burn data to Q(F)SPI with header\n"
  601 + " if data contains header, it will be used, otherwise,\n"
  602 + " safe: most common header, single line, sdr, low freq\n"
  603 + "qspihdr update safe - only update the header in Q(F)SPI\n";
  604 +
  605 +U_BOOT_CMD(qspihdr, 5, 1, do_qspihdr,
  606 + "Q(F)SPI Boot Config sub-system",
  607 + qspihdr_help_text
  608 +);