soc/mediatek/mt8196: Add PMIC MT6373 driver
[coreboot.git] / src / drivers / spi / winbond.c
blobdb93ff616cce433a0df22a5f9a8c17f14012b4f6
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; /* 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 */
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,
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,
253 * Convert BPx, TB and CMP to a region.
254 * SEC (if available) must be zero.
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)
261 size_t protected_size =
262 MIN(bits->bp ? granularity << (bits->bp - 1) : 0, flash_size);
264 int tb = bits->tb;
265 if (bits->cmp) {
266 protected_size = flash_size - protected_size;
267 tb = !tb;
270 *out = region_create(tb ? 0 : flash_size - protected_size, protected_size);
274 * Available on all devices.
275 * Read block protect bits from Status/Status2 Reg.
276 * Converts block protection bits to a region.
278 * Returns:
279 * -1 on error
280 * 1 if region is covered by write protection
281 * 0 if a part of region isn't covered by write protection
283 static int winbond_get_write_protection(const struct spi_flash *flash,
284 const struct region *region)
286 const struct spi_flash_part_id *params;
287 struct region wp_region;
288 struct spi_flash_bpbits bpbits;
289 int ret;
291 params = flash->part;
293 if (!params)
294 return -1;
296 const size_t granularity = (1 << params->protection_granularity_shift);
298 union status_reg1 reg1 = { .u = 0 };
299 union status_reg2 reg2 = { .u = 0 };
301 ret = spi_flash_cmd(&flash->spi, flash->status_cmd, &reg1.u,
302 sizeof(reg1.u));
303 if (ret)
304 return ret;
306 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg2.u,
307 sizeof(reg2.u));
308 if (ret)
309 return ret;
311 if (params->bp_bits == 3) {
312 if (reg1.bp3.sec) {
313 // FIXME: not supported
314 return -1;
317 bpbits = (struct spi_flash_bpbits){
318 .bp = reg1.bp3.bp,
319 .cmp = reg2.cmp,
320 .tb = reg1.bp3.tb,
322 * For W25Q*{,F}* parts:
323 * srp1 srp0
324 * 0 0 | writable if WEL==1
325 * 0 1 | writable if WEL==1 && #WP==Vcc
326 * 1 0 | not writable until next power-down
327 * 1 1 | not writable, permanently
329 * checked datasheets: W25Q128FV, (W25Q80, W25Q16,
330 * W25Q32)
332 .winbond = {
333 .srp0 = reg1.bp3.srp0,
334 .srp1 = reg2.srp1,
337 } else if (params->bp_bits == 4) {
338 bpbits = (struct spi_flash_bpbits){
339 .bp = reg1.bp4.bp,
340 .cmp = reg2.cmp,
341 .tb = reg1.bp4.tb,
343 * For W25Q*{J,D}* parts:
345 * srp1 srp0
346 * 0 0 | writable if WEL==1
347 * 0 1 | writable if WEL==1 && #WP==Vcc
348 * 1 x | not writable until next power-down
350 * checked datasheets: W25Q132JW, W25Q128JW, W25Q256JV.
351 * W25Q16DW
353 * The srp0/srp1 bits got renamed to srp/srl in the
354 * datasheets, we retain the prior naming
355 * convention for the structs though.
357 .winbond = {
358 .srp0 = reg1.bp4.srp0,
359 .srp1 = reg2.srp1,
362 } else {
363 // FIXME: not supported
364 return -1;
367 winbond_bpbits_to_region(granularity, &bpbits, flash->size,
368 &wp_region);
370 if (!region_sz(&wp_region)) {
371 printk(BIOS_DEBUG, "WINBOND: flash isn't protected\n");
373 return 0;
376 printk(BIOS_DEBUG, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
377 region_offset(&wp_region), region_last(&wp_region));
379 return region_is_subregion(&wp_region, region);
383 * Common method to write some bit of the status register 1 & 2 at the same
384 * time. Only change bits that are one in @mask.
385 * Compare the final result to make sure that the register isn't locked.
387 * @param mask: The bits that are affected by @val
388 * @param val: The bits to write
389 * @param non_volatile: Make setting permanent
391 * @return 0 on success
393 static int winbond_flash_cmd_status(const struct spi_flash *flash,
394 const u16 mask,
395 const u16 val,
396 const bool non_volatile)
398 struct {
399 u8 cmd;
400 u16 sreg;
401 } __packed cmdbuf;
402 u8 reg8;
403 int ret;
405 if (!flash)
406 return -1;
408 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
409 if (ret)
410 return ret;
412 cmdbuf.sreg = reg8;
414 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
415 if (ret)
416 return ret;
418 cmdbuf.sreg |= reg8 << 8;
420 if ((val & mask) == (cmdbuf.sreg & mask))
421 return 0;
423 if (non_volatile) {
424 ret = spi_flash_cmd(&flash->spi, CMD_W25_WREN, NULL, 0);
425 } else {
426 ret = spi_flash_cmd(&flash->spi, CMD_VOLATILE_SREG_WREN, NULL,
429 if (ret)
430 return ret;
432 cmdbuf.sreg &= ~mask;
433 cmdbuf.sreg |= val & mask;
434 cmdbuf.cmd = CMD_W25_WRSR;
436 /* Legacy method of writing status register 1 & 2 */
437 ret = spi_flash_cmd_write(&flash->spi, (u8 *)&cmdbuf, sizeof(cmdbuf),
438 NULL, 0);
439 if (ret)
440 return ret;
442 if (non_volatile) {
443 /* Wait tw */
444 ret = spi_flash_cmd_wait_ready(flash, WINBOND_FLASH_TIMEOUT);
445 if (ret)
446 return ret;
447 } else {
448 /* Wait tSHSL */
449 udelay(1);
452 /* Now read the status register to make sure it's not locked */
453 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR, &reg8, sizeof(reg8));
454 if (ret)
455 return ret;
457 cmdbuf.sreg = reg8;
459 ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR2, &reg8, sizeof(reg8));
460 if (ret)
461 return ret;
463 cmdbuf.sreg |= reg8 << 8;
465 printk(BIOS_DEBUG, "WINBOND: SREG=%02x SREG2=%02x\n",
466 cmdbuf.sreg & 0xff,
467 cmdbuf.sreg >> 8);
469 /* Compare against expected result */
470 if ((val & mask) != (cmdbuf.sreg & mask)) {
471 printk(BIOS_ERR, "WINBOND: SREG is locked!\n");
472 ret = -1;
475 return ret;
479 * Available on all devices.
480 * Protect a region starting from start of flash or end of flash.
481 * The caller must provide a supported protected region size.
482 * SEC isn't supported and set to zero.
483 * Write block protect bits to Status/Status2 Reg.
484 * Optionally lock the status register if lock_sreg is set with the provided
485 * mode.
487 * @param flash: The flash to operate on
488 * @param region: The region to write protect
489 * @param mode: Optional status register lock-down mode
491 * @return 0 on success
493 static int
494 winbond_set_write_protection(const struct spi_flash *flash,
495 const struct region *region,
496 const enum spi_flash_status_reg_lockdown mode)
498 const struct spi_flash_part_id *params;
499 struct status_regs mask, val;
500 struct region wp_region;
501 u8 cmp, bp, tb;
502 int ret;
504 /* Need to touch TOP or BOTTOM */
505 if (region_offset(region) != 0 && region_last(region) != flash->size - 1)
506 return -1;
508 params = flash->part;
510 if (!params)
511 return -1;
513 if (params->bp_bits != 3 && params->bp_bits != 4) {
514 /* FIXME: not implemented */
515 return -1;
518 wp_region = *region;
520 if (region_offset(&wp_region) == 0)
521 tb = 1;
522 else
523 tb = 0;
525 if (region_sz(&wp_region) > flash->size / 2) {
526 cmp = 1;
527 wp_region = region_create(tb ? 0 : region_sz(&wp_region),
528 flash->size - region_sz(&wp_region));
529 tb = !tb;
530 } else {
531 cmp = 0;
534 if (region_sz(&wp_region) == 0) {
535 bp = 0;
536 } else if (IS_POWER_OF_2(region_sz(&wp_region)) &&
537 (region_sz(&wp_region) >=
538 (1 << params->protection_granularity_shift))) {
539 bp = log2(region_sz(&wp_region)) -
540 params->protection_granularity_shift + 1;
541 } else {
542 printk(BIOS_ERR, "WINBOND: ERROR: unsupported region size\n");
543 return -1;
546 /* Write block protection bits */
548 if (params->bp_bits == 3) {
549 val.reg1 = (union status_reg1) {
550 .bp3 = { .bp = bp, .tb = tb, .sec = 0 }
552 mask.reg1 = (union status_reg1) {
553 .bp3 = { .bp = ~0, .tb = 1, .sec = 1 }
555 } else {
556 val.reg1 = (union status_reg1) {
557 .bp4 = { .bp = bp, .tb = tb }
559 mask.reg1 = (union status_reg1) {
560 .bp4 = { .bp = ~0, .tb = 1 }
564 val.reg2 = (union status_reg2) { .cmp = cmp };
565 mask.reg2 = (union status_reg2) { .cmp = 1 };
567 if (mode != SPI_WRITE_PROTECTION_PRESERVE) {
568 u8 srp;
569 switch (mode) {
570 case SPI_WRITE_PROTECTION_NONE:
571 srp = 0;
572 break;
573 case SPI_WRITE_PROTECTION_PIN:
574 srp = 1;
575 break;
576 case SPI_WRITE_PROTECTION_REBOOT:
577 srp = 2;
578 break;
579 case SPI_WRITE_PROTECTION_PERMANENT:
580 srp = 3;
581 break;
582 default:
583 return -1;
586 if (params->bp_bits == 3) {
587 val.reg1.bp3.srp0 = !!(srp & 1);
588 mask.reg1.bp3.srp0 = 1;
589 } else {
590 val.reg1.bp4.srp0 = !!(srp & 1);
591 mask.reg1.bp4.srp0 = 1;
594 val.reg2.srp1 = !!(srp & 2);
595 mask.reg2.srp1 = 1;
598 ret = winbond_flash_cmd_status(flash, mask.u, val.u, true);
599 if (ret)
600 return ret;
602 printk(BIOS_DEBUG, "WINBOND: write-protection set to range "
603 "0x%08zx-0x%08zx\n", region_offset(region), region_last(region));
605 return ret;
608 static const struct spi_flash_protection_ops spi_flash_protection_ops = {
609 .get_write = winbond_get_write_protection,
610 .set_write = winbond_set_write_protection,
613 const struct spi_flash_vendor_info spi_flash_winbond_vi = {
614 .id = VENDOR_ID_WINBOND,
615 .page_size_shift = 8,
616 .sector_size_kib_shift = 2,
617 .match_id_mask[0] = 0xffff,
618 .ids = flash_table,
619 .nr_part_ids = ARRAY_SIZE(flash_table),
620 .desc = &spi_flash_pp_0x20_sector_desc,
621 .prot_ops = &spi_flash_protection_ops,