1 /*
2 * Copyright (c) 2020 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/flash.h>
10 #include <jesd216.h>
11 #include <stdio.h>
12 #include <inttypes.h>
13 #include <string.h>
14
15 #if DT_HAS_COMPAT_STATUS_OKAY(jedec_spi_nor)
16 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(jedec_spi_nor)
17 #elif DT_HAS_COMPAT_STATUS_OKAY(jedec_mspi_nor)
18 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(jedec_mspi_nor)
19 #elif DT_HAS_COMPAT_STATUS_OKAY(nordic_qspi_nor)
20 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_qspi_nor)
21 #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_qspi_nor)
22 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(st_stm32_qspi_nor)
23 #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_ospi_nor)
24 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(st_stm32_ospi_nor)
25 #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_xspi_nor)
26 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(st_stm32_xspi_nor)
27 #elif DT_HAS_COMPAT_STATUS_OKAY(nxp_s32_qspi_nor)
28 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_s32_qspi_nor)
29 #elif DT_HAS_COMPAT_STATUS_OKAY(adi_max32_spixf_nor)
30 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(adi_max32_spixf_nor)
31 #elif DT_HAS_COMPAT_STATUS_OKAY(nxp_imx_flexspi_nor)
32 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_imx_flexspi_nor)
33 #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_ospi_b_nor)
34 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_ospi_b_nor)
35 #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_qspi_nor)
36 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_qspi_nor)
37 #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_rz_qspi_xspi)
38 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_rz_qspi_xspi)
39 #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_rz_qspi_spibsc)
40 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_rz_qspi_spibsc)
41 #elif DT_HAS_COMPAT_STATUS_OKAY(sifli_sf32lb_mpi_qspi_nor)
42 #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(sifli_sf32lb_mpi_qspi_nor)
43 #else
44 #error Unsupported flash driver
45 #define FLASH_NODE DT_INVALID_NODE
46 #endif
47
48 typedef void (*dw_extractor)(const struct jesd216_param_header *php,
49 const struct jesd216_bfp *bfp);
50
51 static const char * const mode_tags[] = {
52 [JESD216_MODE_044] = "QSPI XIP",
53 [JESD216_MODE_088] = "OSPI XIP",
54 [JESD216_MODE_111] = "1-1-1",
55 [JESD216_MODE_112] = "1-1-2",
56 [JESD216_MODE_114] = "1-1-4",
57 [JESD216_MODE_118] = "1-1-8",
58 [JESD216_MODE_122] = "1-2-2",
59 [JESD216_MODE_144] = "1-4-4",
60 [JESD216_MODE_188] = "1-8-8",
61 [JESD216_MODE_222] = "2-2-2",
62 [JESD216_MODE_444] = "4-4-4",
63 };
64
summarize_dw1(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)65 static void summarize_dw1(const struct jesd216_param_header *php,
66 const struct jesd216_bfp *bfp)
67 {
68 uint32_t dw1 = sys_le32_to_cpu(bfp->dw1);
69
70 printf("DTR Clocking %ssupported\n",
71 (dw1 & JESD216_SFDP_BFP_DW1_DTRCLK_FLG) ? "" : "not ");
72
73 static const char *const addr_bytes[] = {
74 [JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B] = "3-Byte only",
75 [JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B4B] = "3- or 4-Byte",
76 [JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_4B] = "4-Byte only",
77 [3] = "Reserved",
78 };
79
80 printf("Addressing: %s\n", addr_bytes[(dw1 & JESD216_SFDP_BFP_DW1_ADDRBYTES_MASK)
81 >> JESD216_SFDP_BFP_DW1_ADDRBYTES_SHFT]);
82
83 static const char *const bsersz[] = {
84 [0] = "Reserved 00b",
85 [JESD216_SFDP_BFP_DW1_BSERSZ_VAL_4KSUP] = "uniform",
86 [2] = "Reserved 01b",
87 [JESD216_SFDP_BFP_DW1_BSERSZ_VAL_4KNOTSUP] = "not uniform",
88 };
89
90 printf("4-KiBy erase: %s\n", bsersz[(dw1 & JESD216_SFDP_BFP_DW1_BSERSZ_MASK)
91 >> JESD216_SFDP_BFP_DW1_BSERSZ_SHFT]);
92
93 for (size_t mode = 0; mode < ARRAY_SIZE(mode_tags); ++mode) {
94 const char *tag = mode_tags[mode];
95
96 if (tag) {
97 struct jesd216_instr cmd;
98 int rc = jesd216_bfp_read_support(php, bfp,
99 (enum jesd216_mode_type)mode,
100 &cmd);
101
102 if (rc == 0) {
103 printf("Support %s\n", tag);
104 } else if (rc > 0) {
105 printf("Support %s: instr %02Xh, %u mode clocks, %u waits\n",
106 tag, cmd.instr, cmd.mode_clocks, cmd.wait_states);
107 }
108 }
109 }
110 }
111
summarize_dw2(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)112 static void summarize_dw2(const struct jesd216_param_header *php,
113 const struct jesd216_bfp *bfp)
114 {
115 printf("Flash density: %u bytes\n", (uint32_t)(jesd216_bfp_density(bfp) / 8));
116 }
117
summarize_dw89(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)118 static void summarize_dw89(const struct jesd216_param_header *php,
119 const struct jesd216_bfp *bfp)
120 {
121 struct jesd216_erase_type etype;
122 uint32_t typ_ms;
123 int typ_max_mul;
124
125 for (uint8_t idx = 1; idx < JESD216_NUM_ERASE_TYPES; ++idx) {
126 if (jesd216_bfp_erase(bfp, idx, &etype) == 0) {
127 typ_max_mul = jesd216_bfp_erase_type_times(php, bfp,
128 idx, &typ_ms);
129
130 printf("ET%u: instr %02Xh for %u By", idx, etype.cmd,
131 (uint32_t)BIT(etype.exp));
132 if (typ_max_mul > 0) {
133 printf("; typ %u ms, max %u ms",
134 typ_ms, typ_max_mul * typ_ms);
135 }
136 printf("\n");
137 }
138 }
139 }
140
summarize_dw11(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)141 static void summarize_dw11(const struct jesd216_param_header *php,
142 const struct jesd216_bfp *bfp)
143 {
144 struct jesd216_bfp_dw11 dw11;
145
146 if (jesd216_bfp_decode_dw11(php, bfp, &dw11) != 0) {
147 return;
148 }
149
150 printf("Chip erase: typ %u ms, max %u ms\n",
151 dw11.chip_erase_ms, dw11.typ_max_factor * dw11.chip_erase_ms);
152
153 printf("Byte program: type %u + %u * B us, max %u + %u * B us\n",
154 dw11.byte_prog_first_us, dw11.byte_prog_addl_us,
155 dw11.typ_max_factor * dw11.byte_prog_first_us,
156 dw11.typ_max_factor * dw11.byte_prog_addl_us);
157
158 printf("Page program: typ %u us, max %u us\n",
159 dw11.page_prog_us,
160 dw11.typ_max_factor * dw11.page_prog_us);
161
162 printf("Page program size: %u By\n", dw11.page_size);
163 }
164
summarize_dw12(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)165 static void summarize_dw12(const struct jesd216_param_header *php,
166 const struct jesd216_bfp *bfp)
167 {
168 uint32_t dw12 = sys_le32_to_cpu(bfp->dw10[2]);
169 uint32_t dw13 = sys_le32_to_cpu(bfp->dw10[3]);
170
171 /* Inverted logic flag: 1 means not supported */
172 if ((dw12 & JESD216_SFDP_BFP_DW12_SUSPRESSUP_FLG) != 0) {
173 return;
174 }
175
176 uint8_t susp_instr = dw13 >> 24;
177 uint8_t resm_instr = dw13 >> 16;
178 uint8_t psusp_instr = dw13 >> 8;
179 uint8_t presm_instr = dw13 >> 0;
180
181 printf("Suspend: %02Xh ; Resume: %02Xh\n",
182 susp_instr, resm_instr);
183 if ((susp_instr != psusp_instr)
184 || (resm_instr != presm_instr)) {
185 printf("Program suspend: %02Xh ; Resume: %02Xh\n",
186 psusp_instr, presm_instr);
187 }
188 }
189
summarize_dw14(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)190 static void summarize_dw14(const struct jesd216_param_header *php,
191 const struct jesd216_bfp *bfp)
192 {
193 struct jesd216_bfp_dw14 dw14;
194
195 if (jesd216_bfp_decode_dw14(php, bfp, &dw14) != 0) {
196 return;
197 }
198 printf("DPD: Enter %02Xh, exit %02Xh ; delay %u ns ; poll 0x%02x\n",
199 dw14.enter_dpd_instr, dw14.exit_dpd_instr,
200 dw14.exit_delay_ns, dw14.poll_options);
201 }
202
summarize_dw15(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)203 static void summarize_dw15(const struct jesd216_param_header *php,
204 const struct jesd216_bfp *bfp)
205 {
206 struct jesd216_bfp_dw15 dw15;
207
208 if (jesd216_bfp_decode_dw15(php, bfp, &dw15) != 0) {
209 return;
210 }
211 printf("HOLD or RESET Disable: %ssupported\n",
212 dw15.hold_reset_disable ? "" : "un");
213 printf("QER: %u\n", dw15.qer);
214 if (dw15.support_044) {
215 printf("0-4-4 Mode methods: entry 0x%01x ; exit 0x%02x\n",
216 dw15.entry_044, dw15.exit_044);
217 } else {
218 printf("0-4-4 Mode: not supported\n");
219 }
220 printf("4-4-4 Mode sequences: enable 0x%02x ; disable 0x%01x\n",
221 dw15.enable_444, dw15.disable_444);
222 }
223
summarize_dw16(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)224 static void summarize_dw16(const struct jesd216_param_header *php,
225 const struct jesd216_bfp *bfp)
226 {
227 struct jesd216_bfp_dw16 dw16;
228
229 if (jesd216_bfp_decode_dw16(php, bfp, &dw16) != 0) {
230 return;
231 }
232
233 uint8_t addr_support = jesd216_bfp_addrbytes(bfp);
234
235 /* Don't display bits when 4-byte addressing is not supported. */
236 if (addr_support != JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B) {
237 printf("4-byte addressing support: enter 0x%02x, exit 0x%03x\n",
238 dw16.enter_4ba, dw16.exit_4ba);
239 }
240 printf("Soft Reset and Rescue Sequence support: 0x%02x\n",
241 dw16.srrs_support);
242 printf("Status Register 1 support: 0x%02x\n",
243 dw16.sr1_interface);
244 }
245
summarize_dw19(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)246 static void summarize_dw19(const struct jesd216_param_header *php,
247 const struct jesd216_bfp *bfp)
248 {
249 struct jesd216_bfp_dw19 dw19;
250
251 if (jesd216_bfp_decode_dw19(php, bfp, &dw19) != 0) {
252 return;
253 }
254
255 printf("Octal Enable Requirement: %u\n", dw19.octal_enable_req);
256 }
257
258 /* Indexed from 1 to match JESD216 data word numbering */
259 static const dw_extractor extractor[] = {
260 [1] = summarize_dw1,
261 [2] = summarize_dw2,
262 [8] = summarize_dw89,
263 [11] = summarize_dw11,
264 [12] = summarize_dw12,
265 [14] = summarize_dw14,
266 [15] = summarize_dw15,
267 [16] = summarize_dw16,
268 [19] = summarize_dw19,
269 };
270
dump_bfp(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp)271 static void dump_bfp(const struct jesd216_param_header *php,
272 const struct jesd216_bfp *bfp)
273 {
274 uint8_t dw = 1;
275 uint8_t limit = MIN(1U + php->len_dw, ARRAY_SIZE(extractor));
276
277 printf("Summary of BFP content:\n");
278 while (dw < limit) {
279 dw_extractor ext = extractor[dw];
280
281 if (ext != 0) {
282 ext(php, bfp);
283 }
284 ++dw;
285 }
286 }
287
dump_bytes(const struct jesd216_param_header * php,const uint32_t * dw)288 static void dump_bytes(const struct jesd216_param_header *php,
289 const uint32_t *dw)
290 {
291 char buffer[4 * 3 + 1]; /* B1 B2 B3 B4 */
292 uint8_t nw = 0;
293
294 printf(" [\n\t");
295 while (nw < php->len_dw) {
296 const uint8_t *u8p = (const uint8_t *)&dw[nw];
297 ++nw;
298
299 bool emit_nl = (nw == php->len_dw) || ((nw % 4) == 0);
300
301 sprintf(buffer, "%02x %02x %02x %02x",
302 u8p[0], u8p[1], u8p[2], u8p[3]);
303 if (emit_nl) {
304 printf("%s\n\t", buffer);
305 } else {
306 printf("%s ", buffer);
307 }
308 }
309 printf("];\n");
310 }
311
main(void)312 int main(void)
313 {
314 const struct device *const dev = DEVICE_DT_GET(FLASH_NODE);
315
316 if (!device_is_ready(dev)) {
317 printf("%s: device not ready\n", dev->name);
318 return 0;
319 }
320
321 const uint8_t decl_nph = 5;
322 union {
323 uint8_t raw[JESD216_SFDP_SIZE(decl_nph)];
324 struct jesd216_sfdp_header sfdp;
325 } u;
326 const struct jesd216_sfdp_header *hp = &u.sfdp;
327 int rc = flash_sfdp_read(dev, 0, u.raw, sizeof(u.raw));
328
329 if (rc != 0) {
330 printf("Read SFDP not supported: device not JESD216-compliant "
331 "(err %d)\n", rc);
332 return 0;
333 }
334
335 uint32_t magic = jesd216_sfdp_magic(hp);
336
337 if (magic != JESD216_SFDP_MAGIC) {
338 printf("SFDP magic %08x invalid", magic);
339 return 0;
340 }
341
342 printf("%s: SFDP v %u.%u AP %x with %u PH\n", dev->name,
343 hp->rev_major, hp->rev_minor, hp->access, 1 + hp->nph);
344
345 const struct jesd216_param_header *php = hp->phdr;
346 const struct jesd216_param_header *phpe = php + MIN(decl_nph, 1 + hp->nph);
347
348 while (php != phpe) {
349 uint16_t id = jesd216_param_id(php);
350 uint32_t addr = jesd216_param_addr(php);
351
352 printf("PH%u: %04x rev %u.%u: %u DW @ %x\n",
353 (uint32_t)(php - hp->phdr), id, php->rev_major, php->rev_minor,
354 php->len_dw, addr);
355
356 uint32_t dw[php->len_dw];
357
358 rc = flash_sfdp_read(dev, addr, dw, sizeof(dw));
359 if (rc != 0) {
360 printf("Read failed: %d\n", rc);
361 return 0;
362 }
363
364 if (id == JESD216_SFDP_PARAM_ID_BFP) {
365 const struct jesd216_bfp *bfp = (struct jesd216_bfp *)dw;
366
367 dump_bfp(php, bfp);
368 printf("size = <%u>; // bits\n", (uint32_t)jesd216_bfp_density(bfp));
369 printf("sfdp-bfp =");
370 } else {
371 printf("sfdp-%04x =", id);
372 }
373
374 dump_bytes(php, dw);
375
376 ++php;
377 }
378
379 uint8_t id[3];
380
381 rc = flash_read_jedec_id(dev, id);
382 if (rc == 0) {
383 printf("jedec-id = [%02x %02x %02x];\n",
384 id[0], id[1], id[2]);
385 } else {
386 printf("JEDEC ID read failed: %d\n", rc);
387 }
388 return 0;
389 }
390