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 | |
| 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>
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 164 | ||||
| -rw-r--r-- | include/linux/mtd/nand.h | 10 |
2 files changed, 174 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); |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ccab9dfc5217..48bc2c54302c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -38,6 +38,12 @@ extern void nand_release (struct mtd_info *mtd); | |||
| 38 | /* Internal helper for board drivers which need to override command function */ | 38 | /* Internal helper for board drivers which need to override command function */ |
| 39 | extern void nand_wait_ready(struct mtd_info *mtd); | 39 | extern void nand_wait_ready(struct mtd_info *mtd); |
| 40 | 40 | ||
| 41 | /* locks all blockes present in the device */ | ||
| 42 | extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 43 | |||
| 44 | /* unlocks specified locked blockes */ | ||
| 45 | extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
