1 /*
2  * Copyright (c) 2020 Peter Bigot Consulting, LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #include <sys/types.h>
9 #include <zephyr/kernel.h>
10 #include "jesd216.h"
11 #include "spi_nor.h"
12 
extract_instr(uint16_t packed,struct jesd216_instr * res)13 static bool extract_instr(uint16_t packed,
14 			  struct jesd216_instr *res)
15 {
16 	bool rv = (res != NULL);
17 
18 	if (rv) {
19 		res->instr = packed >> 8;
20 		res->mode_clocks = (packed >> 5) & 0x07;
21 		res->wait_states = packed & 0x1F;
22 	}
23 	return rv;
24 }
25 
jesd216_bfp_read_support(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,enum jesd216_mode_type mode,struct jesd216_instr * res)26 int jesd216_bfp_read_support(const struct jesd216_param_header *php,
27 			     const struct jesd216_bfp *bfp,
28 			     enum jesd216_mode_type mode,
29 			     struct jesd216_instr *res)
30 {
31 	int rv = -ENOTSUP;
32 
33 	switch (mode) {
34 	case JESD216_MODE_044:
35 		if ((php->len_dw >= 15)
36 		    && (sys_le32_to_cpu(bfp->dw10[5]) & BIT(9))) {
37 			rv = 0;
38 		}
39 		break;
40 	case JESD216_MODE_088:
41 		if ((php->len_dw >= 19)
42 		    && (sys_le32_to_cpu(bfp->dw10[9]) & BIT(9))) {
43 			rv = 0;
44 		}
45 		break;
46 	case JESD216_MODE_111:
47 		rv = 0;
48 		break;
49 	case JESD216_MODE_112:
50 		if (sys_le32_to_cpu(bfp->dw1) & BIT(16)) {
51 			uint32_t dw4 = sys_le32_to_cpu(bfp->dw4);
52 
53 			rv = extract_instr(dw4 >> 0, res);
54 		}
55 		break;
56 	case JESD216_MODE_114:
57 		if (sys_le32_to_cpu(bfp->dw1) & BIT(22)) {
58 			uint32_t dw3 = sys_le32_to_cpu(bfp->dw3);
59 
60 			rv = extract_instr(dw3 >> 16, res);
61 		}
62 		break;
63 	case JESD216_MODE_118:
64 		if (php->len_dw >= 17) {
65 			uint32_t dw17 = sys_le32_to_cpu(bfp->dw10[7]);
66 
67 			if ((dw17 >> 24) != 0) {
68 				rv = extract_instr(dw17 >> 16, res);
69 			}
70 		}
71 		break;
72 	case JESD216_MODE_122:
73 		if (sys_le32_to_cpu(bfp->dw1) & BIT(20)) {
74 			uint32_t dw4 = sys_le32_to_cpu(bfp->dw4);
75 
76 			rv = extract_instr(dw4 >> 16, res);
77 		}
78 		break;
79 	case JESD216_MODE_144:
80 		if (sys_le32_to_cpu(bfp->dw1) & BIT(21)) {
81 			uint32_t dw3 = sys_le32_to_cpu(bfp->dw3);
82 
83 			rv = extract_instr(dw3 >> 0, res);
84 		}
85 		break;
86 	case JESD216_MODE_188:
87 		if (php->len_dw >= 17) {
88 			uint32_t dw17 = sys_le32_to_cpu(bfp->dw10[7]);
89 
90 			if ((uint8_t)(dw17 >> 8) != 0) {
91 				rv = extract_instr(dw17 >> 0, res);
92 			}
93 		}
94 		break;
95 	case JESD216_MODE_222:
96 		if (sys_le32_to_cpu(bfp->dw5) & BIT(0)) {
97 			uint32_t dw6 = sys_le32_to_cpu(bfp->dw6);
98 
99 			rv = extract_instr(dw6 >> 16, res);
100 		}
101 		break;
102 	case JESD216_MODE_444:
103 		if (sys_le32_to_cpu(bfp->dw5) & BIT(4)) {
104 			uint32_t dw7 = sys_le32_to_cpu(bfp->dw7);
105 
106 			rv = extract_instr(dw7 >> 16, res);
107 		}
108 		break;
109 	/* Not clear how to detect these; they are identified only by
110 	 * enable/disable sequences.
111 	 */
112 	case JESD216_MODE_44D4D:
113 	case JESD216_MODE_888:
114 	case JESD216_MODE_8D8D8D:
115 		break;
116 	default:
117 		rv = -EINVAL;
118 	}
119 
120 	return rv;
121 }
122 
jesd216_bfp_erase(const struct jesd216_bfp * bfp,uint8_t idx,struct jesd216_erase_type * etp)123 int jesd216_bfp_erase(const struct jesd216_bfp *bfp,
124 		       uint8_t idx,
125 		       struct jesd216_erase_type *etp)
126 {
127 	__ASSERT_NO_MSG((idx > 0) && (idx <= JESD216_NUM_ERASE_TYPES));
128 
129 	/* Types 1 and 2 are in dw8, types 3 and 4 in dw9 */
130 	const uint32_t *dwp = &bfp->dw8 + (idx - 1U) / 2U;
131 	uint32_t dw = sys_le32_to_cpu(*dwp);
132 
133 	/* Type 2(4) is in the upper half of the value. */
134 	if ((idx & 0x01) == 0x00) {
135 		dw >>= 16;
136 	}
137 
138 	/* Extract the exponent and command */
139 	uint8_t exp = (uint8_t)dw;
140 	uint8_t cmd = (uint8_t)(dw >> 8);
141 
142 	if (exp == 0) {
143 		return -EINVAL;
144 	}
145 	etp->cmd = cmd;
146 	etp->exp = exp;
147 	return 0;
148 }
149 
jesd216_bfp_erase_type_times(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,uint8_t idx,uint32_t * typ_ms)150 int jesd216_bfp_erase_type_times(const struct jesd216_param_header *php,
151 				 const struct jesd216_bfp *bfp,
152 				 uint8_t idx,
153 				 uint32_t *typ_ms)
154 {
155 	__ASSERT_NO_MSG((idx > 0) && (idx <= JESD216_NUM_ERASE_TYPES));
156 
157 	/* DW10 introduced in JESD216A */
158 	if (php->len_dw < 10) {
159 		return -ENOTSUP;
160 	}
161 
162 	uint32_t dw10 = sys_le32_to_cpu(bfp->dw10[0]);
163 
164 	/* Each 7-bit erase time entry has a 5-bit count in the lower
165 	 * bits, and a 2-bit unit in the upper bits.  The actual count
166 	 * is the field content plus one.
167 	 *
168 	 * The entries start with ET1 at bit 4.  The low four bits
169 	 * encode a value that is offset and scaled to produce a
170 	 * multiplier to convert from typical time to maximum time.
171 	 */
172 	unsigned int count = 1 + ((dw10 >> (4 + (idx - 1) * 7)) & 0x1F);
173 	unsigned int units = ((dw10 >> (4 + 5 + (idx - 1) * 7)) & 0x03);
174 	unsigned int max_factor = 2 * (1 + (dw10 & 0x0F));
175 
176 	switch (units) {
177 	case 0x00:		/* 1 ms */
178 		*typ_ms = count;
179 		break;
180 	case 0x01:		/* 16 ms */
181 		*typ_ms = count * 16;
182 		break;
183 	case 0x02:		/* 128 ms */
184 		*typ_ms = count * 128;
185 		break;
186 	case 0x03:		/* 1 s */
187 		*typ_ms = count * MSEC_PER_SEC;
188 		break;
189 	}
190 
191 	return max_factor;
192 }
193 
jesd216_bfp_decode_dw11(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,struct jesd216_bfp_dw11 * res)194 int jesd216_bfp_decode_dw11(const struct jesd216_param_header *php,
195 			    const struct jesd216_bfp *bfp,
196 			    struct jesd216_bfp_dw11 *res)
197 {
198 	/* DW11 introduced in JESD216A */
199 	if (php->len_dw < 11) {
200 		return -ENOTSUP;
201 	}
202 
203 	uint32_t dw11 = sys_le32_to_cpu(bfp->dw10[1]);
204 	uint32_t value = 1 + ((dw11 >> 24) & 0x1F);
205 
206 	switch ((dw11 >> 29) & 0x03) {
207 	case 0x00: /* 16 ms */
208 		value *= 16;
209 		break;
210 	case 0x01:
211 		value *= 256;
212 		break;
213 	case 0x02:
214 		value *= 4 * MSEC_PER_SEC;
215 		break;
216 	case 0x03:
217 		value *= 64 * MSEC_PER_SEC;
218 		break;
219 	}
220 	res->chip_erase_ms = value;
221 
222 	value = 1 + ((dw11 >> 19) & 0x0F);
223 	if (dw11 & BIT(23)) {
224 		value *= 8;
225 	}
226 	res->byte_prog_addl_us = value;
227 
228 	value = 1 + ((dw11 >> 14) & 0x0F);
229 	if (dw11 & BIT(18)) {
230 		value *= 8;
231 	}
232 	res->byte_prog_first_us = value;
233 
234 	value = 1 + ((dw11 >> 8) & 0x01F);
235 	if (dw11 & BIT(13)) {
236 		value *= 64;
237 	} else {
238 		value *= 8;
239 	}
240 	res->page_prog_us = value;
241 
242 	res->page_size = BIT((dw11 >> 4) & 0x0F);
243 	res->typ_max_factor = 2 * (1 + (dw11 & 0x0F));
244 
245 	return 0;
246 }
247 
jesd216_bfp_decode_dw14(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,struct jesd216_bfp_dw14 * res)248 int jesd216_bfp_decode_dw14(const struct jesd216_param_header *php,
249 			    const struct jesd216_bfp *bfp,
250 			    struct jesd216_bfp_dw14 *res)
251 {
252 	/* DW14 introduced in JESD216A */
253 	if (php->len_dw < 14) {
254 		return -ENOTSUP;
255 	}
256 
257 	uint32_t dw14 = sys_le32_to_cpu(bfp->dw10[4]);
258 
259 	if (dw14 & BIT(31)) {
260 		return -ENOTSUP;
261 	}
262 
263 	res->enter_dpd_instr = (dw14 >> 23) & 0xFF;
264 	res->exit_dpd_instr = (dw14 >> 15) & 0xFF;
265 
266 	uint32_t value = 1 + ((dw14 >> 8) & 0x1F);
267 
268 	switch ((dw14 >> 13) & 0x03) {
269 	case 0x00: /* 128 ns */
270 		value *= 128;
271 		break;
272 	case 0x01: /* 1 us */
273 		value *= NSEC_PER_USEC;
274 		break;
275 	case 0x02: /* 8 us */
276 		value *= 8 * NSEC_PER_USEC;
277 		break;
278 	case 0x03: /* 64 us */
279 		value *= 64 * NSEC_PER_USEC;
280 		break;
281 	}
282 
283 	res->exit_delay_ns = value;
284 
285 	res->poll_options = (dw14 >> 2) & 0x3F;
286 
287 	return 0;
288 }
289 
jesd216_bfp_decode_dw15(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,struct jesd216_bfp_dw15 * res)290 int jesd216_bfp_decode_dw15(const struct jesd216_param_header *php,
291 			    const struct jesd216_bfp *bfp,
292 			    struct jesd216_bfp_dw15 *res)
293 {
294 	/* DW15 introduced in JESD216A */
295 	if (php->len_dw < 15) {
296 		return -ENOTSUP;
297 	}
298 
299 	uint32_t dw15 = sys_le32_to_cpu(bfp->dw10[5]);
300 
301 	res->hold_reset_disable = (dw15 & BIT(23)) != 0U;
302 	res->qer = (dw15 >> 20) & 0x07;
303 	res->entry_044 = (dw15 >> 16) & 0x0F;
304 	res->exit_044 = (dw15 >> 10) & 0x3F;
305 	res->support_044 = (dw15 & BIT(9)) != 0U;
306 	res->enable_444 = (dw15 >> 4) & 0x1F;
307 	res->disable_444 = (dw15 >> 0) & 0x0F;
308 
309 	return 0;
310 }
311 
jesd216_bfp_decode_dw16(const struct jesd216_param_header * php,const struct jesd216_bfp * bfp,struct jesd216_bfp_dw16 * res)312 int jesd216_bfp_decode_dw16(const struct jesd216_param_header *php,
313 			    const struct jesd216_bfp *bfp,
314 			    struct jesd216_bfp_dw16 *res)
315 {
316 	/* DW16 introduced in JESD216A */
317 	if (php->len_dw < 16) {
318 		return -ENOTSUP;
319 	}
320 
321 	uint32_t dw16 = sys_le32_to_cpu(bfp->dw10[6]);
322 
323 	res->enter_4ba = (dw16 >> 24) & 0xFF;
324 	res->exit_4ba = (dw16 >> 14) & 0x3FF;
325 	res->srrs_support = (dw16 >> 8) & 0x3F;
326 	res->sr1_interface = (dw16 >> 0) & 0x7F;
327 
328 	return 0;
329 }
330