diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 5aaa7f541ea1..bb77f750e75a 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/of_device.h> | 27 | #include <linux/of_device.h> |
28 | #include <linux/of_mtd.h> | 28 | #include <linux/of_mtd.h> |
29 | #include "gpmi-nand.h" | 29 | #include "gpmi-nand.h" |
30 | #include "bch-regs.h" | ||
30 | 31 | ||
31 | /* Resource names for the GPMI NAND driver. */ | 32 | /* Resource names for the GPMI NAND driver. */ |
32 | #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" | 33 | #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" |
@@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
1049 | return max_bitflips; | 1050 | return max_bitflips; |
1050 | } | 1051 | } |
1051 | 1052 | ||
1053 | /* Fake a virtual small page for the subpage read */ | ||
1054 | static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, | ||
1055 | uint32_t offs, uint32_t len, uint8_t *buf, int page) | ||
1056 | { | ||
1057 | struct gpmi_nand_data *this = chip->priv; | ||
1058 | void __iomem *bch_regs = this->resources.bch_regs; | ||
1059 | struct bch_geometry old_geo = this->bch_geometry; | ||
1060 | struct bch_geometry *geo = &this->bch_geometry; | ||
1061 | int size = chip->ecc.size; /* ECC chunk size */ | ||
1062 | int meta, n, page_size; | ||
1063 | u32 r1_old, r2_old, r1_new, r2_new; | ||
1064 | unsigned int max_bitflips; | ||
1065 | int first, last, marker_pos; | ||
1066 | int ecc_parity_size; | ||
1067 | int col = 0; | ||
1068 | |||
1069 | /* The size of ECC parity */ | ||
1070 | ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; | ||
1071 | |||
1072 | /* Align it with the chunk size */ | ||
1073 | first = offs / size; | ||
1074 | last = (offs + len - 1) / size; | ||
1075 | |||
1076 | /* | ||
1077 | * Find the chunk which contains the Block Marker. If this chunk is | ||
1078 | * in the range of [first, last], we have to read out the whole page. | ||
1079 | * Why? since we had swapped the data at the position of Block Marker | ||
1080 | * to the metadata which is bound with the chunk 0. | ||
1081 | */ | ||
1082 | marker_pos = geo->block_mark_byte_offset / size; | ||
1083 | if (last >= marker_pos && first <= marker_pos) { | ||
1084 | dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n", | ||
1085 | page, first, last, marker_pos); | ||
1086 | return gpmi_ecc_read_page(mtd, chip, buf, 0, page); | ||
1087 | } | ||
1088 | |||
1089 | meta = geo->metadata_size; | ||
1090 | if (first) { | ||
1091 | col = meta + (size + ecc_parity_size) * first; | ||
1092 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1); | ||
1093 | |||
1094 | meta = 0; | ||
1095 | buf = buf + first * size; | ||
1096 | } | ||
1097 | |||
1098 | /* Save the old environment */ | ||
1099 | r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); | ||
1100 | r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); | ||
1101 | |||
1102 | /* change the BCH registers and bch_geometry{} */ | ||
1103 | n = last - first + 1; | ||
1104 | page_size = meta + (size + ecc_parity_size) * n; | ||
1105 | |||
1106 | r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | | ||
1107 | BM_BCH_FLASH0LAYOUT0_META_SIZE); | ||
1108 | r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) | ||
1109 | | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); | ||
1110 | writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); | ||
1111 | |||
1112 | r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; | ||
1113 | r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); | ||
1114 | writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); | ||
1115 | |||
1116 | geo->ecc_chunk_count = n; | ||
1117 | geo->payload_size = n * size; | ||
1118 | geo->page_size = page_size; | ||
1119 | geo->auxiliary_status_offset = ALIGN(meta, 4); | ||
1120 | |||
1121 | dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", | ||
1122 | page, offs, len, col, first, n, page_size); | ||
1123 | |||
1124 | /* Read the subpage now */ | ||
1125 | this->swap_block_mark = false; | ||
1126 | max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page); | ||
1127 | |||
1128 | /* Restore */ | ||
1129 | writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); | ||
1130 | writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); | ||
1131 | this->bch_geometry = old_geo; | ||
1132 | this->swap_block_mark = true; | ||
1133 | |||
1134 | return max_bitflips; | ||
1135 | } | ||
1136 | |||
1052 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 1137 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
1053 | const uint8_t *buf, int oob_required) | 1138 | const uint8_t *buf, int oob_required) |
1054 | { | 1139 | { |
@@ -1566,6 +1651,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this) | |||
1566 | ecc->layout = &gpmi_hw_ecclayout; | 1651 | ecc->layout = &gpmi_hw_ecclayout; |
1567 | 1652 | ||
1568 | /* | 1653 | /* |
1654 | * We only enable the subpage read when: | ||
1655 | * (1) the chip is imx6, and | ||
1656 | * (2) the size of the ECC parity is byte aligned. | ||
1657 | */ | ||
1658 | if (GPMI_IS_MX6Q(this) && | ||
1659 | ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { | ||
1660 | ecc->read_subpage = gpmi_ecc_read_subpage; | ||
1661 | chip->options |= NAND_SUBPAGE_READ; | ||
1662 | } | ||
1663 | |||
1664 | /* | ||
1569 | * Can we enable the extra features? such as EDO or Sync mode. | 1665 | * Can we enable the extra features? such as EDO or Sync mode. |
1570 | * | 1666 | * |
1571 | * We do not check the return value now. That's means if we fail in | 1667 | * We do not check the return value now. That's means if we fail in |