Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d70f334 authored by Vimal Singh's avatar Vimal Singh Committed by David Woodhouse
Browse files

mtd: nand: add lock/unlock routines



Add nand lock / unlock routines. At least 'micron' parts
support this.

Signed-off-by: default avatarVimal Singh <vimalsingh@ti.com>
Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 6fe5a6ac
Loading
Loading
Loading
Loading
+164 −0
Original line number Diff line number Diff line
@@ -863,6 +863,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
	return status;
}

/**
 * __nand_unlock - [REPLACABLE] unlocks specified locked blockes
 *
 * @param mtd - mtd info
 * @param ofs - offset to start unlock from
 * @param len - length to unlock
 * @invert -  when = 0, unlock the range of blocks within the lower and
 *                      upper boundary address
 *            whne = 1, unlock the range of blocks outside the boundaries
 *                      of the lower and upper boundary address
 *
 * @return - unlock status
 */
static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
					uint64_t len, int invert)
{
	int ret = 0;
	int status, page;
	struct nand_chip *chip = mtd->priv;

	/* Submit address of first page to unlock */
	page = ofs >> chip->page_shift;
	chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);

	/* Submit address of last page to unlock */
	page = (ofs + len) >> chip->page_shift;
	chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1,
				(page | invert) & chip->pagemask);

	/* Call wait ready function */
	status = chip->waitfunc(mtd, chip);
	udelay(1000);
	/* See if device thinks it succeeded */
	if (status & 0x01) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
					__func__, status);
		ret = -EIO;
	}

	return ret;
}

/**
 * nand_unlock - [REPLACABLE] unlocks specified locked blockes
 *
 * @param mtd - mtd info
 * @param ofs - offset to start unlock from
 * @param len - length to unlock
 *
 * @return - unlock status
 */
int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
	int ret = 0;
	int chipnr;
	struct nand_chip *chip = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
			__func__, (unsigned long long)ofs, len);

	if (check_offs_len(mtd, ofs, len))
		ret = -EINVAL;

	/* Align to last block address if size addresses end of the device */
	if (ofs + len == mtd->size)
		len -= mtd->erasesize;

	nand_get_device(chip, mtd, FL_UNLOCKING);

	/* Shift to get chip number */
	chipnr = ofs >> chip->chip_shift;

	chip->select_chip(mtd, chipnr);

	/* Check, if it is write protected */
	if (nand_check_wp(mtd)) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
					__func__);
		ret = -EIO;
		goto out;
	}

	ret = __nand_unlock(mtd, ofs, len, 0);

out:
	/* de-select the NAND device */
	chip->select_chip(mtd, -1);

	nand_release_device(mtd);

	return ret;
}

/**
 * nand_lock - [REPLACABLE] locks all blockes present in the device
 *
 * @param mtd - mtd info
 * @param ofs - offset to start unlock from
 * @param len - length to unlock
 *
 * @return - lock status
 *
 * This feature is not support in many NAND parts. 'Micron' NAND parts
 * do have this feature, but it allows only to lock all blocks not for
 * specified range for block.
 *
 * Implementing 'lock' feature by making use of 'unlock', for now.
 */
int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
	int ret = 0;
	int chipnr, status, page;
	struct nand_chip *chip = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
			__func__, (unsigned long long)ofs, len);

	if (check_offs_len(mtd, ofs, len))
		ret = -EINVAL;

	nand_get_device(chip, mtd, FL_LOCKING);

	/* Shift to get chip number */
	chipnr = ofs >> chip->chip_shift;

	chip->select_chip(mtd, chipnr);

	/* Check, if it is write protected */
	if (nand_check_wp(mtd)) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
					__func__);
		status = MTD_ERASE_FAILED;
		ret = -EIO;
		goto out;
	}

	/* Submit address of first page to lock */
	page = ofs >> chip->page_shift;
	chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask);

	/* Call wait ready function */
	status = chip->waitfunc(mtd, chip);
	udelay(1000);
	/* See if device thinks it succeeded */
	if (status & 0x01) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
					__func__, status);
		ret = -EIO;
		goto out;
	}

	ret = __nand_unlock(mtd, ofs, len, 0x1);

out:
	/* de-select the NAND device */
	chip->select_chip(mtd, -1);

	nand_release_device(mtd);

	return ret;
}

/**
 * nand_read_page_raw - [Intern] read raw page data without ecc
 * @mtd:	mtd info structure
@@ -3089,6 +3251,8 @@ void nand_release(struct mtd_info *mtd)
		kfree(chip->buffers);
}

EXPORT_SYMBOL_GPL(nand_lock);
EXPORT_SYMBOL_GPL(nand_unlock);
EXPORT_SYMBOL_GPL(nand_scan);
EXPORT_SYMBOL_GPL(nand_scan_ident);
EXPORT_SYMBOL_GPL(nand_scan_tail);
+10 −0
Original line number Diff line number Diff line
@@ -38,6 +38,12 @@ extern void nand_release (struct mtd_info *mtd);
/* Internal helper for board drivers which need to override command function */
extern void nand_wait_ready(struct mtd_info *mtd);

/* locks all blockes present in the device */
extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);

/* unlocks specified locked blockes */
extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);

/* The maximum number of NAND chips in an array */
#define NAND_MAX_CHIPS		8

@@ -82,6 +88,10 @@ extern void nand_wait_ready(struct mtd_info *mtd);
#define NAND_CMD_ERASE2		0xd0
#define NAND_CMD_RESET		0xff

#define NAND_CMD_LOCK		0x2a
#define NAND_CMD_UNLOCK1	0x23
#define NAND_CMD_UNLOCK2	0x24

/* Extended commands for large page devices */
#define NAND_CMD_READSTART	0x30
#define NAND_CMD_RNDOUTSTART	0xE0