Commit 03a539c7 authored by Mason Yang's avatar Mason Yang Committed by Miquel Raynal
Browse files

mtd: rawnand: Macronix: Add support for block protection



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: default avatarMason Yang <masonccyang@mxic.com.tw>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/1583220084-10890-3-git-send-email-masonccyang@mxic.com.tw
parent 92270086
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -11,6 +11,10 @@
#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;
}