|
@@ -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);
|