summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorKamal Dasu <kdasu.kdev@gmail.com>2017-03-03 16:16:53 -0500
committerBoris Brezillon <boris.brezillon@free-electrons.com>2017-04-25 08:18:42 -0400
commit9d2ee0a60b8bd9bef2a0082c533736d6a7b39873 (patch)
tree08104bffea7783a3211f338b5c7cacdc2aa1cd08 /drivers/mtd/nand
parent65a2c1caa70f71690dcb5afd8fc6afe67fcde599 (diff)
mtd: nand: brcmnand: Check flash #WP pin status before nand erase/program
On brcmnand controller v6.x and v7.x, the #WP pin is controlled through the NAND_WP bit in CS_SELECT register. The driver currently assumes that toggling the #WP pin is instantaneously enabling/disabling write-protection, but it actually takes some time to propagate the new state to the internal NAND chip logic. This behavior is sometime causing data corruptions when an erase/program operation is executed before write-protection has really been disabled. Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller") Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c61
1 files changed, 58 insertions, 3 deletions
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 42ebd73f821d..7419c5ce63f8 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -101,6 +101,9 @@ struct brcm_nand_dma_desc {
101#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) 101#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
102#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) 102#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
103 103
104#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)
105#define NAND_POLL_STATUS_TIMEOUT_MS 100
106
104/* Controller feature flags */ 107/* Controller feature flags */
105enum { 108enum {
106 BRCMNAND_HAS_1K_SECTORS = BIT(0), 109 BRCMNAND_HAS_1K_SECTORS = BIT(0),
@@ -765,6 +768,31 @@ enum {
765 CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), 768 CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
766}; 769};
767 770
771static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
772 u32 mask, u32 expected_val,
773 unsigned long timeout_ms)
774{
775 unsigned long limit;
776 u32 val;
777
778 if (!timeout_ms)
779 timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
780
781 limit = jiffies + msecs_to_jiffies(timeout_ms);
782 do {
783 val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
784 if ((val & mask) == expected_val)
785 return 0;
786
787 cpu_relax();
788 } while (time_after(limit, jiffies));
789
790 dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
791 expected_val, val & mask);
792
793 return -ETIMEDOUT;
794}
795
768static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) 796static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
769{ 797{
770 u32 val = en ? CS_SELECT_NAND_WP : 0; 798 u32 val = en ? CS_SELECT_NAND_WP : 0;
@@ -1024,12 +1052,39 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp)
1024 1052
1025 if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { 1053 if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
1026 static int old_wp = -1; 1054 static int old_wp = -1;
1055 int ret;
1027 1056
1028 if (old_wp != wp) { 1057 if (old_wp != wp) {
1029 dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); 1058 dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
1030 old_wp = wp; 1059 old_wp = wp;
1031 } 1060 }
1061
1062 /*
1063 * make sure ctrl/flash ready before and after
1064 * changing state of #WP pin
1065 */
1066 ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
1067 NAND_STATUS_READY,
1068 NAND_CTRL_RDY |
1069 NAND_STATUS_READY, 0);
1070 if (ret)
1071 return;
1072
1032 brcmnand_set_wp(ctrl, wp); 1073 brcmnand_set_wp(ctrl, wp);
1074 chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
1075 /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
1076 ret = bcmnand_ctrl_poll_status(ctrl,
1077 NAND_CTRL_RDY |
1078 NAND_STATUS_READY |
1079 NAND_STATUS_WP,
1080 NAND_CTRL_RDY |
1081 NAND_STATUS_READY |
1082 (wp ? 0 : NAND_STATUS_WP), 0);
1083
1084 if (ret)
1085 dev_err_ratelimited(&host->pdev->dev,
1086 "nand #WP expected %s\n",
1087 wp ? "on" : "off");
1033 } 1088 }
1034} 1089}
1035 1090
@@ -1157,15 +1212,15 @@ static irqreturn_t brcmnand_dma_irq(int irq, void *data)
1157static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) 1212static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
1158{ 1213{
1159 struct brcmnand_controller *ctrl = host->ctrl; 1214 struct brcmnand_controller *ctrl = host->ctrl;
1160 u32 intfc; 1215 int ret;
1161 1216
1162 dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, 1217 dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
1163 brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); 1218 brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
1164 BUG_ON(ctrl->cmd_pending != 0); 1219 BUG_ON(ctrl->cmd_pending != 0);
1165 ctrl->cmd_pending = cmd; 1220 ctrl->cmd_pending = cmd;
1166 1221
1167 intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); 1222 ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
1168 WARN_ON(!(intfc & INTFC_CTLR_READY)); 1223 WARN_ON(ret);
1169 1224
1170 mb(); /* flush previous writes */ 1225 mb(); /* flush previous writes */
1171 brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, 1226 brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,