diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-02-03 13:59:36 -0500 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-05-05 17:51:38 -0400 |
commit | ef5eeea6e911540bbf51a283fe0ffb7389cbe66a (patch) | |
tree | 812ca61ce5ed15e2e7b26b9530b8b99a28c992fc | |
parent | c8766e81ca3a224b092d087f6e1d77bf2c3d665d (diff) |
mtd: nand: brcm: switch to mtd_ooblayout_ops
Implementing the mtd_ooblayout_ops interface is the new way of exposing
ECC/OOB layout to MTD users.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-rw-r--r-- | drivers/mtd/nand/brcmnand/brcmnand.c | 258 |
1 files changed, 157 insertions, 101 deletions
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e5f281f148dc..c3331ffcaffd 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c | |||
@@ -780,127 +780,183 @@ static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg) | |||
780 | } | 780 | } |
781 | 781 | ||
782 | /* | 782 | /* |
783 | * Returns a nand_ecclayout strucutre for the given layout/configuration. | 783 | * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given |
784 | * Returns NULL on failure. | 784 | * the layout/configuration. |
785 | * Returns -ERRCODE on failure. | ||
785 | */ | 786 | */ |
786 | static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, | 787 | static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, |
787 | struct brcmnand_host *host) | 788 | struct mtd_oob_region *oobregion) |
788 | { | 789 | { |
790 | struct nand_chip *chip = mtd_to_nand(mtd); | ||
791 | struct brcmnand_host *host = nand_get_controller_data(chip); | ||
789 | struct brcmnand_cfg *cfg = &host->hwcfg; | 792 | struct brcmnand_cfg *cfg = &host->hwcfg; |
790 | int i, j; | 793 | int sas = cfg->spare_area_size << cfg->sector_size_1k; |
791 | struct nand_ecclayout *layout; | 794 | int sectors = cfg->page_size / (512 << cfg->sector_size_1k); |
792 | int req; | ||
793 | int sectors; | ||
794 | int sas; | ||
795 | int idx1, idx2; | ||
796 | |||
797 | layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); | ||
798 | if (!layout) | ||
799 | return NULL; | ||
800 | |||
801 | sectors = cfg->page_size / (512 << cfg->sector_size_1k); | ||
802 | sas = cfg->spare_area_size << cfg->sector_size_1k; | ||
803 | |||
804 | /* Hamming */ | ||
805 | if (is_hamming_ecc(cfg)) { | ||
806 | for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { | ||
807 | /* First sector of each page may have BBI */ | ||
808 | if (i == 0) { | ||
809 | layout->oobfree[idx2].offset = i * sas + 1; | ||
810 | /* Small-page NAND use byte 6 for BBI */ | ||
811 | if (cfg->page_size == 512) | ||
812 | layout->oobfree[idx2].offset--; | ||
813 | layout->oobfree[idx2].length = 5; | ||
814 | } else { | ||
815 | layout->oobfree[idx2].offset = i * sas; | ||
816 | layout->oobfree[idx2].length = 6; | ||
817 | } | ||
818 | idx2++; | ||
819 | layout->eccpos[idx1++] = i * sas + 6; | ||
820 | layout->eccpos[idx1++] = i * sas + 7; | ||
821 | layout->eccpos[idx1++] = i * sas + 8; | ||
822 | layout->oobfree[idx2].offset = i * sas + 9; | ||
823 | layout->oobfree[idx2].length = 7; | ||
824 | idx2++; | ||
825 | /* Leave zero-terminated entry for OOBFREE */ | ||
826 | if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || | ||
827 | idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) | ||
828 | break; | ||
829 | } | ||
830 | 795 | ||
831 | return layout; | 796 | if (section >= sectors) |
832 | } | 797 | return -ERANGE; |
833 | 798 | ||
834 | /* | 799 | oobregion->offset = (section * sas) + 6; |
835 | * CONTROLLER_VERSION: | 800 | oobregion->length = 3; |
836 | * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) | 801 | |
837 | * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) | 802 | return 0; |
838 | * But we will just be conservative. | 803 | } |
839 | */ | 804 | |
840 | req = DIV_ROUND_UP(ecc_level * 14, 8); | 805 | static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, |
841 | if (req >= sas) { | 806 | struct mtd_oob_region *oobregion) |
842 | dev_err(&host->pdev->dev, | 807 | { |
843 | "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", | 808 | struct nand_chip *chip = mtd_to_nand(mtd); |
844 | req, sas); | 809 | struct brcmnand_host *host = nand_get_controller_data(chip); |
845 | return NULL; | 810 | struct brcmnand_cfg *cfg = &host->hwcfg; |
846 | } | 811 | int sas = cfg->spare_area_size << cfg->sector_size_1k; |
812 | int sectors = cfg->page_size / (512 << cfg->sector_size_1k); | ||
813 | |||
814 | if (section >= sectors * 2) | ||
815 | return -ERANGE; | ||
847 | 816 | ||
848 | layout->eccbytes = req * sectors; | 817 | oobregion->offset = (section / 2) * sas; |
849 | for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { | 818 | |
850 | for (j = sas - req; j < sas && idx1 < | 819 | if (section & 1) { |
851 | MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) | 820 | oobregion->offset += 9; |
852 | layout->eccpos[idx1] = i * sas + j; | 821 | oobregion->length = 7; |
822 | } else { | ||
823 | oobregion->length = 6; | ||
853 | 824 | ||
854 | /* First sector of each page may have BBI */ | 825 | /* First sector of each page may have BBI */ |
855 | if (i == 0) { | 826 | if (!section) { |
856 | if (cfg->page_size == 512 && (sas - req >= 6)) { | 827 | /* |
857 | /* Small-page NAND use byte 6 for BBI */ | 828 | * Small-page NAND use byte 6 for BBI while large-page |
858 | layout->oobfree[idx2].offset = 0; | 829 | * NAND use byte 0. |
859 | layout->oobfree[idx2].length = 5; | 830 | */ |
860 | idx2++; | 831 | if (cfg->page_size > 512) |
861 | if (sas - req > 6) { | 832 | oobregion->offset++; |
862 | layout->oobfree[idx2].offset = 6; | 833 | oobregion->length--; |
863 | layout->oobfree[idx2].length = | ||
864 | sas - req - 6; | ||
865 | idx2++; | ||
866 | } | ||
867 | } else if (sas > req + 1) { | ||
868 | layout->oobfree[idx2].offset = i * sas + 1; | ||
869 | layout->oobfree[idx2].length = sas - req - 1; | ||
870 | idx2++; | ||
871 | } | ||
872 | } else if (sas > req) { | ||
873 | layout->oobfree[idx2].offset = i * sas; | ||
874 | layout->oobfree[idx2].length = sas - req; | ||
875 | idx2++; | ||
876 | } | 834 | } |
877 | /* Leave zero-terminated entry for OOBFREE */ | ||
878 | if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || | ||
879 | idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) | ||
880 | break; | ||
881 | } | 835 | } |
882 | 836 | ||
883 | return layout; | 837 | return 0; |
838 | } | ||
839 | |||
840 | static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { | ||
841 | .ecc = brcmnand_hamming_ooblayout_ecc, | ||
842 | .free = brcmnand_hamming_ooblayout_free, | ||
843 | }; | ||
844 | |||
845 | static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, | ||
846 | struct mtd_oob_region *oobregion) | ||
847 | { | ||
848 | struct nand_chip *chip = mtd_to_nand(mtd); | ||
849 | struct brcmnand_host *host = nand_get_controller_data(chip); | ||
850 | struct brcmnand_cfg *cfg = &host->hwcfg; | ||
851 | int sas = cfg->spare_area_size << cfg->sector_size_1k; | ||
852 | int sectors = cfg->page_size / (512 << cfg->sector_size_1k); | ||
853 | |||
854 | if (section >= sectors) | ||
855 | return -ERANGE; | ||
856 | |||
857 | oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; | ||
858 | oobregion->length = chip->ecc.bytes; | ||
859 | |||
860 | return 0; | ||
884 | } | 861 | } |
885 | 862 | ||
886 | static struct nand_ecclayout *brcmstb_choose_ecc_layout( | 863 | static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, |
887 | struct brcmnand_host *host) | 864 | struct mtd_oob_region *oobregion) |
865 | { | ||
866 | struct nand_chip *chip = mtd_to_nand(mtd); | ||
867 | struct brcmnand_host *host = nand_get_controller_data(chip); | ||
868 | struct brcmnand_cfg *cfg = &host->hwcfg; | ||
869 | int sas = cfg->spare_area_size << cfg->sector_size_1k; | ||
870 | int sectors = cfg->page_size / (512 << cfg->sector_size_1k); | ||
871 | |||
872 | if (section >= sectors) | ||
873 | return -ERANGE; | ||
874 | |||
875 | if (sas <= chip->ecc.bytes) | ||
876 | return 0; | ||
877 | |||
878 | oobregion->offset = section * sas; | ||
879 | oobregion->length = sas - chip->ecc.bytes; | ||
880 | |||
881 | if (!section) { | ||
882 | oobregion->offset++; | ||
883 | oobregion->length--; | ||
884 | } | ||
885 | |||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, | ||
890 | struct mtd_oob_region *oobregion) | ||
891 | { | ||
892 | struct nand_chip *chip = mtd_to_nand(mtd); | ||
893 | struct brcmnand_host *host = nand_get_controller_data(chip); | ||
894 | struct brcmnand_cfg *cfg = &host->hwcfg; | ||
895 | int sas = cfg->spare_area_size << cfg->sector_size_1k; | ||
896 | |||
897 | if (section > 1 || sas - chip->ecc.bytes < 6 || | ||
898 | (section && sas - chip->ecc.bytes == 6)) | ||
899 | return -ERANGE; | ||
900 | |||
901 | if (!section) { | ||
902 | oobregion->offset = 0; | ||
903 | oobregion->length = 5; | ||
904 | } else { | ||
905 | oobregion->offset = 6; | ||
906 | oobregion->length = sas - chip->ecc.bytes - 6; | ||
907 | } | ||
908 | |||
909 | return 0; | ||
910 | } | ||
911 | |||
912 | static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { | ||
913 | .ecc = brcmnand_bch_ooblayout_ecc, | ||
914 | .free = brcmnand_bch_ooblayout_free_lp, | ||
915 | }; | ||
916 | |||
917 | static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { | ||
918 | .ecc = brcmnand_bch_ooblayout_ecc, | ||
919 | .free = brcmnand_bch_ooblayout_free_sp, | ||
920 | }; | ||
921 | |||
922 | static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) | ||
888 | { | 923 | { |
889 | struct nand_ecclayout *layout; | ||
890 | struct brcmnand_cfg *p = &host->hwcfg; | 924 | struct brcmnand_cfg *p = &host->hwcfg; |
925 | struct mtd_info *mtd = nand_to_mtd(&host->chip); | ||
926 | struct nand_ecc_ctrl *ecc = &host->chip.ecc; | ||
891 | unsigned int ecc_level = p->ecc_level; | 927 | unsigned int ecc_level = p->ecc_level; |
928 | int sas = p->spare_area_size << p->sector_size_1k; | ||
929 | int sectors = p->page_size / (512 << p->sector_size_1k); | ||
892 | 930 | ||
893 | if (p->sector_size_1k) | 931 | if (p->sector_size_1k) |
894 | ecc_level <<= 1; | 932 | ecc_level <<= 1; |
895 | 933 | ||
896 | layout = brcmnand_create_layout(ecc_level, host); | 934 | if (is_hamming_ecc(p)) { |
897 | if (!layout) { | 935 | ecc->bytes = 3 * sectors; |
936 | mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); | ||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | /* | ||
941 | * CONTROLLER_VERSION: | ||
942 | * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) | ||
943 | * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) | ||
944 | * But we will just be conservative. | ||
945 | */ | ||
946 | ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); | ||
947 | if (p->page_size == 512) | ||
948 | mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); | ||
949 | else | ||
950 | mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); | ||
951 | |||
952 | if (ecc->bytes >= sas) { | ||
898 | dev_err(&host->pdev->dev, | 953 | dev_err(&host->pdev->dev, |
899 | "no proper ecc_layout for this NAND cfg\n"); | 954 | "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", |
900 | return NULL; | 955 | ecc->bytes, sas); |
956 | return -EINVAL; | ||
901 | } | 957 | } |
902 | 958 | ||
903 | return layout; | 959 | return 0; |
904 | } | 960 | } |
905 | 961 | ||
906 | static void brcmnand_wp(struct mtd_info *mtd, int wp) | 962 | static void brcmnand_wp(struct mtd_info *mtd, int wp) |
@@ -2010,9 +2066,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) | |||
2010 | /* only use our internal HW threshold */ | 2066 | /* only use our internal HW threshold */ |
2011 | mtd->bitflip_threshold = 1; | 2067 | mtd->bitflip_threshold = 1; |
2012 | 2068 | ||
2013 | chip->ecc.layout = brcmstb_choose_ecc_layout(host); | 2069 | ret = brcmstb_choose_ecc_layout(host); |
2014 | if (!chip->ecc.layout) | 2070 | if (ret) |
2015 | return -ENXIO; | 2071 | return ret; |
2016 | 2072 | ||
2017 | if (nand_scan_tail(mtd)) | 2073 | if (nand_scan_tail(mtd)) |
2018 | return -ENXIO; | 2074 | return -ENXIO; |