diff options
author | Vimal Singh <vimal.newwork@gmail.com> | 2010-02-08 05:20:49 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-02-26 08:21:49 -0500 |
commit | 7d70f334ad2bf1b3aaa1f0699c0f442e14bcc9e0 (patch) | |
tree | 2d2b23d7d8145c2fb6b6414708732facdeb27fe7 /drivers/mtd | |
parent | 6fe5a6acdc126107e54a6c584536e09ab7dde949 (diff) |
mtd: nand: add lock/unlock routines
Add nand lock / unlock routines. At least 'micron' parts
support this.
Signed-off-by: Vimal Singh <vimalsingh@ti.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2dfeb4bea83a..ed62e1ee0f81 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -864,6 +864,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
864 | } | 864 | } |
865 | 865 | ||
866 | /** | 866 | /** |
867 | * __nand_unlock - [REPLACABLE] unlocks specified locked blockes | ||
868 | * | ||
869 | * @param mtd - mtd info | ||
870 | * @param ofs - offset to start unlock from | ||
871 | * @param len - length to unlock | ||
872 | * @invert - when = 0, unlock the range of blocks within the lower and | ||
873 | * upper boundary address | ||
874 | * whne = 1, unlock the range of blocks outside the boundaries | ||
875 | * of the lower and upper boundary address | ||
876 | * | ||
877 | * @return - unlock status | ||
878 | */ | ||
879 | static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, | ||
880 | uint64_t len, int invert) | ||
881 | { | ||
882 | int ret = 0; | ||
883 | int status, page; | ||
884 | struct nand_chip *chip = mtd->priv; | ||
885 | |||
886 | /* Submit address of first page to unlock */ | ||
887 | page = ofs >> chip->page_shift; | ||
888 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); | ||
889 | |||
890 | /* Submit address of last page to unlock */ | ||
891 | page = (ofs + len) >> chip->page_shift; | ||
892 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, | ||
893 | (page | invert) & chip->pagemask); | ||
894 | |||
895 | /* Call wait ready function */ | ||
896 | status = chip->waitfunc(mtd, chip); | ||
897 | udelay(1000); | ||
898 | /* See if device thinks it succeeded */ | ||
899 | if (status & 0x01) { | ||
900 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", | ||
901 | __func__, status); | ||
902 | ret = -EIO; | ||
903 | } | ||
904 | |||
905 | return ret; | ||
906 | } | ||
907 | |||
908 | /** | ||
909 | * nand_unlock - [REPLACABLE] unlocks specified locked blockes | ||
910 | * | ||
911 | * @param mtd - mtd info | ||
912 | * @param ofs - offset to start unlock from | ||
913 | * @param len - length to unlock | ||
914 | * | ||
915 | * @return - unlock status | ||
916 | */ | ||
917 | int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
918 | { | ||
919 | int ret = 0; | ||
920 | int chipnr; | ||
921 | struct nand_chip *chip = mtd->priv; | ||
922 | |||
923 | DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", | ||
924 | __func__, (unsigned long long)ofs, len); | ||
925 | |||
926 | if (check_offs_len(mtd, ofs, len)) | ||
927 | ret = -EINVAL; | ||
928 | |||
929 | /* Align to last block address if size addresses end of the device */ | ||
930 | if (ofs + len == mtd->size) | ||
931 | len -= mtd->erasesize; | ||
932 | |||
933 | nand_get_device(chip, mtd, FL_UNLOCKING); | ||
934 | |||
935 | /* Shift to get chip number */ | ||
936 | chipnr = ofs >> chip->chip_shift; | ||
937 | |||
938 | chip->select_chip(mtd, chipnr); | ||
939 | |||
940 | /* Check, if it is write protected */ | ||
941 | if (nand_check_wp(mtd)) { | ||
942 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", | ||
943 | __func__); | ||
944 | ret = -EIO; | ||
945 | goto out; | ||
946 | } | ||
947 | |||
948 | ret = __nand_unlock(mtd, ofs, len, 0); | ||
949 | |||
950 | out: | ||
951 | /* de-select the NAND device */ | ||
952 | chip->select_chip(mtd, -1); | ||
953 | |||
954 | nand_release_device(mtd); | ||
955 | |||
956 | return ret; | ||
957 | } | ||
958 | |||
959 | /** | ||
960 | * nand_lock - [REPLACABLE] locks all blockes present in the device | ||
961 | * | ||
962 | * @param mtd - mtd info | ||
963 | * @param ofs - offset to start unlock from | ||
964 | * @param len - length to unlock | ||
965 | * | ||
966 | * @return - lock status | ||
967 | * | ||
968 | * This feature is not support in many NAND parts. 'Micron' NAND parts | ||
969 | * do have this feature, but it allows only to lock all blocks not for | ||
970 | * specified range for block. | ||
971 | * | ||
972 | * Implementing 'lock' feature by making use of 'unlock', for now. | ||
973 | */ | ||
974 | int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
975 | { | ||
976 | int ret = 0; | ||
977 | int chipnr, status, page; | ||
978 | struct nand_chip *chip = mtd->priv; | ||
979 | |||
980 | DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", | ||
981 | __func__, (unsigned long long)ofs, len); | ||
982 | |||
983 | if (check_offs_len(mtd, ofs, len)) | ||
984 | ret = -EINVAL; | ||
985 | |||
986 | nand_get_device(chip, mtd, FL_LOCKING); | ||
987 | |||
988 | /* Shift to get chip number */ | ||
989 | chipnr = ofs >> chip->chip_shift; | ||
990 | |||
991 | chip->select_chip(mtd, chipnr); | ||
992 | |||
993 | /* Check, if it is write protected */ | ||
994 | if (nand_check_wp(mtd)) { | ||
995 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", | ||
996 | __func__); | ||
997 | status = MTD_ERASE_FAILED; | ||
998 | ret = -EIO; | ||
999 | goto out; | ||
1000 | } | ||
1001 | |||
1002 | /* Submit address of first page to lock */ | ||
1003 | page = ofs >> chip->page_shift; | ||
1004 | chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); | ||
1005 | |||
1006 | /* Call wait ready function */ | ||
1007 | status = chip->waitfunc(mtd, chip); | ||
1008 | udelay(1000); | ||
1009 | /* See if device thinks it succeeded */ | ||
1010 | if (status & 0x01) { | ||
1011 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", | ||
1012 | __func__, status); | ||
1013 | ret = -EIO; | ||
1014 | goto out; | ||
1015 | } | ||
1016 | |||
1017 | ret = __nand_unlock(mtd, ofs, len, 0x1); | ||
1018 | |||
1019 | out: | ||
1020 | /* de-select the NAND device */ | ||
1021 | chip->select_chip(mtd, -1); | ||
1022 | |||
1023 | nand_release_device(mtd); | ||
1024 | |||
1025 | return ret; | ||
1026 | } | ||
1027 | |||
1028 | /** | ||
867 | * nand_read_page_raw - [Intern] read raw page data without ecc | 1029 | * nand_read_page_raw - [Intern] read raw page data without ecc |
868 | * @mtd: mtd info structure | 1030 | * @mtd: mtd info structure |
869 | * @chip: nand chip info structure | 1031 | * @chip: nand chip info structure |
@@ -3089,6 +3251,8 @@ void nand_release(struct mtd_info *mtd) | |||
3089 | kfree(chip->buffers); | 3251 | kfree(chip->buffers); |
3090 | } | 3252 | } |
3091 | 3253 | ||
3254 | EXPORT_SYMBOL_GPL(nand_lock); | ||
3255 | EXPORT_SYMBOL_GPL(nand_unlock); | ||
3092 | EXPORT_SYMBOL_GPL(nand_scan); | 3256 | EXPORT_SYMBOL_GPL(nand_scan); |
3093 | EXPORT_SYMBOL_GPL(nand_scan_ident); | 3257 | EXPORT_SYMBOL_GPL(nand_scan_ident); |
3094 | EXPORT_SYMBOL_GPL(nand_scan_tail); | 3258 | EXPORT_SYMBOL_GPL(nand_scan_tail); |