2 * This file is part of the coreboot project.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <console/console.h>
16 #include <commonlib/helpers.h>
17 #include <spi_flash.h>
18 #include <spi-generic.h>
23 #include "spi_flash_internal.h"
24 #include "spi_winbond.h"
26 struct winbond_spi_flash_params
{
29 uint8_t _reserved_for_flags
: 3;
30 uint8_t l2_page_size_shift
: 4;
31 uint8_t pages_per_sector_shift
: 4;
32 uint8_t sectors_per_block_shift
: 4;
33 uint8_t nr_blocks_shift
;
35 uint8_t protection_granularity_shift
: 5;
39 union status_reg1_bp3
{
51 union status_reg1_bp4
{
77 #if defined(__BIG_ENDIAN)
78 union status_reg2 reg2
;
80 union status_reg1_bp3 reg1_bp3
;
81 union status_reg1_bp4 reg1_bp4
;
85 union status_reg1_bp3 reg1_bp3
;
86 union status_reg1_bp4 reg1_bp4
;
88 union status_reg2 reg2
;
95 static const struct winbond_spi_flash_params winbond_spi_flash_table
[] = {
98 .l2_page_size_shift
= 8,
99 .pages_per_sector_shift
= 4,
100 .sectors_per_block_shift
= 4,
101 .nr_blocks_shift
= 4,
106 .l2_page_size_shift
= 8,
107 .pages_per_sector_shift
= 4,
108 .sectors_per_block_shift
= 4,
109 .nr_blocks_shift
= 5,
114 .l2_page_size_shift
= 8,
115 .pages_per_sector_shift
= 4,
116 .sectors_per_block_shift
= 4,
117 .nr_blocks_shift
= 6,
122 .l2_page_size_shift
= 8,
123 .pages_per_sector_shift
= 4,
124 .sectors_per_block_shift
= 4,
125 .nr_blocks_shift
= 4,
131 .l2_page_size_shift
= 8,
132 .pages_per_sector_shift
= 4,
133 .sectors_per_block_shift
= 4,
134 .nr_blocks_shift
= 5,
140 .l2_page_size_shift
= 8,
141 .pages_per_sector_shift
= 4,
142 .sectors_per_block_shift
= 4,
143 .nr_blocks_shift
= 6,
149 .l2_page_size_shift
= 8,
150 .pages_per_sector_shift
= 4,
151 .sectors_per_block_shift
= 4,
152 .nr_blocks_shift
= 7,
158 .l2_page_size_shift
= 8,
159 .pages_per_sector_shift
= 4,
160 .sectors_per_block_shift
= 4,
161 .nr_blocks_shift
= 4,
167 .l2_page_size_shift
= 8,
168 .pages_per_sector_shift
= 4,
169 .sectors_per_block_shift
= 4,
170 .nr_blocks_shift
= 5,
173 .protection_granularity_shift
= 16,
178 .l2_page_size_shift
= 8,
179 .pages_per_sector_shift
= 4,
180 .sectors_per_block_shift
= 4,
181 .nr_blocks_shift
= 5,
184 .protection_granularity_shift
= 16,
189 .l2_page_size_shift
= 8,
190 .pages_per_sector_shift
= 4,
191 .sectors_per_block_shift
= 4,
192 .nr_blocks_shift
= 6,
195 .protection_granularity_shift
= 16,
200 .l2_page_size_shift
= 8,
201 .pages_per_sector_shift
= 4,
202 .sectors_per_block_shift
= 4,
203 .nr_blocks_shift
= 6,
206 .protection_granularity_shift
= 16,
211 .l2_page_size_shift
= 8,
212 .pages_per_sector_shift
= 4,
213 .sectors_per_block_shift
= 4,
214 .nr_blocks_shift
= 7,
217 .protection_granularity_shift
= 17,
222 .l2_page_size_shift
= 8,
223 .pages_per_sector_shift
= 4,
224 .sectors_per_block_shift
= 4,
225 .nr_blocks_shift
= 7,
228 .protection_granularity_shift
= 17,
233 .l2_page_size_shift
= 8,
234 .pages_per_sector_shift
= 4,
235 .sectors_per_block_shift
= 4,
236 .nr_blocks_shift
= 8,
239 .protection_granularity_shift
= 18,
244 .l2_page_size_shift
= 8,
245 .pages_per_sector_shift
= 4,
246 .sectors_per_block_shift
= 4,
247 .nr_blocks_shift
= 8,
250 .protection_granularity_shift
= 18,
255 .l2_page_size_shift
= 8,
256 .pages_per_sector_shift
= 4,
257 .sectors_per_block_shift
= 4,
258 .nr_blocks_shift
= 8,
261 .protection_granularity_shift
= 18,
266 .l2_page_size_shift
= 8,
267 .pages_per_sector_shift
= 4,
268 .sectors_per_block_shift
= 4,
269 .nr_blocks_shift
= 8,
272 .protection_granularity_shift
= 18,
277 .l2_page_size_shift
= 8,
278 .pages_per_sector_shift
= 4,
279 .sectors_per_block_shift
= 4,
280 .nr_blocks_shift
= 9,
283 .protection_granularity_shift
= 16,
288 .l2_page_size_shift
= 8,
289 .pages_per_sector_shift
= 4,
290 .sectors_per_block_shift
= 4,
291 .nr_blocks_shift
= 9,
294 .protection_granularity_shift
= 16,
299 static int winbond_write(const struct spi_flash
*flash
, u32 offset
, size_t len
,
302 unsigned long byte_addr
;
303 unsigned long page_size
;
309 page_size
= flash
->page_size
;
311 for (actual
= 0; actual
< len
; actual
+= chunk_len
) {
312 byte_addr
= offset
% page_size
;
313 chunk_len
= MIN(len
- actual
, page_size
- byte_addr
);
314 chunk_len
= spi_crop_chunk(&flash
->spi
, sizeof(cmd
), chunk_len
);
317 cmd
[1] = (offset
>> 16) & 0xff;
318 cmd
[2] = (offset
>> 8) & 0xff;
319 cmd
[3] = offset
& 0xff;
320 #if CONFIG(DEBUG_SPI_FLASH)
321 printk(BIOS_SPEW
, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
322 " chunk_len = %zu\n", buf
+ actual
,
323 cmd
[0], cmd
[1], cmd
[2], cmd
[3], chunk_len
);
326 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_WREN
, NULL
, 0);
328 printk(BIOS_WARNING
, "SF: Enabling Write failed\n");
332 ret
= spi_flash_cmd_write(&flash
->spi
, cmd
, sizeof(cmd
),
333 buf
+ actual
, chunk_len
);
335 printk(BIOS_WARNING
, "SF: Winbond Page Program failed\n");
339 ret
= spi_flash_cmd_wait_ready(flash
,
340 SPI_FLASH_PROG_TIMEOUT_MS
);
347 #if CONFIG(DEBUG_SPI_FLASH)
348 printk(BIOS_SPEW
, "SF: Winbond: Successfully programmed %zu bytes @"
349 " 0x%lx\n", len
, (unsigned long)(offset
- len
));
358 * Convert BPx, TB and CMP to a region.
359 * SEC (if available) must be zero.
361 static void winbond_bpbits_to_region(const size_t granularity
,
365 const size_t flash_size
,
368 size_t protected_size
=
369 MIN(bp
? granularity
<< (bp
- 1) : 0, flash_size
);
372 protected_size
= flash_size
- protected_size
;
376 out
->offset
= tb
? 0 : flash_size
- protected_size
;
377 out
->size
= protected_size
;
381 * Available on all devices.
382 * Read block protect bits from Status/Status2 Reg.
383 * Converts block protection bits to a region.
387 * 1 if region is covered by write protection
388 * 0 if a part of region isn't covered by write protection
390 static int winbond_get_write_protection(const struct spi_flash
*flash
,
391 const struct region
*region
)
393 const struct winbond_spi_flash_params
*params
;
394 struct region wp_region
;
395 union status_reg2 reg2
;
399 params
= (const struct winbond_spi_flash_params
*)flash
->driver_private
;
400 const size_t granularity
= (1 << params
->protection_granularity_shift
);
402 if (params
->bp_bits
== 3) {
403 union status_reg1_bp3 reg1_bp3
= { .u
= 0 };
405 ret
= spi_flash_cmd(&flash
->spi
, flash
->status_cmd
, ®1_bp3
.u
,
409 // FIXME: not supported
415 } else if (params
->bp_bits
== 4) {
416 union status_reg1_bp4 reg1_bp4
= { .u
= 0 };
418 ret
= spi_flash_cmd(&flash
->spi
, flash
->status_cmd
, ®1_bp4
.u
,
424 // FIXME: not supported
430 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®2
.u
,
435 winbond_bpbits_to_region(granularity
, bp
, tb
, reg2
.cmp
, flash
->size
,
438 if (!region_sz(&wp_region
)) {
439 printk(BIOS_DEBUG
, "WINBOND: flash isn't protected\n");
444 printk(BIOS_DEBUG
, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
445 region_offset(&wp_region
), region_end(&wp_region
));
447 return region_is_subregion(&wp_region
, region
);
451 * Common method to write some bit of the status register 1 & 2 at the same
452 * time. Only change bits that are one in @mask.
453 * Compare the final result to make sure that the register isn't locked.
455 * @param mask: The bits that are affected by @val
456 * @param val: The bits to write
457 * @param non_volatile: Make setting permanent
459 * @return 0 on success
461 static int winbond_flash_cmd_status(const struct spi_flash
*flash
,
464 const bool non_volatile
)
476 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR
, ®8
, sizeof(reg8
));
482 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®8
, sizeof(reg8
));
486 cmdbuf
.sreg
|= reg8
<< 8;
488 if ((val
& mask
) == (cmdbuf
.sreg
& mask
))
492 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_WREN
, NULL
, 0);
494 ret
= spi_flash_cmd(&flash
->spi
, CMD_VOLATILE_SREG_WREN
, NULL
,
500 cmdbuf
.sreg
&= ~mask
;
501 cmdbuf
.sreg
|= val
& mask
;
502 cmdbuf
.cmd
= CMD_W25_WRSR
;
504 /* Legacy method of writing status register 1 & 2 */
505 ret
= spi_flash_cmd_write(&flash
->spi
, (u8
*)&cmdbuf
, sizeof(cmdbuf
),
512 ret
= spi_flash_cmd_wait_ready(flash
, WINBOND_FLASH_TIMEOUT
);
520 /* Now read the status register to make sure it's not locked */
521 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR
, ®8
, sizeof(reg8
));
527 ret
= spi_flash_cmd(&flash
->spi
, CMD_W25_RDSR2
, ®8
, sizeof(reg8
));
531 cmdbuf
.sreg
|= reg8
<< 8;
533 printk(BIOS_DEBUG
, "WINBOND: SREG=%02x SREG2=%02x\n",
537 /* Compare against expected result */
538 if ((val
& mask
) != (cmdbuf
.sreg
& mask
)) {
539 printk(BIOS_ERR
, "WINBOND: SREG is locked!\n");
547 * Available on all devices.
548 * Protect a region starting from start of flash or end of flash.
549 * The caller must provide a supported protected region size.
550 * SEC isn't supported and set to zero.
551 * Write block protect bits to Status/Status2 Reg.
552 * Optionally lock the status register if lock_sreg is set with the provided
555 * @param flash: The flash to operate on
556 * @param region: The region to write protect
557 * @param non_volatile: Make setting permanent
558 * @param mode: Optional status register lock-down mode
560 * @return 0 on success
563 winbond_set_write_protection(const struct spi_flash
*flash
,
564 const struct region
*region
,
565 const bool non_volatile
,
566 const enum spi_flash_status_reg_lockdown mode
)
568 const struct winbond_spi_flash_params
*params
;
569 struct status_regs mask
, val
;
570 struct region wp_region
;
574 /* Need to touch TOP or BOTTOM */
575 if (region_offset(region
) != 0 && region_end(region
) != flash
->size
)
578 params
= (const struct winbond_spi_flash_params
*)flash
->driver_private
;
582 if (params
->bp_bits
!= 3 && params
->bp_bits
!= 4) {
583 /* FIXME: not implemented */
589 if (region_offset(&wp_region
) == 0)
594 if (region_sz(&wp_region
) > flash
->size
/ 2) {
596 wp_region
.offset
= tb
? 0 : region_sz(&wp_region
);
597 wp_region
.size
= flash
->size
- region_sz(&wp_region
);
603 if (region_sz(&wp_region
) == 0) {
605 } else if (IS_POWER_OF_2(region_sz(&wp_region
)) &&
606 (region_sz(&wp_region
) >=
607 (1 << params
->protection_granularity_shift
))) {
608 bp
= log2(region_sz(&wp_region
)) -
609 params
->protection_granularity_shift
+ 1;
611 printk(BIOS_ERR
, "WINBOND: ERROR: unsupported region size\n");
615 /* Write block protection bits */
617 if (params
->bp_bits
== 3) {
618 val
.reg1_bp3
= (union status_reg1_bp3
) { .bp
= bp
, .tb
= tb
,
620 mask
.reg1_bp3
= (union status_reg1_bp3
) { .bp
= ~0, .tb
= 1,
623 val
.reg1_bp4
= (union status_reg1_bp4
) { .bp
= bp
, .tb
= tb
};
624 mask
.reg1_bp4
= (union status_reg1_bp4
) { .bp
= ~0, .tb
= 1 };
627 val
.reg2
= (union status_reg2
) { .cmp
= cmp
};
628 mask
.reg2
= (union status_reg2
) { .cmp
= 1 };
630 if (mode
!= SPI_WRITE_PROTECTION_PRESERVE
) {
633 case SPI_WRITE_PROTECTION_NONE
:
636 case SPI_WRITE_PROTECTION_PIN
:
639 case SPI_WRITE_PROTECTION_REBOOT
:
642 case SPI_WRITE_PROTECTION_PERMANENT
:
649 if (params
->bp_bits
== 3) {
650 val
.reg1_bp3
.srp0
= !!(srp
& 1);
651 mask
.reg1_bp3
.srp0
= 1;
653 val
.reg1_bp4
.srp0
= !!(srp
& 1);
654 mask
.reg1_bp4
.srp0
= 1;
657 val
.reg2
.srp1
= !!(srp
& 2);
661 ret
= winbond_flash_cmd_status(flash
, mask
.u
, val
.u
, non_volatile
);
665 printk(BIOS_DEBUG
, "WINBOND: write-protection set to range "
666 "0x%08zx-0x%08zx\n", region_offset(region
), region_end(region
));
671 static const struct spi_flash_ops spi_flash_ops
= {
672 .write
= winbond_write
,
673 .erase
= spi_flash_cmd_erase
,
674 .status
= spi_flash_cmd_status
,
675 .get_write_protection
= winbond_get_write_protection
,
676 .set_write_protection
= winbond_set_write_protection
,
679 int spi_flash_probe_winbond(const struct spi_slave
*spi
, u8
*idcode
,
680 struct spi_flash
*flash
)
682 const struct winbond_spi_flash_params
*params
;
685 for (i
= 0; i
< ARRAY_SIZE(winbond_spi_flash_table
); i
++) {
686 params
= &winbond_spi_flash_table
[i
];
687 if (params
->id
== ((idcode
[1] << 8) | idcode
[2]))
691 if (i
== ARRAY_SIZE(winbond_spi_flash_table
)) {
692 printk(BIOS_WARNING
, "SF: Unsupported Winbond ID %02x%02x\n",
693 idcode
[1], idcode
[2]);
697 memcpy(&flash
->spi
, spi
, sizeof(*spi
));
698 flash
->name
= params
->name
;
700 /* Params are in power-of-two. */
701 flash
->page_size
= 1 << params
->l2_page_size_shift
;
702 flash
->sector_size
= flash
->page_size
*
703 (1 << params
->pages_per_sector_shift
);
704 flash
->size
= flash
->sector_size
*
705 (1 << params
->sectors_per_block_shift
) *
706 (1 << params
->nr_blocks_shift
);
707 flash
->erase_cmd
= CMD_W25_SE
;
708 flash
->status_cmd
= CMD_W25_RDSR
;
710 flash
->flags
.dual_spi
= params
->dual_spi
;
712 flash
->ops
= &spi_flash_ops
;
713 flash
->driver_private
= params
;