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