aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@free-electrons.com>2016-02-03 13:59:36 -0500
committerBoris Brezillon <boris.brezillon@free-electrons.com>2016-05-05 17:51:38 -0400
commitef5eeea6e911540bbf51a283fe0ffb7389cbe66a (patch)
tree812ca61ce5ed15e2e7b26b9530b8b99a28c992fc
parentc8766e81ca3a224b092d087f6e1d77bf2c3d665d (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.c258
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 */
786static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, 787static 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); 805static 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
840static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
841 .ecc = brcmnand_hamming_ooblayout_ecc,
842 .free = brcmnand_hamming_ooblayout_free,
843};
844
845static 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
886static struct nand_ecclayout *brcmstb_choose_ecc_layout( 863static 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
889static 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
912static 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
917static 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
922static 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
906static void brcmnand_wp(struct mtd_info *mtd, int wp) 962static 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;