soc/intel/alderlake: Add ADL-P 4+4 with 28W TDP
[coreboot.git] / src / drivers / spi / winbond.c
blob32bcc791277c4424e78c35bd7fdbeb95ddd8788a
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
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>
10 #include "spi_flash_internal.h"
11 #include "spi_winbond.h"
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;
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;
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;
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
55 u16 u;
59 static const struct spi_flash_part_id flash_table[] = {
61 /* W25P80 */
62 .id[0] = 0x2014,
63 .nr_sectors_shift = 8,
66 /* W25P16 */
67 .id[0] = 0x2015,
68 .nr_sectors_shift = 9,
71 /* W25P32 */
72 .id[0] = 0x2016,
73 .nr_sectors_shift = 10,
76 /* W25X80 */
77 .id[0] = 0x3014,
78 .nr_sectors_shift = 8,
79 .fast_read_dual_output_support = 1,
82 /* W25X16 */
83 .id[0] = 0x3015,
84 .nr_sectors_shift = 9,
85 .fast_read_dual_output_support = 1,
88 /* W25X32 */
89 .id[0] = 0x3016,
90 .nr_sectors_shift = 10,
91 .fast_read_dual_output_support = 1,
94 /* W25X64 */
95 .id[0] = 0x3017,
96 .nr_sectors_shift = 11,
97 .fast_read_dual_output_support = 1,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
244 * Convert BPx, TB and CMP to a region.
245 * SEC (if available) must be zero.
247 static void winbond_bpbits_to_region(const size_t granularity,
248 const u8 bp,
249 bool tb,
250 const bool cmp,
251 const size_t flash_size,
252 struct region *out)
254 size_t protected_size =
255 MIN(bp ? granularity << (bp - 1) : 0, flash_size);
257 if (cmp) {
258 protected_size = flash_size - protected_size;
259 tb = !tb;
262 out->offset = tb ? 0 : flash_size - protected_size;
263 out->size = protected_size;
267 * Available on all devices.
268 * Read block protect bits from Status/Status2 Reg.
269 * Converts block protection bits to a region.
271 * Returns:
272 * -1 on error
273 * 1 if region is covered by write protection
274 * 0 if a part of region isn't covered by write protection
276 static int winbond_get_write_protection(const struct spi_flash *flash,
277 const struct region *region)
279 const struct spi_flash_part_id *params;
280 struct region wp_region;
281 union status_reg2 reg2;
282 u8 bp, tb;
283 int ret;
285 params = flash->part;
287 if (!params)
288 return -1;
290 const size_t granularity = (1 << params->protection_granularity_shift);
292 union status_reg1 reg1 = { .u = 0 };
294 ret = spi_flash_cmd(&flash->spi, flash->status_cmd, &reg1.u,
295 sizeof(reg1.u));
296 if (ret)
297 return ret;
299 if (params->bp_bits == 3) {
300 if (reg1.bp3.sec) {
301 // FIXME: not supported
302 return -1;
305 bp = reg1.bp3.bp;
306 tb = reg1.bp3.tb;
307 } else if (params->bp_bits == 4) {
308 bp = reg1.bp4.bp;
309 tb = reg1.bp4.tb;
310 } else {
311 // FIXME: not supported
312 return -1;
315 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg2.u,
316 sizeof(reg2.u));
317 if (ret)
318 return ret;
320 winbond_bpbits_to_region(granularity, bp, tb, reg2.cmp, flash->size,
321 &wp_region);
323 if (!region_sz(&wp_region)) {
324 printk(BIOS_DEBUG, "WINBOND: flash isn't protected\n");
326 return 0;
329 printk(BIOS_DEBUG, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
330 region_offset(&wp_region), region_end(&wp_region));
332 return region_is_subregion(&wp_region, region);
336 * Common method to write some bit of the status register 1 & 2 at the same
337 * time. Only change bits that are one in @mask.
338 * Compare the final result to make sure that the register isn't locked.
340 * @param mask: The bits that are affected by @val
341 * @param val: The bits to write
342 * @param non_volatile: Make setting permanent
344 * @return 0 on success
346 static int winbond_flash_cmd_status(const struct spi_flash *flash,
347 const u16 mask,
348 const u16 val,
349 const bool non_volatile)
351 struct {
352 u8 cmd;
353 u16 sreg;
354 } __packed cmdbuf;
355 u8 reg8;
356 int ret;
358 if (!flash)
359 return -1;
361 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
362 if (ret)
363 return ret;
365 cmdbuf.sreg = reg8;
367 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
368 if (ret)
369 return ret;
371 cmdbuf.sreg |= reg8 << 8;
373 if ((val & mask) == (cmdbuf.sreg & mask))
374 return 0;
376 if (non_volatile) {
377 ret = spi_flash_cmd(&flash->spi, CMD_W25_WREN, NULL, 0);
378 } else {
379 ret = spi_flash_cmd(&flash->spi, CMD_VOLATILE_SREG_WREN, NULL,
382 if (ret)
383 return ret;
385 cmdbuf.sreg &= ~mask;
386 cmdbuf.sreg |= val & mask;
387 cmdbuf.cmd = CMD_W25_WRSR;
389 /* Legacy method of writing status register 1 & 2 */
390 ret = spi_flash_cmd_write(&flash->spi, (u8 *)&cmdbuf, sizeof(cmdbuf),
391 NULL, 0);
392 if (ret)
393 return ret;
395 if (non_volatile) {
396 /* Wait tw */
397 ret = spi_flash_cmd_wait_ready(flash, WINBOND_FLASH_TIMEOUT);
398 if (ret)
399 return ret;
400 } else {
401 /* Wait tSHSL */
402 udelay(1);
405 /* Now read the status register to make sure it's not locked */
406 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
407 if (ret)
408 return ret;
410 cmdbuf.sreg = reg8;
412 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
413 if (ret)
414 return ret;
416 cmdbuf.sreg |= reg8 << 8;
418 printk(BIOS_DEBUG, "WINBOND: SREG=%02x SREG2=%02x\n",
419 cmdbuf.sreg & 0xff,
420 cmdbuf.sreg >> 8);
422 /* Compare against expected result */
423 if ((val & mask) != (cmdbuf.sreg & mask)) {
424 printk(BIOS_ERR, "WINBOND: SREG is locked!\n");
425 ret = -1;
428 return ret;
432 * Available on all devices.
433 * Protect a region starting from start of flash or end of flash.
434 * The caller must provide a supported protected region size.
435 * SEC isn't supported and set to zero.
436 * Write block protect bits to Status/Status2 Reg.
437 * Optionally lock the status register if lock_sreg is set with the provided
438 * mode.
440 * @param flash: The flash to operate on
441 * @param region: The region to write protect
442 * @param mode: Optional status register lock-down mode
444 * @return 0 on success
446 static int
447 winbond_set_write_protection(const struct spi_flash *flash,
448 const struct region *region,
449 const enum spi_flash_status_reg_lockdown mode)
451 const struct spi_flash_part_id *params;
452 struct status_regs mask, val;
453 struct region wp_region;
454 u8 cmp, bp, tb;
455 int ret;
457 /* Need to touch TOP or BOTTOM */
458 if (region_offset(region) != 0 && region_end(region) != flash->size)
459 return -1;
461 params = flash->part;
463 if (!params)
464 return -1;
466 if (params->bp_bits != 3 && params->bp_bits != 4) {
467 /* FIXME: not implemented */
468 return -1;
471 wp_region = *region;
473 if (region_offset(&wp_region) == 0)
474 tb = 1;
475 else
476 tb = 0;
478 if (region_sz(&wp_region) > flash->size / 2) {
479 cmp = 1;
480 wp_region.offset = tb ? 0 : region_sz(&wp_region);
481 wp_region.size = flash->size - region_sz(&wp_region);
482 tb = !tb;
483 } else {
484 cmp = 0;
487 if (region_sz(&wp_region) == 0) {
488 bp = 0;
489 } else if (IS_POWER_OF_2(region_sz(&wp_region)) &&
490 (region_sz(&wp_region) >=
491 (1 << params->protection_granularity_shift))) {
492 bp = log2(region_sz(&wp_region)) -
493 params->protection_granularity_shift + 1;
494 } else {
495 printk(BIOS_ERR, "WINBOND: ERROR: unsupported region size\n");
496 return -1;
499 /* Write block protection bits */
501 if (params->bp_bits == 3) {
502 val.reg1 = (union status_reg1) {
503 .bp3 = { .bp = bp, .tb = tb, .sec = 0 }
505 mask.reg1 = (union status_reg1) {
506 .bp3 = { .bp = ~0, .tb = 1, .sec = 1 }
508 } else {
509 val.reg1 = (union status_reg1) {
510 .bp4 = { .bp = bp, .tb = tb }
512 mask.reg1 = (union status_reg1) {
513 .bp4 = { .bp = ~0, .tb = 1 }
517 val.reg2 = (union status_reg2) { .cmp = cmp };
518 mask.reg2 = (union status_reg2) { .cmp = 1 };
520 if (mode != SPI_WRITE_PROTECTION_PRESERVE) {
521 u8 srp;
522 switch (mode) {
523 case SPI_WRITE_PROTECTION_NONE:
524 srp = 0;
525 break;
526 case SPI_WRITE_PROTECTION_PIN:
527 srp = 1;
528 break;
529 case SPI_WRITE_PROTECTION_REBOOT:
530 srp = 2;
531 break;
532 case SPI_WRITE_PROTECTION_PERMANENT:
533 srp = 3;
534 break;
535 default:
536 return -1;
539 if (params->bp_bits == 3) {
540 val.reg1.bp3.srp0 = !!(srp & 1);
541 mask.reg1.bp3.srp0 = 1;
542 } else {
543 val.reg1.bp4.srp0 = !!(srp & 1);
544 mask.reg1.bp4.srp0 = 1;
547 val.reg2.srp1 = !!(srp & 2);
548 mask.reg2.srp1 = 1;
551 ret = winbond_flash_cmd_status(flash, mask.u, val.u, true);
552 if (ret)
553 return ret;
555 printk(BIOS_DEBUG, "WINBOND: write-protection set to range "
556 "0x%08zx-0x%08zx\n", region_offset(region), region_end(region));
558 return ret;
561 static const struct spi_flash_protection_ops spi_flash_protection_ops = {
562 .get_write = winbond_get_write_protection,
563 .set_write = winbond_set_write_protection,
566 const struct spi_flash_vendor_info spi_flash_winbond_vi = {
567 .id = VENDOR_ID_WINBOND,
568 .page_size_shift = 8,
569 .sector_size_kib_shift = 2,
570 .match_id_mask[0] = 0xffff,
571 .ids = flash_table,
572 .nr_part_ids = ARRAY_SIZE(flash_table),
573 .desc = &spi_flash_pp_0x20_sector_desc,
574 .prot_ops = &spi_flash_protection_ops,