coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
winbond.c
Go to the documentation of this file.
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 {
15  struct {
17  uint8_t wel : 1;
18  uint8_t bp : 3;
19  uint8_t tb : 1;
20  uint8_t sec : 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;
30 };
31 
32 union status_reg2 {
34  struct {
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 
234 /*
235  * Convert BPx, TB and CMP to a region.
236  * SEC (if available) must be zero.
237  */
238 static void winbond_bpbits_to_region(const size_t granularity,
239  const u8 bp,
240  bool tb,
241  const bool cmp,
242  const size_t flash_size,
243  struct region *out)
244 {
245  size_t protected_size =
246  MIN(bp ? granularity << (bp - 1) : 0, flash_size);
247 
248  if (cmp) {
249  protected_size = flash_size - protected_size;
250  tb = !tb;
251  }
252 
253  out->offset = tb ? 0 : flash_size - protected_size;
254  out->size = protected_size;
255 }
256 
257 /*
258  * Available on all devices.
259  * Read block protect bits from Status/Status2 Reg.
260  * Converts block protection bits to a region.
261  *
262  * Returns:
263  * -1 on error
264  * 1 if region is covered by write protection
265  * 0 if a part of region isn't covered by write protection
266  */
267 static int winbond_get_write_protection(const struct spi_flash *flash,
268  const struct region *region)
269 {
270  const struct spi_flash_part_id *params;
271  struct region wp_region;
272  union status_reg2 reg2;
273  u8 bp, tb;
274  int ret;
275 
276  params = flash->part;
277 
278  if (!params)
279  return -1;
280 
281  const size_t granularity = (1 << params->protection_granularity_shift);
282 
283  union status_reg1 reg1 = { .u = 0 };
284 
285  ret = spi_flash_cmd(&flash->spi, flash->status_cmd, &reg1.u,
286  sizeof(reg1.u));
287  if (ret)
288  return ret;
289 
290  if (params->bp_bits == 3) {
291  if (reg1.bp3.sec) {
292  // FIXME: not supported
293  return -1;
294  }
295 
296  bp = reg1.bp3.bp;
297  tb = reg1.bp3.tb;
298  } else if (params->bp_bits == 4) {
299  bp = reg1.bp4.bp;
300  tb = reg1.bp4.tb;
301  } else {
302  // FIXME: not supported
303  return -1;
304  }
305 
306  ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg2.u,
307  sizeof(reg2.u));
308  if (ret)
309  return ret;
310 
311  winbond_bpbits_to_region(granularity, bp, tb, reg2.cmp, flash->size,
312  &wp_region);
313 
314  if (!region_sz(&wp_region)) {
315  printk(BIOS_DEBUG, "WINBOND: flash isn't protected\n");
316 
317  return 0;
318  }
319 
320  printk(BIOS_DEBUG, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
321  region_offset(&wp_region), region_end(&wp_region));
322 
323  return region_is_subregion(&wp_region, region);
324 }
325 
326 /**
327  * Common method to write some bit of the status register 1 & 2 at the same
328  * time. Only change bits that are one in @mask.
329  * Compare the final result to make sure that the register isn't locked.
330  *
331  * @param mask: The bits that are affected by @val
332  * @param val: The bits to write
333  * @param non_volatile: Make setting permanent
334  *
335  * @return 0 on success
336  */
337 static int winbond_flash_cmd_status(const struct spi_flash *flash,
338  const u16 mask,
339  const u16 val,
340  const bool non_volatile)
341 {
342  struct {
343  u8 cmd;
344  u16 sreg;
345  } __packed cmdbuf;
346  u8 reg8;
347  int ret;
348 
349  if (!flash)
350  return -1;
351 
352  ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
353  if (ret)
354  return ret;
355 
356  cmdbuf.sreg = reg8;
357 
358  ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
359  if (ret)
360  return ret;
361 
362  cmdbuf.sreg |= reg8 << 8;
363 
364  if ((val & mask) == (cmdbuf.sreg & mask))
365  return 0;
366 
367  if (non_volatile) {
368  ret = spi_flash_cmd(&flash->spi, CMD_W25_WREN, NULL, 0);
369  } else {
371  0);
372  }
373  if (ret)
374  return ret;
375 
376  cmdbuf.sreg &= ~mask;
377  cmdbuf.sreg |= val & mask;
378  cmdbuf.cmd = CMD_W25_WRSR;
379 
380  /* Legacy method of writing status register 1 & 2 */
381  ret = spi_flash_cmd_write(&flash->spi, (u8 *)&cmdbuf, sizeof(cmdbuf),
382  NULL, 0);
383  if (ret)
384  return ret;
385 
386  if (non_volatile) {
387  /* Wait tw */
389  if (ret)
390  return ret;
391  } else {
392  /* Wait tSHSL */
393  udelay(1);
394  }
395 
396  /* Now read the status register to make sure it's not locked */
397  ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
398  if (ret)
399  return ret;
400 
401  cmdbuf.sreg = reg8;
402 
403  ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
404  if (ret)
405  return ret;
406 
407  cmdbuf.sreg |= reg8 << 8;
408 
409  printk(BIOS_DEBUG, "WINBOND: SREG=%02x SREG2=%02x\n",
410  cmdbuf.sreg & 0xff,
411  cmdbuf.sreg >> 8);
412 
413  /* Compare against expected result */
414  if ((val & mask) != (cmdbuf.sreg & mask)) {
415  printk(BIOS_ERR, "WINBOND: SREG is locked!\n");
416  ret = -1;
417  }
418 
419  return ret;
420 }
421 
422 /*
423  * Available on all devices.
424  * Protect a region starting from start of flash or end of flash.
425  * The caller must provide a supported protected region size.
426  * SEC isn't supported and set to zero.
427  * Write block protect bits to Status/Status2 Reg.
428  * Optionally lock the status register if lock_sreg is set with the provided
429  * mode.
430  *
431  * @param flash: The flash to operate on
432  * @param region: The region to write protect
433  * @param mode: Optional status register lock-down mode
434  *
435  * @return 0 on success
436  */
437 static int
439  const struct region *region,
440  const enum spi_flash_status_reg_lockdown mode)
441 {
442  const struct spi_flash_part_id *params;
443  struct status_regs mask, val;
444  struct region wp_region;
445  u8 cmp, bp, tb;
446  int ret;
447 
448  /* Need to touch TOP or BOTTOM */
449  if (region_offset(region) != 0 && region_end(region) != flash->size)
450  return -1;
451 
452  params = flash->part;
453 
454  if (!params)
455  return -1;
456 
457  if (params->bp_bits != 3 && params->bp_bits != 4) {
458  /* FIXME: not implemented */
459  return -1;
460  }
461 
462  wp_region = *region;
463 
464  if (region_offset(&wp_region) == 0)
465  tb = 1;
466  else
467  tb = 0;
468 
469  if (region_sz(&wp_region) > flash->size / 2) {
470  cmp = 1;
471  wp_region.offset = tb ? 0 : region_sz(&wp_region);
472  wp_region.size = flash->size - region_sz(&wp_region);
473  tb = !tb;
474  } else {
475  cmp = 0;
476  }
477 
478  if (region_sz(&wp_region) == 0) {
479  bp = 0;
480  } else if (IS_POWER_OF_2(region_sz(&wp_region)) &&
481  (region_sz(&wp_region) >=
482  (1 << params->protection_granularity_shift))) {
483  bp = log2(region_sz(&wp_region)) -
484  params->protection_granularity_shift + 1;
485  } else {
486  printk(BIOS_ERR, "WINBOND: ERROR: unsupported region size\n");
487  return -1;
488  }
489 
490  /* Write block protection bits */
491 
492  if (params->bp_bits == 3) {
493  val.reg1 = (union status_reg1) {
494  .bp3 = { .bp = bp, .tb = tb, .sec = 0 }
495  };
496  mask.reg1 = (union status_reg1) {
497  .bp3 = { .bp = ~0, .tb = 1, .sec = 1 }
498  };
499  } else {
500  val.reg1 = (union status_reg1) {
501  .bp4 = { .bp = bp, .tb = tb }
502  };
503  mask.reg1 = (union status_reg1) {
504  .bp4 = { .bp = ~0, .tb = 1 }
505  };
506  }
507 
508  val.reg2 = (union status_reg2) { .cmp = cmp };
509  mask.reg2 = (union status_reg2) { .cmp = 1 };
510 
511  if (mode != SPI_WRITE_PROTECTION_PRESERVE) {
512  u8 srp;
513  switch (mode) {
515  srp = 0;
516  break;
518  srp = 1;
519  break;
521  srp = 2;
522  break;
524  srp = 3;
525  break;
526  default:
527  return -1;
528  }
529 
530  if (params->bp_bits == 3) {
531  val.reg1.bp3.srp0 = !!(srp & 1);
532  mask.reg1.bp3.srp0 = 1;
533  } else {
534  val.reg1.bp4.srp0 = !!(srp & 1);
535  mask.reg1.bp4.srp0 = 1;
536  }
537 
538  val.reg2.srp1 = !!(srp & 2);
539  mask.reg2.srp1 = 1;
540  }
541 
542  ret = winbond_flash_cmd_status(flash, mask.u, val.u, true);
543  if (ret)
544  return ret;
545 
546  printk(BIOS_DEBUG, "WINBOND: write-protection set to range "
547  "0x%08zx-0x%08zx\n", region_offset(region), region_end(region));
548 
549  return ret;
550 }
551 
554  .set_write = winbond_set_write_protection,
555 };
556 
559  .page_size_shift = 8,
560  .sector_size_kib_shift = 2,
561  .match_id_mask[0] = 0xffff,
562  .ids = flash_table,
563  .nr_part_ids = ARRAY_SIZE(flash_table),
565  .prot_ops = &spi_flash_protection_ops,
566 };
static struct sdram_info params
Definition: sdram_configs.c:83
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define MIN(a, b)
Definition: helpers.h:37
#define IS_POWER_OF_2(x)
Definition: helpers.h:50
#define printk(level,...)
Definition: stdlib.h:16
static int log2(u32 x)
Definition: lib.h:53
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
static size_t region_sz(const struct region *r)
Definition: region.h:110
int region_is_subregion(const struct region *p, const struct region *c)
Definition: region.c:7
static size_t region_end(const struct region *r)
Definition: region.h:115
static size_t region_offset(const struct region *r)
Definition: region.h:105
static const int mask[4]
Definition: gpio.c:308
#define VENDOR_ID_WINBOND
Definition: spi-generic.h:27
const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc
Definition: spi_flash.c:793
int spi_flash_cmd_wait_ready(const struct spi_flash *flash, unsigned long timeout)
Definition: spi_flash.c:221
int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len)
Definition: spi_flash.c:107
int spi_flash_cmd_write(const struct spi_slave *spi, const u8 *cmd, size_t cmd_len, const void *data, size_t data_len)
Definition: spi_flash.c:122
spi_flash_status_reg_lockdown
Definition: spi_flash.h:28
@ SPI_WRITE_PROTECTION_PERMANENT
Definition: spi_flash.h:33
@ SPI_WRITE_PROTECTION_PIN
Definition: spi_flash.h:31
@ SPI_WRITE_PROTECTION_PRESERVE
Definition: spi_flash.h:29
@ SPI_WRITE_PROTECTION_NONE
Definition: spi_flash.h:30
@ SPI_WRITE_PROTECTION_REBOOT
Definition: spi_flash.h:32
#define CMD_W25_RDSR2
Definition: spi_winbond.h:9
#define CMD_W25_WRSR
Definition: spi_winbond.h:8
#define CMD_W25_RDSR
Definition: spi_winbond.h:7
#define CMD_W25_WREN
Definition: spi_winbond.h:5
#define WINBOND_FLASH_TIMEOUT
Definition: spi_winbond.h:23
#define CMD_VOLATILE_SREG_WREN
Definition: spi_winbond.h:20
#define NULL
Definition: stddef.h:19
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
Definition: x86.c:23
Definition: region.h:76
size_t size
Definition: region.h:78
size_t offset
Definition: region.h:77
int(* get_write)(const struct spi_flash *flash, const struct region *region)
Definition: spi_flash.h:61
const struct spi_flash_part_id * part
Definition: spi_flash.h:105
u8 status_cmd
Definition: spi_flash.h:99
u32 size
Definition: spi_flash.h:95
struct spi_slave spi
Definition: spi_flash.h:84
union status_reg1 reg1
Definition: winbond.c:51
union status_reg2 reg2
Definition: winbond.c:52
u8 val
Definition: sys.c:300
void udelay(uint32_t us)
Definition: udelay.c:15
struct status_reg1::@89 bp3
uint8_t wel
Definition: winbond.c:17
uint8_t tb
Definition: winbond.c:19
uint8_t busy
Definition: winbond.c:16
uint8_t bp
Definition: winbond.c:18
uint8_t srp0
Definition: winbond.c:21
uint8_t u
Definition: winbond.c:14
uint8_t sec
Definition: winbond.c:20
struct status_reg1::@90 bp4
uint8_t qe
Definition: winbond.c:36
uint8_t u
Definition: winbond.c:33
uint8_t res
Definition: winbond.c:37
uint8_t lb
Definition: winbond.c:38
uint8_t sus
Definition: winbond.c:40
uint8_t cmp
Definition: winbond.c:39
uint8_t srp1
Definition: winbond.c:35
const struct spi_flash_vendor_info spi_flash_winbond_vi
Definition: winbond.c:557
static int winbond_get_write_protection(const struct spi_flash *flash, const struct region *region)
Definition: winbond.c:267
static const struct spi_flash_part_id flash_table[]
Definition: winbond.c:59
static int winbond_flash_cmd_status(const struct spi_flash *flash, const u16 mask, const u16 val, const bool non_volatile)
Common method to write some bit of the status register 1 & 2 at the same time.
Definition: winbond.c:337
static void winbond_bpbits_to_region(const size_t granularity, const u8 bp, bool tb, const bool cmp, const size_t flash_size, struct region *out)
Definition: winbond.c:238
static int winbond_set_write_protection(const struct spi_flash *flash, const struct region *region, const enum spi_flash_status_reg_lockdown mode)
Definition: winbond.c:438
static const struct spi_flash_protection_ops spi_flash_protection_ops
Definition: winbond.c:552