mtd: rawnand: Macronix: Add support for block protection
authorMason Yang <masonccyang@mxic.com.tw>
Tue, 3 Mar 2020 07:21:22 +0000 (15:21 +0800)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 11 Mar 2020 15:17:55 +0000 (16:17 +0100)
Macronix AC/AD series support using SET_FEATURES to change block
protection and unprotection. Block protection support can be checked
with GET_FEATURES.

Signed-off-by: Mason Yang <masonccyang@mxic.com.tw>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/1583220084-10890-3-git-send-email-masonccyang@mxic.com.tw
drivers/mtd/nand/raw/nand_macronix.c

index 0a2fe25f263925af296cdd6994fc1dbf7c0e6b86..fbe2fff0cf1b34def9f7cf4462cf70b2f744db98 100644 (file)
 #define MACRONIX_READ_RETRY_BIT BIT(0)
 #define MACRONIX_NUM_READ_RETRY_MODES 6
 
+#define ONFI_FEATURE_ADDR_MXIC_PROTECTION 0xA0
+#define MXIC_BLOCK_PROTECTION_ALL_LOCK 0x38
+#define MXIC_BLOCK_PROTECTION_ALL_UNLOCK 0x0
+
 #define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0
 #define MACRONIX_RANDOMIZER_BIT BIT(1)
 #define MACRONIX_RANDOMIZER_ENPGM BIT(0)
@@ -172,6 +176,73 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip)
                     ONFI_FEATURE_ADDR_TIMING_MODE, 1);
 }
 
+/*
+ * Macronix NAND supports Block Protection by Protectoin(PT) pin;
+ * active high at power-on which protects the entire chip even the #WP is
+ * disabled. Lock/unlock protection area can be partition according to
+ * protection bits, i.e. upper 1/2 locked, upper 1/4 locked and so on.
+ */
+static int mxic_nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
+{
+       u8 feature[ONFI_SUBFEATURE_PARAM_LEN];
+       int ret;
+
+       feature[0] = MXIC_BLOCK_PROTECTION_ALL_LOCK;
+       nand_select_target(chip, 0);
+       ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION,
+                               feature);
+       nand_deselect_target(chip);
+       if (ret)
+               pr_err("%s all blocks failed\n", __func__);
+
+       return ret;
+}
+
+static int mxic_nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
+{
+       u8 feature[ONFI_SUBFEATURE_PARAM_LEN];
+       int ret;
+
+       feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK;
+       nand_select_target(chip, 0);
+       ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION,
+                               feature);
+       nand_deselect_target(chip);
+       if (ret)
+               pr_err("%s all blocks failed\n", __func__);
+
+       return ret;
+}
+
+static void macronix_nand_block_protection_support(struct nand_chip *chip)
+{
+       u8 feature[ONFI_SUBFEATURE_PARAM_LEN];
+       int ret;
+
+       bitmap_set(chip->parameters.get_feature_list,
+                  ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
+
+       feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK;
+       nand_select_target(chip, 0);
+       ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION,
+                               feature);
+       nand_deselect_target(chip);
+       if (ret || feature[0] != MXIC_BLOCK_PROTECTION_ALL_LOCK) {
+               if (ret)
+                       pr_err("Block protection check failed\n");
+
+               bitmap_clear(chip->parameters.get_feature_list,
+                            ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
+               return;
+       }
+
+       bitmap_set(chip->parameters.set_feature_list,
+                  ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
+
+       chip->lock_area = mxic_nand_lock;
+       chip->unlock_area = mxic_nand_unlock;
+}
+
 static int macronix_nand_init(struct nand_chip *chip)
 {
        if (nand_is_slc(chip))
@@ -179,6 +250,7 @@ static int macronix_nand_init(struct nand_chip *chip)
 
        macronix_nand_fix_broken_get_timings(chip);
        macronix_nand_onfi_init(chip);
+       macronix_nand_block_protection_support(chip);
 
        return 0;
 }