1 /* This file is part of the coreboot project. */
2 /* SPDX-License-Identifier: GPL-2.0-or-later */
4 #include <console/console.h>
5 #include <commonlib/helpers.h>
7 #include <spi-generic.h>
12 #include "spi_flash_internal.h"
13 #include "spi_winbond.h"
15 union status_reg1_bp3
{
27 union status_reg1_bp4
{
53 #if defined(__BIG_ENDIAN)
54 union status_reg2 reg2
;
56 union status_reg1_bp3 reg1_bp3
;
57 union status_reg1_bp4 reg1_bp4
;
61 union status_reg1_bp3 reg1_bp3
;
62 union status_reg1_bp4 reg1_bp4
;
64 union status_reg2 reg2
;
71 static const struct spi_flash_part_id flash_table
[] = {
75 .nr_sectors_shift
= 8,
80 .nr_sectors_shift
= 9,
85 .nr_sectors_shift
= 10,
90 .nr_sectors_shift
= 8,
91 .fast_read_dual_output_support
= 1,
96 .nr_sectors_shift
= 9,
97 .fast_read_dual_output_support
= 1,
102 .nr_sectors_shift
= 10,
103 .fast_read_dual_output_support
= 1,
108 .nr_sectors_shift
= 11,
109 .fast_read_dual_output_support
= 1,
114 .nr_sectors_shift
= 8,
115 .fast_read_dual_output_support
= 1,
120 .nr_sectors_shift
= 9,
121 .fast_read_dual_output_support
= 1,
122 .protection_granularity_shift
= 16,
128 .nr_sectors_shift
= 9,
129 .fast_read_dual_output_support
= 1,
130 .protection_granularity_shift
= 16,
136 .nr_sectors_shift
= 10,
137 .fast_read_dual_output_support
= 1,
138 .protection_granularity_shift
= 16,
144 .nr_sectors_shift
= 10,
145 .fast_read_dual_output_support
= 1,
146 .protection_granularity_shift
= 16,
152 .nr_sectors_shift
= 11,
153 .fast_read_dual_output_support
= 1,
154 .protection_granularity_shift
= 17,
160 .nr_sectors_shift
= 11,
161 .fast_read_dual_output_support
= 1,
162 .protection_granularity_shift
= 17,
168 .nr_sectors_shift
= 11,
169 .fast_read_dual_output_support
= 1,
170 .protection_granularity_shift
= 17,
176 .nr_sectors_shift
= 12,
177 .fast_read_dual_output_support
= 1,
178 .protection_granularity_shift
= 18,
184 .nr_sectors_shift
= 12,
185 .fast_read_dual_output_support
= 1,
186 .protection_granularity_shift
= 18,
192 .nr_sectors_shift
= 12,
193 .fast_read_dual_output_support
= 1,
194 .protection_granularity_shift
= 18,
200 .nr_sectors_shift
= 12,
201 .fast_read_dual_output_support
= 1,
202 .protection_granularity_shift
= 18,
208 .nr_sectors_shift
= 13,
209 .fast_read_dual_output_support
= 1,
210 .protection_granularity_shift
= 16,
216 .nr_sectors_shift
= 13,
217 .fast_read_dual_output_support
= 1,
218 .protection_granularity_shift
= 16,
224 * Convert BPx, TB and CMP to a region.
225 * SEC (if available) must be zero.
227 static void winbond_bpbits_to_region(const size_t granularity
,
231 const size_t flash_size
,
234 size_t protected_size
=
235 MIN(bp
? granularity
<< (bp
- 1) : 0, flash_size
);
238 protected_size
= flash_size
- protected_size
;
242 out
->offset
= tb
? 0 : flash_size
- protected_size
;
243 out
->size
= protected_size
;
247 * Available on all devices.
248 * Read block protect bits from Status/Status2 Reg.
249 * Converts block protection bits to a region.
253 * 1 if region is covered by write protection
254 * 0 if a part of region isn't covered by write protection
256 static int winbond_get_write_protection(const struct spi_flash
*flash
,
257 const struct region
*region
)
259 const struct spi_flash_part_id
*params
;
260 struct region wp_region
;
261 union status_reg2 reg2
;
265 params
= flash
->part
;
270 const size_t granularity
= (1 << params
->protection_granularity_shift
);
272 if (params
->bp_bits
== 3) {
273 union status_reg1_bp3 reg1_bp3
= { .u
= 0 };
275 ret
= spi_flash_cmd(&flash
->spi
, flash
->status_cmd
, ®1_bp3
.u
,
279 // FIXME: not supported
285 } else if (params
->bp_bits
== 4) {
286 union status_reg1_bp4 reg1_bp4
= { .u
= 0 };
288 ret
= spi_flash_cmd(&flash
->spi
, flash
->status_cmd
, ®1_bp4
.u
,
294 // FIXME: not supported
300 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®2
.u
,
305 winbond_bpbits_to_region(granularity
, bp
, tb
, reg2
.cmp
, flash
->size
,
308 if (!region_sz(&wp_region
)) {
309 printk(BIOS_DEBUG
, "WINBOND: flash isn't protected\n");
314 printk(BIOS_DEBUG
, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
315 region_offset(&wp_region
), region_end(&wp_region
));
317 return region_is_subregion(&wp_region
, region
);
321 * Common method to write some bit of the status register 1 & 2 at the same
322 * time. Only change bits that are one in @mask.
323 * Compare the final result to make sure that the register isn't locked.
325 * @param mask: The bits that are affected by @val
326 * @param val: The bits to write
327 * @param non_volatile: Make setting permanent
329 * @return 0 on success
331 static int winbond_flash_cmd_status(const struct spi_flash
*flash
,
334 const bool non_volatile
)
346 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR
, ®8
, sizeof(reg8
));
352 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®8
, sizeof(reg8
));
356 cmdbuf
.sreg
|= reg8
<< 8;
358 if ((val
& mask
) == (cmdbuf
.sreg
& mask
))
362 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_WREN
, NULL
, 0);
364 ret
= spi_flash_cmd(&flash
->spi
, CMD_VOLATILE_SREG_WREN
, NULL
,
370 cmdbuf
.sreg
&= ~mask
;
371 cmdbuf
.sreg
|= val
& mask
;
372 cmdbuf
.cmd
= CMD_W25_WRSR
;
374 /* Legacy method of writing status register 1 & 2 */
375 ret
= spi_flash_cmd_write(&flash
->spi
, (u8
*)&cmdbuf
, sizeof(cmdbuf
),
382 ret
= spi_flash_cmd_wait_ready(flash
, WINBOND_FLASH_TIMEOUT
);
390 /* Now read the status register to make sure it's not locked */
391 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR
, ®8
, sizeof(reg8
));
397 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®8
, sizeof(reg8
));
401 cmdbuf
.sreg
|= reg8
<< 8;
403 printk(BIOS_DEBUG
, "WINBOND: SREG=%02x SREG2=%02x\n",
407 /* Compare against expected result */
408 if ((val
& mask
) != (cmdbuf
.sreg
& mask
)) {
409 printk(BIOS_ERR
, "WINBOND: SREG is locked!\n");
417 * Available on all devices.
418 * Protect a region starting from start of flash or end of flash.
419 * The caller must provide a supported protected region size.
420 * SEC isn't supported and set to zero.
421 * Write block protect bits to Status/Status2 Reg.
422 * Optionally lock the status register if lock_sreg is set with the provided
425 * @param flash: The flash to operate on
426 * @param region: The region to write protect
427 * @param non_volatile: Make setting permanent
428 * @param mode: Optional status register lock-down mode
430 * @return 0 on success
433 winbond_set_write_protection(const struct spi_flash
*flash
,
434 const struct region
*region
,
435 const bool non_volatile
,
436 const enum spi_flash_status_reg_lockdown mode
)
438 const struct spi_flash_part_id
*params
;
439 struct status_regs mask
, val
;
440 struct region wp_region
;
444 /* Need to touch TOP or BOTTOM */
445 if (region_offset(region
) != 0 && region_end(region
) != flash
->size
)
448 params
= flash
->part
;
453 if (params
->bp_bits
!= 3 && params
->bp_bits
!= 4) {
454 /* FIXME: not implemented */
460 if (region_offset(&wp_region
) == 0)
465 if (region_sz(&wp_region
) > flash
->size
/ 2) {
467 wp_region
.offset
= tb
? 0 : region_sz(&wp_region
);
468 wp_region
.size
= flash
->size
- region_sz(&wp_region
);
474 if (region_sz(&wp_region
) == 0) {
476 } else if (IS_POWER_OF_2(region_sz(&wp_region
)) &&
477 (region_sz(&wp_region
) >=
478 (1 << params
->protection_granularity_shift
))) {
479 bp
= log2(region_sz(&wp_region
)) -
480 params
->protection_granularity_shift
+ 1;
482 printk(BIOS_ERR
, "WINBOND: ERROR: unsupported region size\n");
486 /* Write block protection bits */
488 if (params
->bp_bits
== 3) {
489 val
.reg1_bp3
= (union status_reg1_bp3
) { .bp
= bp
, .tb
= tb
,
491 mask
.reg1_bp3
= (union status_reg1_bp3
) { .bp
= ~0, .tb
= 1,
494 val
.reg1_bp4
= (union status_reg1_bp4
) { .bp
= bp
, .tb
= tb
};
495 mask
.reg1_bp4
= (union status_reg1_bp4
) { .bp
= ~0, .tb
= 1 };
498 val
.reg2
= (union status_reg2
) { .cmp
= cmp
};
499 mask
.reg2
= (union status_reg2
) { .cmp
= 1 };
501 if (mode
!= SPI_WRITE_PROTECTION_PRESERVE
) {
504 case SPI_WRITE_PROTECTION_NONE
:
507 case SPI_WRITE_PROTECTION_PIN
:
510 case SPI_WRITE_PROTECTION_REBOOT
:
513 case SPI_WRITE_PROTECTION_PERMANENT
:
520 if (params
->bp_bits
== 3) {
521 val
.reg1_bp3
.srp0
= !!(srp
& 1);
522 mask
.reg1_bp3
.srp0
= 1;
524 val
.reg1_bp4
.srp0
= !!(srp
& 1);
525 mask
.reg1_bp4
.srp0
= 1;
528 val
.reg2
.srp1
= !!(srp
& 2);
532 ret
= winbond_flash_cmd_status(flash
, mask
.u
, val
.u
, non_volatile
);
536 printk(BIOS_DEBUG
, "WINBOND: write-protection set to range "
537 "0x%08zx-0x%08zx\n", region_offset(region
), region_end(region
));
542 static const struct spi_flash_protection_ops spi_flash_protection_ops
= {
543 .get_write
= winbond_get_write_protection
,
544 .set_write
= winbond_set_write_protection
,
547 const struct spi_flash_vendor_info spi_flash_winbond_vi
= {
548 .id
= VENDOR_ID_WINBOND
,
549 .page_size_shift
= 8,
550 .sector_size_kib_shift
= 2,
551 .match_id_mask
[0] = 0xffff,
553 .nr_part_ids
= ARRAY_SIZE(flash_table
),
554 .desc
= &spi_flash_pp_0x20_sector_desc
,
555 .prot_ops
= &spi_flash_protection_ops
,