MLK-25271: new qspihdr subsystem for u-boot q(f)spi boot
authorHan Xu <han.xu@nxp.com>
Fri, 22 Jan 2021 03:18:53 +0000 (21:18 -0600)
committerYe Li <ye.li@nxp.com>
Thu, 29 Apr 2021 10:26:24 +0000 (03:26 -0700)
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)
(cherry picked from commit 3a09583fdfc5af012a2979d2b31e3ff3900c80aa)

arch/arm/mach-imx/Kconfig
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/cmd_qspihdr.c [new file with mode: 0644]

index 6245bcb..5fec385 100644 (file)
@@ -166,6 +166,14 @@ config CMD_NANDBCB
          This is similar to kobs-ng, which is used in Linux as separate
          rootfs package.
 
+config CMD_QSPIHDR
+       bool "Q(F)SPI Boot Config Header command"
+       depends on DM_SPI_FLASH
+       default y
+       help
+         Boot from Q(F)SPI need a boot config header, this command can
+         help to check if header already exists or add one if not.
+
 config FSL_MFGPROT
        bool "Support the 'mfgprot' command"
        depends on IMX_HAB || AHAB_BOOT
index 12a06e2..a2d9e16 100644 (file)
@@ -78,6 +78,7 @@ obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o
 obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
 obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
 obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
+obj-$(CONFIG_CMD_QSPIHDR) += cmd_qspihdr.o
 obj-$(CONFIG_IMX_VSERVICE) += imx_vservice.o
 endif
 
