1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #include <console/console.h>
4 #include <commonlib/helpers.h>
5 #include <spi_flash.h>
6 #include <spi-generic.h>
7 #include <delay.h>
8 #include <lib.h>
9
10 #include "spi_flash_internal.h"
11 #include "spi_winbond.h"
12
13 union status_reg1 {
14 uint8_t u;
15 struct {
16 uint8_t busy : 1;
17 uint8_t wel : 1;
18 uint8_t bp : 3;
19 uint8_t tb : 1;
20 uint8_t sec : 1;
21 uint8_t srp0 : 1;
22 } bp3; /* for example: W25Q128FW */
23 struct {
24 uint8_t busy : 1;
25 uint8_t wel : 1;
26 uint8_t bp : 4;
27 uint8_t tb : 1;
28 uint8_t srp0 : 1;
29 } bp4; /* for example: W25Q256J */
30 };
31
32 union status_reg2 {
33 uint8_t u;
34 struct {
35 uint8_t srp1 : 1;
36 uint8_t qe : 1;
37 uint8_t res : 1;
38 uint8_t lb : 3;
39 uint8_t cmp : 1;
40 uint8_t sus : 1;
41 };
42 };
43
44 struct status_regs {
45 union {
46 struct {
47 #if defined(__BIG_ENDIAN)
48 union status_reg2 reg2;
49 union status_reg1 reg1;
50 #else
51 union status_reg1 reg1;
52 union status_reg2 reg2;
53 #endif
54 };
55 u16 u;
56 };
57 };
58
59 static const struct spi_flash_part_id flash_table[] = {
60 {
61 /* W25P80 */
62 .id[0] = 0x2014,
63 .nr_sectors_shift = 8,
64 },
65 {
66 /* W25P16 */
67 .id[0] = 0x2015,
68 .nr_sectors_shift = 9,
69 },
70 {
71 /* W25P32 */
72 .id[0] = 0x2016,
73 .nr_sectors_shift = 10,
74 },
75 {
76 /* W25X80 */
77 .id[0] = 0x3014,
78 .nr_sectors_shift = 8,
79 .fast_read_dual_output_support = 1,
80 },
81 {
82 /* W25X16 */
83 .id[0] = 0x3015,
84 .nr_sectors_shift = 9,
85 .fast_read_dual_output_support = 1,
86 },
87 {
88 /* W25X32 */
89 .id[0] = 0x3016,
90 .nr_sectors_shift = 10,
91 .fast_read_dual_output_support = 1,
92 },
93 {
94 /* W25X64 */
95 .id[0] = 0x3017,
96 .nr_sectors_shift = 11,
97 .fast_read_dual_output_support = 1,
98 },
99 {
100 /* W25Q80_V */
101 .id[0] = 0x4014,
102 .nr_sectors_shift = 8,
103 .fast_read_dual_output_support = 1,
104 .fast_read_dual_io_support = 1,
105 },
106 {
107 /* W25Q16_V */
108 .id[0] = 0x4015,
109 .nr_sectors_shift = 9,
110 .fast_read_dual_output_support = 1,
111 .fast_read_dual_io_support = 1,
112 .protection_granularity_shift = 16,
113 .bp_bits = 3,
114 },
115 {
116 /* W25Q16DW */
117 .id[0] = 0x6015,
118 .nr_sectors_shift = 9,
119 .fast_read_dual_output_support = 1,
120 .fast_read_dual_io_support = 1,
121 .protection_granularity_shift = 16,
122 .bp_bits = 3,
123 },
124 {
125 /* W25Q32_V */
126 .id[0] = 0x4016,
127 .nr_sectors_shift = 10,
128 .fast_read_dual_output_support = 1,
129 .fast_read_dual_io_support = 1,
130 .protection_granularity_shift = 16,
131 .bp_bits = 3,
132 },
133 {
134 /* W25Q32DW */
135 .id[0] = 0x6016,
136 .nr_sectors_shift = 10,
137 .fast_read_dual_output_support = 1,
138 .fast_read_dual_io_support = 1,
139 .protection_granularity_shift = 16,
140 .bp_bits = 3,
141 },
142 {
143 /* W25Q64_V */
144 .id[0] = 0x4017,
145 .nr_sectors_shift = 11,
146 .fast_read_dual_output_support = 1,
147 .fast_read_dual_io_support = 1,
148 .protection_granularity_shift = 17,
149 .bp_bits = 3,
150 },
151 {
152 /* W25Q64DW */
153 .id[0] = 0x6017,
154 .nr_sectors_shift = 11,
155 .fast_read_dual_output_support = 1,
156 .fast_read_dual_io_support = 1,
157 .protection_granularity_shift = 17,
158 .bp_bits = 3,
159 },
160 {
161 /* W25Q64JW */
162 .id[0] = 0x8017,
163 .nr_sectors_shift = 11,
164 .fast_read_dual_output_support = 1,
165 .fast_read_dual_io_support = 1,
166 .protection_granularity_shift = 17,
167 .bp_bits = 3,
168 },
169 {
170 /* W25Q128_V */
171 .id[0] = 0x4018,
172 .nr_sectors_shift = 12,
173 .fast_read_dual_output_support = 1,
174 .fast_read_dual_io_support = 1,
175 .protection_granularity_shift = 18,
176 .bp_bits = 3,
177 },
178 {
179 /* W25Q128FW */
180 .id[0] = 0x6018,
181 .nr_sectors_shift = 12,
182 .fast_read_dual_output_support = 1,
183 .fast_read_dual_io_support = 1,
184 .protection_granularity_shift = 18,
185 .bp_bits = 3,
186 },
187 {
188 /* W25Q128J */
189 .id[0] = 0x7018,
190 .nr_sectors_shift = 12,
191 .fast_read_dual_output_support = 1,
192 .fast_read_dual_io_support = 1,
193 .protection_granularity_shift = 18,
194 .bp_bits = 3,
195 },
196 {
197 /* W25Q128JW */
198 .id[0] = 0x8018,
199 .nr_sectors_shift = 12,
200 .fast_read_dual_output_support = 1,
201 .fast_read_dual_io_support = 1,
202 .protection_granularity_shift = 18,
203 .bp_bits = 3,
204 },
205 {
206 /* W25Q512NW-IM */
207 .id[0] = 0x8020,
208 .nr_sectors_shift = 14,
209 .fast_read_dual_output_support = 1,
210 .fast_read_dual_io_support = 1,
211 .protection_granularity_shift = 16,
212 .bp_bits = 4,
213 },
214 {
215 /* W25Q256_V */
216 .id[0] = 0x4019,
217 .nr_sectors_shift = 13,
218 .fast_read_dual_output_support = 1,
219 .fast_read_dual_io_support = 1,
220 .protection_granularity_shift = 16,
221 .bp_bits = 4,
222 },
223 {
224 /* W25Q256J */
225 .id[0] = 0x7019,
226 .nr_sectors_shift = 13,
227 .fast_read_dual_output_support = 1,
228 .fast_read_dual_io_support = 1,
229 .protection_granularity_shift = 16,
230 .bp_bits = 4,
231 },
232 {
233 /* W25Q256JW */
234 .id[0] = 0x6019,
235 .nr_sectors_shift = 13,
236 .fast_read_dual_output_support = 1,
237 .fast_read_dual_io_support = 1,
238 .protection_granularity_shift = 16,
239 .bp_bits = 4,
240 },
241 {
242 /* W25Q256JW_DTR */
243 .id[0] = 0x8019,
244 .nr_sectors_shift = 13,
245 .fast_read_dual_output_support = 1,
246 .fast_read_dual_io_support = 1,
247 .protection_granularity_shift = 16,
248 .bp_bits = 4,
249 },
250 };
251
252 /*
253 * Convert BPx, TB and CMP to a region.
254 * SEC (if available) must be zero.
255 */
winbond_bpbits_to_region(const size_t granularity,const struct spi_flash_bpbits * bits,const size_t flash_size,struct region * out)256 static void winbond_bpbits_to_region(const size_t granularity,
257 const struct spi_flash_bpbits *bits,
258 const size_t flash_size,
259 struct region *out)
260 {
261 size_t protected_size =
262 MIN(bits->bp ? granularity << (bits->bp - 1) : 0, flash_size);
263
264 int tb = bits->tb;
265 if (bits->cmp) {
266 protected_size = flash_size - protected_size;
267 tb = !tb;
268 }
269
270 out->offset = tb ? 0 : flash_size - protected_size;
271 out->size = protected_size;
272 }
273
274 /*
275 * Available on all devices.
276 * Read block protect bits from Status/Status2 Reg.
277 * Converts block protection bits to a region.
278 *
279 * Returns:
280 * -1 on error
281 * 1 if region is covered by write protection
282 * 0 if a part of region isn't covered by write protection
283 */
winbond_get_write_protection(const struct spi_flash * flash,const struct region * region)284 static int winbond_get_write_protection(const struct spi_flash *flash,
285 const struct region *region)
286 {
287 const struct spi_flash_part_id *params;
288 struct region wp_region;
289 struct spi_flash_bpbits bpbits;
290 int ret;
291
292 params = flash->part;
293
294 if (!params)
295 return -1;
296
297 const size_t granularity = (1 << params->protection_granularity_shift);
298
299 union status_reg1 reg1 = { .u = 0 };
300 union status_reg2 reg2 = { .u = 0 };
301
302 ret = spi_flash_cmd(&flash->spi, flash->status_cmd, ®1.u,
303 sizeof(reg1.u));
304 if (ret)
305 return ret;
306
307 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, ®2.u,
308 sizeof(reg2.u));
309 if (ret)
310 return ret;
311
312 if (params->bp_bits == 3) {
313 if (reg1.bp3.sec) {
314 // FIXME: not supported
315 return -1;
316 }
317
318 bpbits = (struct spi_flash_bpbits){
319 .bp = reg1.bp3.bp,
320 .cmp = reg2.cmp,
321 .tb = reg1.bp3.tb,
322 /*
323 * For W25Q*{,F}* parts:
324 * srp1 srp0
325 * 0 0 | writable if WEL==1
326 * 0 1 | writable if WEL==1 && #WP==Vcc
327 * 1 0 | not writable until next power-down
328 * 1 1 | not writable, permanently
329 *
330 * checked datasheets: W25Q128FV, (W25Q80, W25Q16,
331 * W25Q32)
332 */
333 .winbond = {
334 .srp0 = reg1.bp3.srp0,
335 .srp1 = reg2.srp1,
336 },
337 };
338 } else if (params->bp_bits == 4) {
339 bpbits = (struct spi_flash_bpbits){
340 .bp = reg1.bp4.bp,
341 .cmp = reg2.cmp,
342 .tb = reg1.bp4.tb,
343 /*
344 * For W25Q*{J,D}* parts:
345 *
346 * srp1 srp0
347 * 0 0 | writable if WEL==1
348 * 0 1 | writable if WEL==1 && #WP==Vcc
349 * 1 x | not writable until next power-down
350 *
351 * checked datasheets: W25Q132JW, W25Q128JW, W25Q256JV.
352 * W25Q16DW
353 *
354 * The srp0/srp1 bits got renamed to srp/srl in the
355 * datasheets, we retain the prior naming
356 * convention for the structs though.
357 */
358 .winbond = {
359 .srp0 = reg1.bp4.srp0,
360 .srp1 = reg2.srp1,
361 },
362 };
363 } else {
364 // FIXME: not supported
365 return -1;
366 }
367
368 winbond_bpbits_to_region(granularity, &bpbits, flash->size,
369 &wp_region);
370
371 if (!region_sz(&wp_region)) {
372 printk(BIOS_DEBUG, "WINBOND: flash isn't protected\n");
373
374 return 0;
375 }
376
377 printk(BIOS_DEBUG, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
378 region_offset(&wp_region), region_end(&wp_region));
379
380 return region_is_subregion(&wp_region, region);
381 }
382
383 /**
384 * Common method to write some bit of the status register 1 & 2 at the same
385 * time. Only change bits that are one in @mask.
386 * Compare the final result to make sure that the register isn't locked.
387 *
388 * @param mask: The bits that are affected by @val
389 * @param val: The bits to write
390 * @param non_volatile: Make setting permanent
391 *
392 * @return 0 on success
393 */
winbond_flash_cmd_status(const struct spi_flash * flash,const u16 mask,const u16 val,const bool non_volatile)394 static int winbond_flash_cmd_status(const struct spi_flash *flash,
395 const u16 mask,
396 const u16 val,
397 const bool non_volatile)
398 {
399 struct {
400 u8 cmd;
401 u16 sreg;
402 } __packed cmdbuf;
403 u8 reg8;
404 int ret;
405
406 if (!flash)
407 return -1;
408
409 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, ®8, sizeof(reg8));
410 if (ret)
411 return ret;
412
413 cmdbuf.sreg = reg8;
414
415 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, ®8, sizeof(reg8));
416 if (ret)
417 return ret;
418
419 cmdbuf.sreg |= reg8 << 8;
420
421 if ((val & mask) == (cmdbuf.sreg & mask))
422 return 0;
423
424 if (non_volatile) {
425 ret = spi_flash_cmd(&flash->spi, CMD_W25_WREN, NULL, 0);
426 } else {
427 ret = spi_flash_cmd(&flash->spi, CMD_VOLATILE_SREG_WREN, NULL,
428 0);
429 }
430 if (ret)
431 return ret;
432
433 cmdbuf.sreg &= ~mask;
434 cmdbuf.sreg |= val & mask;
435 cmdbuf.cmd = CMD_W25_WRSR;
436
437 /* Legacy method of writing status register 1 & 2 */
438 ret = spi_flash_cmd_write(&flash->spi, (u8 *)&cmdbuf, sizeof(cmdbuf),
439 NULL, 0);
440 if (ret)
441 return ret;
442
443 if (non_volatile) {
444 /* Wait tw */
445 ret = spi_flash_cmd_wait_ready(flash, WINBOND_FLASH_TIMEOUT);
446 if (ret)
447 return ret;
448 } else {
449 /* Wait tSHSL */
450 udelay(1);
451 }
452
453 /* Now read the status register to make sure it's not locked */
454 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, ®8, sizeof(reg8));
455 if (ret)
456 return ret;
457
458 cmdbuf.sreg = reg8;
459
460 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, ®8, sizeof(reg8));
461 if (ret)
462 return ret;
463
464 cmdbuf.sreg |= reg8 << 8;
465
466 printk(BIOS_DEBUG, "WINBOND: SREG=%02x SREG2=%02x\n",
467 cmdbuf.sreg & 0xff,
468 cmdbuf.sreg >> 8);
469
470 /* Compare against expected result */
471 if ((val & mask) != (cmdbuf.sreg & mask)) {
472 printk(BIOS_ERR, "WINBOND: SREG is locked!\n");
473 ret = -1;
474 }
475
476 return ret;
477 }
478
479 /*
480 * Available on all devices.
481 * Protect a region starting from start of flash or end of flash.
482 * The caller must provide a supported protected region size.
483 * SEC isn't supported and set to zero.
484 * Write block protect bits to Status/Status2 Reg.
485 * Optionally lock the status register if lock_sreg is set with the provided
486 * mode.
487 *
488 * @param flash: The flash to operate on
489 * @param region: The region to write protect
490 * @param mode: Optional status register lock-down mode
491 *
492 * @return 0 on success
493 */
494 static int
winbond_set_write_protection(const struct spi_flash * flash,const struct region * region,const enum spi_flash_status_reg_lockdown mode)495 winbond_set_write_protection(const struct spi_flash *flash,
496 const struct region *region,
497 const enum spi_flash_status_reg_lockdown mode)
498 {
499 const struct spi_flash_part_id *params;
500 struct status_regs mask, val;
501 struct region wp_region;
502 u8 cmp, bp, tb;
503 int ret;
504
505 /* Need to touch TOP or BOTTOM */
506 if (region_offset(region) != 0 && region_end(region) != flash->size)
507 return -1;
508
509 params = flash->part;
510
511 if (!params)
512 return -1;
513
514 if (params->bp_bits != 3 && params->bp_bits != 4) {
515 /* FIXME: not implemented */
516 return -1;
517 }
518
519 wp_region = *region;
520
521 if (region_offset(&wp_region) == 0)
522 tb = 1;
523 else
524 tb = 0;
525
526 if (region_sz(&wp_region) > flash->size / 2) {
527 cmp = 1;
528 wp_region.offset = tb ? 0 : region_sz(&wp_region);
529 wp_region.size = flash->size - region_sz(&wp_region);
530 tb = !tb;
531 } else {
532 cmp = 0;
533 }
534
535 if (region_sz(&wp_region) == 0) {
536 bp = 0;
537 } else if (IS_POWER_OF_2(region_sz(&wp_region)) &&
538 (region_sz(&wp_region) >=
539 (1 << params->protection_granularity_shift))) {
540 bp = log2(region_sz(&wp_region)) -
541 params->protection_granularity_shift + 1;
542 } else {
543 printk(BIOS_ERR, "WINBOND: ERROR: unsupported region size\n");
544 return -1;
545 }
546
547 /* Write block protection bits */
548
549 if (params->bp_bits == 3) {
550 val.reg1 = (union status_reg1) {
551 .bp3 = { .bp = bp, .tb = tb, .sec = 0 }
552 };
553 mask.reg1 = (union status_reg1) {
554 .bp3 = { .bp = ~0, .tb = 1, .sec = 1 }
555 };
556 } else {
557 val.reg1 = (union status_reg1) {
558 .bp4 = { .bp = bp, .tb = tb }
559 };
560 mask.reg1 = (union status_reg1) {
561 .bp4 = { .bp = ~0, .tb = 1 }
562 };
563 }
564
565 val.reg2 = (union status_reg2) { .cmp = cmp };
566 mask.reg2 = (union status_reg2) { .cmp = 1 };
567
568 if (mode != SPI_WRITE_PROTECTION_PRESERVE) {
569 u8 srp;
570 switch (mode) {
571 case SPI_WRITE_PROTECTION_NONE:
572 srp = 0;
573 break;
574 case SPI_WRITE_PROTECTION_PIN:
575 srp = 1;
576 break;
577 case SPI_WRITE_PROTECTION_REBOOT:
578 srp = 2;
579 break;
580 case SPI_WRITE_PROTECTION_PERMANENT:
581 srp = 3;
582 break;
583 default:
584 return -1;
585 }
586
587 if (params->bp_bits == 3) {
588 val.reg1.bp3.srp0 = !!(srp & 1);
589 mask.reg1.bp3.srp0 = 1;
590 } else {
591 val.reg1.bp4.srp0 = !!(srp & 1);
592 mask.reg1.bp4.srp0 = 1;
593 }
594
595 val.reg2.srp1 = !!(srp & 2);
596 mask.reg2.srp1 = 1;
597 }
598
599 ret = winbond_flash_cmd_status(flash, mask.u, val.u, true);
600 if (ret)
601 return ret;
602
603 printk(BIOS_DEBUG, "WINBOND: write-protection set to range "
604 "0x%08zx-0x%08zx\n", region_offset(region), region_end(region));
605
606 return ret;
607 }
608
609 static const struct spi_flash_protection_ops spi_flash_protection_ops = {
610 .get_write = winbond_get_write_protection,
611 .set_write = winbond_set_write_protection,
612 };
613
614 const struct spi_flash_vendor_info spi_flash_winbond_vi = {
615 .id = VENDOR_ID_WINBOND,
616 .page_size_shift = 8,
617 .sector_size_kib_shift = 2,
618 .match_id_mask[0] = 0xffff,
619 .ids = flash_table,
620 .nr_part_ids = ARRAY_SIZE(flash_table),
621 .desc = &spi_flash_pp_0x20_sector_desc,
622 .prot_ops = &spi_flash_protection_ops,
623 };
624