aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorBrian Norris <computersforpeace@gmail.com>2016-01-29 14:25:36 -0500
committerBrian Norris <computersforpeace@gmail.com>2016-03-07 21:01:57 -0500
commit3dd8012a8eeb3702fa17450ec1a16a3f38af138d (patch)
tree699d4370d9e17fd294f695f7942debd3684c7a5c /drivers/mtd
parent76a4707de5e18dc32d9cb4e990686140c5664a15 (diff)
mtd: spi-nor: add TB (Top/Bottom) protect support
Some flash support a bit in the status register that inverts protection so that it applies to the bottom of the flash, not the top. This yields additions to the protection range table, as noted in the comments. Because this feature is not universal to all flash that support lock/unlock, control it via a new flag. Signed-off-by: Brian Norris <computersforpeace@gmail.com> Tested-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c70
1 files changed, 63 insertions, 7 deletions
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d1edafc51cbb..2aebf18f7c36 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -70,6 +70,11 @@ struct flash_info {
70#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */ 70#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
71#define USE_FSR BIT(7) /* use flag status register */ 71#define USE_FSR BIT(7) /* use flag status register */
72#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */ 72#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
73#define SPI_NOR_HAS_TB BIT(9) /*
74 * Flash SR has Top/Bottom (TB) protect
75 * bit. Must be used with
76 * SPI_NOR_HAS_LOCK.
77 */
73}; 78};
74 79
75#define JEDEC_MFR(info) ((info)->id[0]) 80#define JEDEC_MFR(info) ((info)->id[0])
@@ -435,7 +440,10 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
435 } else { 440 } else {
436 pow = ((sr & mask) ^ mask) >> shift; 441 pow = ((sr & mask) ^ mask) >> shift;
437 *len = mtd->size >> pow; 442 *len = mtd->size >> pow;
438 *ofs = mtd->size - *len; 443 if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
444 *ofs = 0;
445 else
446 *ofs = mtd->size - *len;
439 } 447 }
440} 448}
441 449
@@ -476,12 +484,14 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
476 484
477/* 485/*
478 * Lock a region of the flash. Compatible with ST Micro and similar flash. 486 * Lock a region of the flash. Compatible with ST Micro and similar flash.
479 * Supports only the block protection bits BP{0,1,2} in the status register 487 * Supports the block protection bits BP{0,1,2} in the status register
480 * (SR). Does not support these features found in newer SR bitfields: 488 * (SR). Does not support these features found in newer SR bitfields:
481 * - TB: top/bottom protect - only handle TB=0 (top protect)
482 * - SEC: sector/block protect - only handle SEC=0 (block protect) 489 * - SEC: sector/block protect - only handle SEC=0 (block protect)
483 * - CMP: complement protect - only support CMP=0 (range is not complemented) 490 * - CMP: complement protect - only support CMP=0 (range is not complemented)
484 * 491 *
492 * Support for the following is provided conditionally for some flash:
493 * - TB: top/bottom protect
494 *
485 * Sample table portion for 8MB flash (Winbond w25q64fw): 495 * Sample table portion for 8MB flash (Winbond w25q64fw):
486 * 496 *
487 * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion 497 * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
@@ -494,6 +504,13 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
494 * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 504 * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
495 * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 505 * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
496 * X | X | 1 | 1 | 1 | 8 MB | ALL 506 * X | X | 1 | 1 | 1 | 8 MB | ALL
507 * ------|-------|-------|-------|-------|---------------|-------------------
508 * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64
509 * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32
510 * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16
511 * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8
512 * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4
513 * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2
497 * 514 *
498 * Returns negative on errors, 0 on success. 515 * Returns negative on errors, 0 on success.
499 */ 516 */
@@ -504,6 +521,8 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
504 u8 mask = SR_BP2 | SR_BP1 | SR_BP0; 521 u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
505 u8 shift = ffs(mask) - 1, pow, val; 522 u8 shift = ffs(mask) - 1, pow, val;
506 loff_t lock_len; 523 loff_t lock_len;
524 bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
525 bool use_top;
507 int ret; 526 int ret;
508 527
509 status_old = read_sr(nor); 528 status_old = read_sr(nor);
@@ -514,13 +533,26 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
514 if (stm_is_locked_sr(nor, ofs, len, status_old)) 533 if (stm_is_locked_sr(nor, ofs, len, status_old))
515 return 0; 534 return 0;
516 535
536 /* If anything below us is unlocked, we can't use 'bottom' protection */
537 if (!stm_is_locked_sr(nor, 0, ofs, status_old))
538 can_be_bottom = false;
539
517 /* If anything above us is unlocked, we can't use 'top' protection */ 540 /* If anything above us is unlocked, we can't use 'top' protection */
518 if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len), 541 if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
519 status_old)) 542 status_old))
543 can_be_top = false;
544
545 if (!can_be_bottom && !can_be_top)
520 return -EINVAL; 546 return -EINVAL;
521 547
548 /* Prefer top, if both are valid */
549 use_top = can_be_top;
550
522 /* lock_len: length of region that should end up locked */ 551 /* lock_len: length of region that should end up locked */
523 lock_len = mtd->size - ofs; 552 if (use_top)
553 lock_len = mtd->size - ofs;
554 else
555 lock_len = ofs + len;
524 556
525 /* 557 /*
526 * Need smallest pow such that: 558 * Need smallest pow such that:
@@ -539,11 +571,14 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
539 if (!(val & mask)) 571 if (!(val & mask))
540 return -EINVAL; 572 return -EINVAL;
541 573
542 status_new = (status_old & ~mask) | val; 574 status_new = (status_old & ~mask & ~SR_TB) | val;
543 575
544 /* Disallow further writes if WP pin is asserted */ 576 /* Disallow further writes if WP pin is asserted */
545 status_new |= SR_SRWD; 577 status_new |= SR_SRWD;
546 578
579 if (!use_top)
580 status_new |= SR_TB;
581
547 /* Don't bother if they're the same */ 582 /* Don't bother if they're the same */
548 if (status_new == status_old) 583 if (status_new == status_old)
549 return 0; 584 return 0;
@@ -571,6 +606,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
571 u8 mask = SR_BP2 | SR_BP1 | SR_BP0; 606 u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
572 u8 shift = ffs(mask) - 1, pow, val; 607 u8 shift = ffs(mask) - 1, pow, val;
573 loff_t lock_len; 608 loff_t lock_len;
609 bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
610 bool use_top;
574 int ret; 611 int ret;
575 612
576 status_old = read_sr(nor); 613 status_old = read_sr(nor);
@@ -583,10 +620,24 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
583 620
584 /* If anything below us is locked, we can't use 'top' protection */ 621 /* If anything below us is locked, we can't use 'top' protection */
585 if (!stm_is_unlocked_sr(nor, 0, ofs, status_old)) 622 if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))
623 can_be_top = false;
624
625 /* If anything above us is locked, we can't use 'bottom' protection */
626 if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
627 status_old))
628 can_be_bottom = false;
629
630 if (!can_be_bottom && !can_be_top)
586 return -EINVAL; 631 return -EINVAL;
587 632
633 /* Prefer top, if both are valid */
634 use_top = can_be_top;
635
588 /* lock_len: length of region that should remain locked */ 636 /* lock_len: length of region that should remain locked */
589 lock_len = mtd->size - (ofs + len); 637 if (use_top)
638 lock_len = mtd->size - (ofs + len);
639 else
640 lock_len = ofs;
590 641
591 /* 642 /*
592 * Need largest pow such that: 643 * Need largest pow such that:
@@ -607,12 +658,15 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
607 return -EINVAL; 658 return -EINVAL;
608 } 659 }
609 660
610 status_new = (status_old & ~mask) | val; 661 status_new = (status_old & ~mask & ~SR_TB) | val;
611 662
612 /* Don't protect status register if we're fully unlocked */ 663 /* Don't protect status register if we're fully unlocked */
613 if (lock_len == mtd->size) 664 if (lock_len == mtd->size)
614 status_new &= ~SR_SRWD; 665 status_new &= ~SR_SRWD;
615 666
667 if (!use_top)
668 status_new |= SR_TB;
669
616 /* Don't bother if they're the same */ 670 /* Don't bother if they're the same */
617 if (status_new == status_old) 671 if (status_new == status_old)
618 return 0; 672 return 0;
@@ -1277,6 +1331,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
1277 1331
1278 if (info->flags & USE_FSR) 1332 if (info->flags & USE_FSR)
1279 nor->flags |= SNOR_F_USE_FSR; 1333 nor->flags |= SNOR_F_USE_FSR;
1334 if (info->flags & SPI_NOR_HAS_TB)
1335 nor->flags |= SNOR_F_HAS_SR_TB;
1280 1336
1281#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS 1337#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
1282 /* prefer "small sector" erase if possible */ 1338 /* prefer "small sector" erase if possible */