diff --git a/arch/arm/mach-imx/cmd_qspihdr.c b/arch/arm/mach-imx/cmd_qspihdr.c
new file mode 100644 (file)
index 0000000..6e27586
--- /dev/null
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 NXP
+ */
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <asm/io.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <dm/device-internal.h>
+
+static struct spi_flash *flash;
+
+#define QSPI_HDR_TAG           0xc0ffee01 /* c0ffee01 */
+#define QSPI_HDR_TAG_OFF       0x1fc
+#define FSPI_HDR_TAG           0x42464346/* FCFB, bigendian */
+#define FSPI_HDR_TAG_OFF       0x0
+
+#define HDR_LEN                        0x200
+
+#ifdef CONFIG_MX7
+#define QSPI_HDR_OFF   0x0
+#define QSPI_DATA_OFF  0x400
+#else
+#define QSPI_HDR_OFF   0x400
+#define QSPI_DATA_OFF  0x1000
+#endif
+
+#ifdef CONFIG_IMX8MM
+#define FSPI_HDR_OFF   0x0
+#define FSPI_DATA_OFF  0x1000
+#else
+#define FSPI_HDR_OFF   0x400
+#define FSPI_DATA_OFF  0x1000
+#endif
+
+#define FLAG_VERBOSE           1
+
+struct qspi_config_parameter {
+       u32 dqs_loopback;                       /* Sets DQS LoopBack Mode to enable Dummy Pad MCR[24] */
+       u32 hold_delay;                         /* No needed on ULT1 */
+       u32 hsphs;                              /* Half Speed Phase Shift */
+       u32 hsdly;                              /* Half Speed Delay Selection */
+       u32 device_quad_mode_en;                /* Write Command to Device */
+       u32 device_cmd;                         /* Cmd to xfer to device */
+       u32 write_cmd_ipcr;                     /* IPCR value of Write Cmd */
+       u32 write_enable_ipcr;                  /* IPCR value of Write enable */
+       u32 cs_hold_time;                       /* CS hold time in terms of serial clock.(for example 1 serial clock cyle) */
+       u32 cs_setup_time;                      /* CS setup time in terms of serial clock.(for example 1 serial clock cyle) */
+       u32 sflash_A1_size;                     /* interms of Bytes */
+       u32 sflash_A2_size;                     /* interms of Bytes */
+       u32 sflash_B1_size;                     /* interms of Bytes */
+       u32 sflash_B2_size;                     /* interms of Bytes */
+       u32 sclk_freq;                          /* 0 - 18MHz, 1 - 49MHz, 2 - 55MHz, 3 - 60MHz, 4 - 66Mhz, 5 - 76MHz, 6 - 99MHz (only for SDR Mode) */
+       u16 busy_bit_offset;                    /* Flash device busy bit offset in status register */
+       u16 busy_bit_polarity;                  /* Polarity of busy bit, 0 means the busy bit is 1 while busy and vice versa. */
+       u32 sflash_type;                        /* 1 - Single, 2 - Dual, 4 - Quad */
+       u32 sflash_port;                        /* 0 - Only Port-A, 1 - Both PortA and PortB */
+       u32 ddr_mode_enable;                    /* Enable DDR mode if set to TRUE */
+       u32 dqs_enable;                         /* Enable DQS mode if set to TRUE. Bit 0 represents DQS_EN, bit 1 represents DQS_LAT_EN */
+       u32 parallel_mode_enable;               /* Enable Individual or parrallel mode. */
+       u32 portA_cs1;                          /* Enable Port A CS1 */
+       u32 portB_cs1;                          /* Enable Port B CS1 */
+       u32 fsphs;                              /* Full Speed Phase Selection */
+       u32 fsdly;                              /* Full Speed Phase Selection */
+       u32 ddrsmp;                             /* Select the sampling point for incoming data when serial flash is in DDR mode. */
+       u32 command_seq[64];                    /* Set of seq to perform optimum read on SFLASH as as per vendor SFLASH */
+       u32 read_status_ipcr;                   /* IPCR value of Read Status Reg */
+       u32 enable_dqs_phase;                   /* Enable DQS phase */
+       u32 config_cmds_en;                     /* Enable config commands */
+       u32 config_cmds[4];                     /* config commands, used to configure nor flash */
+       u32 config_cmds_args[4];                /* config commands argu */
+       u32 dqs_pad_setting_override;           /* DQS pin pad setting override */
+       u32 sclk_pad_setting_override;          /* SCLK pin pad setting override */
+       u32 data_pad_setting_override;          /* DATA pins pad setting override */
+       u32 cs_pad_setting_override;            /* CS pins pad setting override */
+       u32 dqs_loopback_internal;              /* 0: dqs loopback from pad, 1: dqs loopback internally */
+       u32 dqs_phase_sel;                      /* dqs phase sel */
+       u32 dqs_fa_delay_chain_sel;             /* dqs fa delay chain selection */
+       u32 dqs_fb_delay_chain_sel;             /* dqs fb delay chain selection */
+       u32 sclk_fa_delay_chain_sel;            /* sclk fa delay chain selection */
+       u32 sclk_fb_delay_chain_sel;            /* sclk fb delay chain selection */
+       u32 misc_clock_enable;                  /* Misc clock enable, bit 0 means differential clock enable, bit 1 means CK2 clock enable. */
+       u32 reserve[15];                        /* Reserved area, the total size of configuration structure should be 512 bytes */
+       u32 tag;                                /* QSPI configuration TAG, should be 0xc0ffee01 */
+};
+
+struct fspi_config_parameter {
+       u32 tag;                        /* tag, 0x46434642 ascii 'FCFB' */
+       u32 version;                    /* 0x00000156 ascii bugfix | minor | major | 'V' */
+       u16 reserved;
+       u8  reserved0[2];
+       u8  readSampleClkSrc;           /* 0 - internal loopback, 1 - loopback from DQS pad, 2 - loopback from SCK pad, 3 - Flash provided DQS */
+       u8  dataHoldTime;               /* CS hold time */
+       u8  dataSetupTime;              /* CS setup time */
+       u8  columnAddressWidth;         /* 3 - for HyperFlash, 0 - other devices */
+       u8  deviceModeCfgEnable;        /* device mode configuration enable feature, 0 - disable, 1- enable */
+       u8  reserved1[3];
+       u32 deviceModeSeq;              /* sequence parameter for device mode configuration */
+       u32 deviceModeArg;              /* device mode argument, effective only when deviceModeCfgEnable = 1 */
+       u8  configCmdEnable;            /* config command enable feature, 0 - disable, 1 - enable */
+       u8  reserved2[3];
+       u32 configCmdSeqs[4];           /* sequences for config command, allow 4 separate configuration command sequences */
+       u32 configCmdArgs[4];           /* arguments for each separate configuration command sequence */
+       u32 controllerMiscOption;
+                                       /*
+                                        *
+                                        * +--------+----------------------------------------------------------+
+                                        * | offset | description                                              |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | differential clock enable                                |
+                                        * |   0    |                                                          |
+                                        * |        | 0 - differential clock is not supported                  |
+                                        * |        | 1 - differential clock is supported                      |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | CK2 enable                                               |
+                                        * |   1    |                                                          |
+                                        * |        | must set 0 for this silicon                              |
+                                        * |        |                                                          |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | parallel mode enable                                     |
+                                        * |   2    |                                                          |
+                                        * |        | must set 0 for this silicon                              |
+                                        * |        |                                                          |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | word addressable enable                                  |
+                                        * |   3    |                                                          |
+                                        * |        | 0 - device is not word addressable                       |
+                                        * |        | 1 - device is word addressable                           |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | safe configuration frequency enable                      |
+                                        * |   4    |                                                          |
+                                        * |        | 0 - configure external device using specified frequency  |
+                                        * |        | 1 - configure external device using 30MHz                |
+                                        * +--------+----------------------------------------------------------+
+                                        * |   5    | reserved                                                 |
+                                        * +--------+----------------------------------------------------------+
+                                        * |        | ddr mode enable                                          |
+                                        * |   6    |                                                          |
+                                        * |        | 0 - external device works using SDR commands             |
+                                        * |        | 1 - external device works using DDR commands             |
+                                        * +--------+----------------------------------------------------------+
+                                        */
+       u8  deviceType;                 /* 1 - serial NOR */
+       u8  sflashPadType;              /* 1 - single pad, 2 - dual pads, 4 - quad pads, 8 - octal pads */
+       u8  serialClkFreq;              /* 1 - 20MHz, 2 - 50MHz, 3 - 60MHz, 4 - 80MHz, 5 - 100MHz, 6 - 133MHz, 7 - 166MHz, other values - 20MHz*/
+       u8  lutCustomSeqEnable;         /* 0 - use pre-defined LUT sequence index and number, 1 - use LUT sequence parameters provided in this block */
+       u32 reserved3[2];
+       u32 sflashA1Size;               /* For SPI NOR, need to fill with actual size, in terms of bytes */
+       u32 sflashA2Size;               /* same as above */
+       u32 sflashB1Size;               /* same as above */
+       u32 sflashB2Size;               /* same as above */
+       u32 csPadSettingOverride;       /* set to 0 if it is not supported */
+       u32 sclkPadSettingOverride;     /* set to 0 if it is not supported */
+       u32 dataPadSettingOverride;     /* set to 0 if it is not supported */
+       u32 dqsPadSettingOverride;      /* set to 0 if it is not supported */
+       u32 timeoutInMs;                /* maximum wait time during dread busy status, not used in ROM */
+       u32 commandInterval;            /* interval of CS deselected period, set to 0 */
+       u16 dataValidTime[2];           /* time from clock edge to data valid edge */
+                                       /* This field is used when the FlexSPI root clock is less than 100MHz and the read sample */
+                                       /* clock source is device provided DQS signal without CK2 support. */
+                                       /* [31:16] - data valid time for DLLB in terms of 0.1ns */
+                                       /* [15:0]  - data valid time for DLLA in terms of 0.1ns */
+       u16 busyOffset;                 /* busy bit offset, valid range: 0 - 31 */
+       u16 busyBitPolarity;            /* 0 - busy bit is 1 if device is busy, 1 - busy bit is 0 if device is busy */
+       u32 lookupTable[64];            /* lookup table */
+       u32 lutCustomSeq[12];           /* customized LUT sequence */
+       u32 reserved4[4];
+       u32 pageSize;                   /* page size of serial NOR flash, not used in ROM */
+       u32 sectorSize;                 /* sector size of serial NOR flash, not used in ROM */
+       u32 reserved5[14];
+};
+
+struct header_config {
+       union {
+               struct qspi_config_parameter qspi_hdr_config;
+               struct fspi_config_parameter fspi_hdr_config;
+       };
+};
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+static struct qspi_config_parameter qspi_safe_config = {
+       .cs_hold_time           = 3,
+       .cs_setup_time          = 3,
+       .sflash_A1_size         = 0x4000000,
+       .sflash_B1_size         = 0x4000000,
+       .sflash_type            = 1,
+       .command_seq[0]         = 0x08180403,
+       .command_seq[1]         = 0x24001c00,
+       .tag                    = 0xc0ffee01,
+};
+
+static struct header_config *safe_config = (struct header_config *)&qspi_safe_config;
+#else
+static struct fspi_config_parameter fspi_safe_config = {
+       .tag                    = 0x42464346,
+       .version                = 0x56010000,
+       .dataHoldTime           = 0x3,
+       .dataSetupTime          = 0x3,
+       .deviceType             = 0x1,
+       .sflashPadType          = 0x1,
+       .serialClkFreq          = 0x2,
+       .sflashA1Size           = 0x10000000,
+       .lookupTable[0]         = 0x0818040b,
+       .lookupTable[1]         = 0x24043008,
+};
+
+static struct header_config *safe_config = (struct header_config *)&fspi_safe_config;
+#endif
+
+static int qspi_erase_update(struct spi_flash *flash, int off, int len, void *buf)
+{
+       int size;
+       int ret;
+
+       size = ROUND(len, flash->sector_size);
+       ret = spi_flash_erase(flash, off, size);
+       printf("Erase %#x bytes @ %#x %s\n",
+              size, off, ret ? "ERROR" : "OK");
+       if (ret)
+               return ret;
+
+       ret = spi_flash_write(flash, off, len, buf);
+       printf("Write %#x bytes @ %#x %s\n",
+              len, off, ret ? "ERROR" : "OK");
+
+       return ret;
+}
+
+static int do_qspihdr_check(int argc, char * const argv[], int flag)
+{
+       u32 buf;
+       unsigned long addr;
+       char *endp;
+       void *tmp;
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       int off = QSPI_HDR_OFF + QSPI_HDR_TAG_OFF;
+       int tag = QSPI_HDR_TAG;
+#else
+       int off = FSPI_HDR_OFF + FSPI_HDR_TAG_OFF;
+       int tag = FSPI_HDR_TAG;
+#endif
+
+       if (argc == 3) {
+               /* check data in memory */
+               addr = simple_strtoul(argv[2], &endp, 16);
+
+               tmp = map_physmem(addr + off, 4, MAP_WRBACK);
+               if (!tmp) {
+                       printf("Failed to map physical memory\n");
+                       return 1;
+               }
+
+               if (*(u32 *)tmp == tag) {
+                       if (flag & FLAG_VERBOSE)
+                               printf("Found boot config header in memory\n");
+                       unmap_physmem(tmp, 4);
+                       return 0;
+               } else {
+                       if (flag & FLAG_VERBOSE)
+                               printf("NO boot config header in memory\n");
+                       unmap_physmem(tmp, 4);
+                       return 1;
+               }
+       } else {
+               spi_flash_read(flash, off, 4, &buf);
+
+               if (buf == tag) {
+                       if (flag & FLAG_VERBOSE)
+                               printf("Found boot config header in Q(F)SPI\n");
+                       return 0;
+               } else {
+                       if (flag & FLAG_VERBOSE)
+                               printf("NO boot config header in Q(F)SPI\n");
+                       return 1;
+               }
+       }
+}
+
+static void hdr_dump(void *data)
+{
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       struct qspi_config_parameter *hdr =
+               (struct qspi_config_parameter *)data;
+#else
+       struct fspi_config_parameter *hdr =
+               (struct fspi_config_parameter *)data;
+#endif
+       int i;
+
+#define PH(mem, cnt) (                                         \
+{                                                              \
+       if (cnt > 1) {                                          \
+               int len = strlen(#mem);                         \
+               char *sub = strchr(#mem, '[');                  \
+               if (sub)                                        \
+                       *sub = '\0';                            \
+               for (i = 0; i < cnt; ++i)                       \
+                       printf("  %s[%02d%-*s = %08x\n",        \
+                              #mem, i, 25 - len, "]",          \
+                              (u32)*(&hdr->mem + i));          \
+       } else {                                                \
+               printf("  %-25s = %0*x\n",                      \
+                      #mem, (int)sizeof(hdr->mem), hdr->mem);  \
+               }                                               \
+}                                                              \
+)
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       PH(dqs_loopback, 1);
+       PH(hold_delay, 1);
+       PH(hsphs, 1);
+       PH(hsdly, 1);
+       PH(device_quad_mode_en, 1);
+       PH(write_cmd_ipcr, 1);
+       PH(write_enable_ipcr, 1);
+       PH(cs_hold_time, 1);
+       PH(cs_setup_time, 1);
+       PH(sflash_A1_size, 1);
+       PH(sflash_A2_size, 1);
+       PH(sflash_B1_size, 1);
+       PH(sflash_B2_size, 1);
+       PH(sclk_freq, 1);
+       PH(busy_bit_offset, 1);
+       PH(busy_bit_polarity, 1);
+       PH(sflash_type, 1);
+       PH(sflash_port, 1);
+       PH(ddr_mode_enable, 1);
+       PH(dqs_enable, 1);
+       PH(parallel_mode_enable, 1);
+       PH(portA_cs1, 1);
+       PH(portB_cs1, 1);
+       PH(fsphs, 1);
+       PH(fsdly, 1);
+       PH(ddrsmp, 1);
+       PH(command_seq[0], 64);
+       PH(read_status_ipcr, 1);
+       PH(enable_dqs_phase, 1);
+       PH(config_cmds_en, 1);
+       PH(config_cmds[0], 4);
+       PH(config_cmds_args[0], 4);
+       PH(dqs_pad_setting_override, 1);
+       PH(sclk_pad_setting_override, 1);
+       PH(data_pad_setting_override, 1);
+       PH(cs_pad_setting_override, 1);
+       PH(dqs_loopback_internal, 1);
+       PH(dqs_phase_sel, 1);
+       PH(dqs_fa_delay_chain_sel, 1);
+       PH(dqs_fb_delay_chain_sel, 1);
+       PH(sclk_fa_delay_chain_sel, 1);
+       PH(sclk_fb_delay_chain_sel, 1);
+       PH(misc_clock_enable, 1);
+       PH(tag, 1);
+#else
+       PH(tag, 1);
+       PH(version, 1);
+       PH(readSampleClkSrc, 1);
+       PH(dataHoldTime, 1);
+       PH(dataSetupTime, 1);
+       PH(columnAddressWidth, 1);
+       PH(deviceModeCfgEnable, 1);
+       PH(deviceModeSeq, 1);
+       PH(deviceModeArg, 1);
+       PH(configCmdEnable, 1);
+       PH(configCmdSeqs[0], 4);
+       PH(configCmdArgs[0], 4);
+       PH(controllerMiscOption, 1);
+       PH(deviceType, 1);
+       PH(sflashPadType, 1);
+       PH(serialClkFreq, 1);
+       PH(lutCustomSeqEnable, 1);
+       PH(sflashA1Size, 1);
+       PH(sflashA2Size, 1);
+       PH(sflashB1Size, 1);
+       PH(sflashB2Size, 1);
+       PH(csPadSettingOverride, 1);
+       PH(sclkPadSettingOverride, 1);
+       PH(dataPadSettingOverride, 1);
+       PH(dqsPadSettingOverride, 1);
+       PH(timeoutInMs, 1);
+       PH(commandInterval, 1);
+       PH(dataValidTime[0], 2);
+       PH(busyOffset, 1);
+       PH(busyBitPolarity, 1);
+       PH(lookupTable[0], 64);
+       PH(lutCustomSeq[0], 12);
+       PH(pageSize, 1);
+       PH(sectorSize, 1);
+#endif
+}
+
+static int do_qspihdr_dump(int argc, char * const argv[])
+{
+       unsigned long addr;
+       char *endp;
+       void *tmp;
+       void *buf;
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       int off = QSPI_HDR_OFF;
+#else
+       int off = FSPI_HDR_OFF;
+#endif
+
+       if (argc == 3) {
+               /* check data in memory */
+               if (do_qspihdr_check(3, argv, FLAG_VERBOSE)) {
+                       /* return 0 in any cases */
+                       return 0;
+               }
+
+               addr = simple_strtoul(argv[2], &endp, 16);
+
+               tmp = map_physmem(addr + off, HDR_LEN, MAP_WRBACK);
+               if (!tmp) {
+                       printf("Failed to map physical memory\n");
+                       return 1;
+               }
+
+               hdr_dump(tmp);
+               unmap_physmem(tmp, HDR_LEN);
+       } else {
+               /* check data in Q(F)SPI */
+               buf = malloc(HDR_LEN);
+               if (!buf) {
+                       printf("Failed to alloc memory\n");
+                       /* return 0 in any cases */
+                       return 0;
+               }
+
+               spi_flash_read(flash, off, HDR_LEN, buf);
+
+               hdr_dump(buf);
+               free(buf);
+       }
+
+       return 0;
+}
+
+static int do_qspihdr_init(int argc, char * const argv[])
+{
+       unsigned long addr, len;
+       char *endp;
+       int total_len;
+       void *tmp;
+       void *buf;
+       bool hdr_flag = false;
+       int ret;
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       int hdr_off = QSPI_HDR_OFF;
+       int data_off = QSPI_DATA_OFF;
+#else
+       int hdr_off = FSPI_HDR_OFF;
+       int data_off = FSPI_DATA_OFF;
+
+       safe_config->fspi_hdr_config.pageSize = flash->page_size;
+       safe_config->fspi_hdr_config.sectorSize = flash->sector_size;
+#endif
+
+       addr = simple_strtoul(argv[2], &endp, 16);
+       len = simple_strtoul(argv[3], &endp, 16);
+
+       total_len = data_off + len;
+       if (total_len > flash->size) {
+               printf("Error: length %lx over flash size (%#x)\n",
+                      len, flash->size);
+               return 1;
+       }
+
+       /* check if header exists in this memory area*/
+       if (do_qspihdr_check(3, argv, 0) == 0)
+               hdr_flag = true;
+
+       tmp = map_physmem(addr, len, MAP_WRBACK);
+       if (!tmp) {
+               printf("Failed to map physical memory\n");
+               return 1;
+       }
+
+       if (hdr_flag)
+               goto burn_image;
+
+       buf = malloc(total_len);
+       if (!buf) {
+               printf("Failed to alloc memory\n");
+               unmap_physmem(tmp, total_len);
+               return 1;
+       }
+
+       memset(buf, 0xff, total_len);
+       memcpy(buf + hdr_off, safe_config, HDR_LEN);
+       memcpy(buf + data_off, tmp, len);
+
+burn_image:
+       if (hdr_flag) {
+               ret = qspi_erase_update(flash, 0, len, tmp);
+       } else {
+               ret = qspi_erase_update(flash, 0, total_len, buf);
+               free(buf);
+       }
+
+       unmap_physmem(tmp, total_len);
+       return ret;
+}
+
+static int do_qspihdr_update(int argc, char * const argv[])
+{
+       int len;
+       int size;
+       void *buf;
+       int ret;
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_ARCH_MX7ULP)
+       int hdr_off = QSPI_HDR_OFF;
+#else
+       int hdr_off = FSPI_HDR_OFF;
+#endif
+
+       len = hdr_off + HDR_LEN;
+       size = ROUND(len, flash->sector_size);
+
+       buf = malloc(size);
+       if (!buf) {
+               printf("Failed to alloc memory\n");
+               return 1;
+       }
+
+       spi_flash_read(flash, 0, size, buf);
+       memcpy(buf + hdr_off, safe_config, HDR_LEN);
+
+       ret = qspi_erase_update(flash, 0, size, buf);
+       free(buf);
+
+       return ret;
+}
+
+static int do_qspihdr(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+       char *cmd;
+       unsigned int bus = CONFIG_SF_DEFAULT_BUS;
+       unsigned int cs = CONFIG_SF_DEFAULT_CS;
+       unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
+       unsigned int mode = CONFIG_SF_DEFAULT_MODE;
+       int flags = 0;
+       int ret;
+
+       if (argc < 2)
+               goto usage;
+
+#ifdef CONFIG_DM_SPI_FLASH
+       struct udevice *new, *bus_dev;
+
+       ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
+       if (!ret)
+               device_remove(new, DM_REMOVE_NORMAL);
+       flash = NULL;
+       ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
+       if (ret) {
+               printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
+                      bus, cs, ret);
+               return 1;
+       }
+       flash = dev_get_uclass_priv(new);
+#endif
+
+       cmd = argv[1];
+
+       if (strcmp(cmd, "check") == 0)
+               return do_qspihdr_check(argc, argv, flags | FLAG_VERBOSE);
+
+       if (strcmp(cmd, "dump") == 0)
+               return do_qspihdr_dump(argc, argv);
+
+       if (strcmp(cmd, "init") == 0) {
+               if (argc < 5)
+                       goto usage;
+               return do_qspihdr_init(argc, argv);
+       }
+
+       if (strcmp(cmd, "update") == 0) {
+               if (argc < 3)
+                       goto usage;
+               return do_qspihdr_update(argc, argv);
+       }
+
+       return 0;
+usage:
+       return CMD_RET_USAGE;
+}
+
+static char qspihdr_help_text[] =
+       "check [addr] - check if boot config already exists, 0-yes, 1-no\n"
+       "               with addr, it will check data in memory of this addr\n"
+       "               without addr, it will check data in Q(F)SPI chip\n"
+       "qspihdr dump [addr] - dump the header information, if exists\n"
+       "               with addr, it will check data in memory of this addr\n"
+       "               without addr, it will check data in Q(F)SPI chip\n"
+       "qspihdr init addr len safe - burn data to Q(F)SPI with header\n"
+       "               if data contains header, it will be used, otherwise,\n"
+       "               safe: most common header, single line, sdr, low freq\n"
+       "qspihdr update safe - only update the header in Q(F)SPI\n";
+
+U_BOOT_CMD(qspihdr, 5, 1, do_qspihdr,
+       "Q(F)SPI Boot Config sub-system",
+       qspihdr_help_text
+);