Commit 3a09583fdfc5af012a2979d2b31e3ff3900c80aa
Committed by
Ye Li
1 parent
ad7b74b415
Exists in
smarc-8mp-android-11.0.0_2.0.0
and in
1 other branch
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
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 | +); |
-
mentioned in commit f07a8c