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