diff options
Diffstat (limited to 'target/linux/ipq806x/patches-6.1/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch')
-rw-r--r-- | target/linux/ipq806x/patches-6.1/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch | 406 |
1 files changed, 0 insertions, 406 deletions
diff --git a/target/linux/ipq806x/patches-6.1/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch b/target/linux/ipq806x/patches-6.1/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch deleted file mode 100644 index 2a66195304..0000000000 --- a/target/linux/ipq806x/patches-6.1/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch +++ /dev/null @@ -1,406 +0,0 @@ -From 862bdedd7f4b8aebf00fdb422062e64896e97809 Mon Sep 17 00:00:00 2001 -From: Christian Marangi <ansuelsmth@gmail.com> -Date: Thu, 16 Jun 2022 02:18:34 +0200 -Subject: [PATCH 2/2] mtd: nand: raw: qcom_nandc: add support for unprotected - spare data pages - -IPQ8064 nand have special pages where a different layout scheme is used. -These special page are used by boot partition and on reading them -lots of warning are reported about wrong ECC data and if written to -results in broken data and not bootable device. - -The layout scheme used by these special page consist in using 512 bytes -as the codeword size (even for the last codeword) while writing to CFG0 -register. This forces the NAND controller to unprotect the 4 bytes of -spare data. - -Since the kernel is unaware of this different layout for these special -page, it does try to protect the spare data too during read/write and -warn about CRC errors. - -Add support for this by permitting the user to declare these special -pages in dts by declaring offset and size of the partition. The driver -internally will convert these value to nand pages. - -On user read/write the page is checked and if it's a boot page the -correct layout is used. - -Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> -Reviewed-by: Manivannan Sadhasivam <mani@kernel.org> -Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> -Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-3-ansuelsmth@gmail.com ---- - drivers/mtd/nand/raw/qcom_nandc.c | 199 +++++++++++++++++++++++++++++- - 1 file changed, 194 insertions(+), 5 deletions(-) - ---- a/drivers/mtd/nand/raw/qcom_nandc.c -+++ b/drivers/mtd/nand/raw/qcom_nandc.c -@@ -79,8 +79,10 @@ - #define DISABLE_STATUS_AFTER_WRITE 4 - #define CW_PER_PAGE 6 - #define UD_SIZE_BYTES 9 -+#define UD_SIZE_BYTES_MASK GENMASK(18, 9) - #define ECC_PARITY_SIZE_BYTES_RS 19 - #define SPARE_SIZE_BYTES 23 -+#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) - #define NUM_ADDR_CYCLES 27 - #define STATUS_BFR_READ 30 - #define SET_RD_MODE_AFTER_STATUS 31 -@@ -101,6 +103,7 @@ - #define ECC_MODE 4 - #define ECC_PARITY_SIZE_BYTES_BCH 8 - #define ECC_NUM_DATA_BYTES 16 -+#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) - #define ECC_FORCE_CLK_OPEN 30 - - /* NAND_DEV_CMD1 bits */ -@@ -431,12 +434,31 @@ struct qcom_nand_controller { - }; - - /* -+ * NAND special boot partitions -+ * -+ * @page_offset: offset of the partition where spare data is not protected -+ * by ECC (value in pages) -+ * @page_offset: size of the partition where spare data is not protected -+ * by ECC (value in pages) -+ */ -+struct qcom_nand_boot_partition { -+ u32 page_offset; -+ u32 page_size; -+}; -+ -+/* - * NAND chip structure - * -+ * @boot_partitions: array of boot partitions where offset and size of the -+ * boot partitions are stored -+ * - * @chip: base NAND chip structure - * @node: list node to add itself to host_list in - * qcom_nand_controller - * -+ * @nr_boot_partitions: count of the boot partitions where spare data is not -+ * protected by ECC -+ * - * @cs: chip select value for this chip - * @cw_size: the number of bytes in a single step/codeword - * of a page, consisting of all data, ecc, spare -@@ -455,14 +477,20 @@ struct qcom_nand_controller { - * - * @status: value to be returned if NAND_CMD_STATUS command - * is executed -+ * @codeword_fixup: keep track of the current layout used by -+ * the driver for read/write operation. - * @use_ecc: request the controller to use ECC for the - * upcoming read/write - * @bch_enabled: flag to tell whether BCH ECC mode is used - */ - struct qcom_nand_host { -+ struct qcom_nand_boot_partition *boot_partitions; -+ - struct nand_chip chip; - struct list_head node; - -+ int nr_boot_partitions; -+ - int cs; - int cw_size; - int cw_data; -@@ -480,6 +508,7 @@ struct qcom_nand_host { - u32 clrreadstatus; - - u8 status; -+ bool codeword_fixup; - bool use_ecc; - bool bch_enabled; - }; -@@ -492,6 +521,7 @@ struct qcom_nand_host { - * @is_bam - whether NAND controller is using BAM - * @is_qpic - whether NAND CTRL is part of qpic IP - * @qpic_v2 - flag to indicate QPIC IP version 2 -+ * @use_codeword_fixup - whether NAND has different layout for boot partitions - */ - struct qcom_nandc_props { - u32 ecc_modes; -@@ -499,6 +529,7 @@ struct qcom_nandc_props { - bool is_bam; - bool is_qpic; - bool qpic_v2; -+ bool use_codeword_fixup; - }; - - /* Frees the BAM transaction memory */ -@@ -1708,7 +1739,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_size; - -- if (qcom_nandc_is_last_cw(ecc, cw)) { -+ if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) * 4); - oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + -@@ -1789,7 +1820,7 @@ check_for_erased_page(struct qcom_nand_h - } - - for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { -- if (qcom_nandc_is_last_cw(ecc, cw)) { -+ if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) { - data_size = ecc->size - ((ecc->steps - 1) * 4); - oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; - } else { -@@ -1947,7 +1978,7 @@ static int read_page_ecc(struct qcom_nan - for (i = 0; i < ecc->steps; i++) { - int data_size, oob_size; - -- if (qcom_nandc_is_last_cw(ecc, i)) { -+ if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; -@@ -2044,6 +2075,69 @@ static int copy_last_cw(struct qcom_nand - return ret; - } - -+static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page) -+{ -+ struct qcom_nand_boot_partition *boot_partition; -+ u32 start, end; -+ int i; -+ -+ /* -+ * Since the frequent access will be to the non-boot partitions like rootfs, -+ * optimize the page check by: -+ * -+ * 1. Checking if the page lies after the last boot partition. -+ * 2. Checking from the boot partition end. -+ */ -+ -+ /* First check the last boot partition */ -+ boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1]; -+ start = boot_partition->page_offset; -+ end = start + boot_partition->page_size; -+ -+ /* Page is after the last boot partition end. This is NOT a boot partition */ -+ if (page > end) -+ return false; -+ -+ /* Actually check if it's a boot partition */ -+ if (page < end && page >= start) -+ return true; -+ -+ /* Check the other boot partitions starting from the second-last partition */ -+ for (i = host->nr_boot_partitions - 2; i >= 0; i--) { -+ boot_partition = &host->boot_partitions[i]; -+ start = boot_partition->page_offset; -+ end = start + boot_partition->page_size; -+ -+ if (page < end && page >= start) -+ return true; -+ } -+ -+ return false; -+} -+ -+static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page) -+{ -+ bool codeword_fixup = qcom_nandc_is_boot_partition(host, page); -+ -+ /* Skip conf write if we are already in the correct mode */ -+ if (codeword_fixup == host->codeword_fixup) -+ return; -+ -+ host->codeword_fixup = codeword_fixup; -+ -+ host->cw_data = codeword_fixup ? 512 : 516; -+ host->spare_bytes = host->cw_size - host->ecc_bytes_hw - -+ host->bbm_size - host->cw_data; -+ -+ host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK); -+ host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES | -+ host->cw_data << UD_SIZE_BYTES; -+ -+ host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK; -+ host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES; -+ host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS; -+} -+ - /* implements ecc->read_page() */ - static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -@@ -2052,6 +2146,9 @@ static int qcom_nandc_read_page(struct n - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf = NULL; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = oob_required ? chip->oob_poi : NULL; -@@ -2071,6 +2168,9 @@ static int qcom_nandc_read_page_raw(stru - int cw, ret; - u8 *data_buf = buf, *oob_buf = chip->oob_poi; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - for (cw = 0; cw < ecc->steps; cw++) { - ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, - page, cw); -@@ -2091,6 +2191,9 @@ static int qcom_nandc_read_oob(struct na - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - clear_read_regs(nandc); - clear_bam_transaction(nandc); - -@@ -2111,6 +2214,9 @@ static int qcom_nandc_write_page(struct - u8 *data_buf, *oob_buf; - int i, ret; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - clear_read_regs(nandc); -@@ -2126,7 +2232,7 @@ static int qcom_nandc_write_page(struct - for (i = 0; i < ecc->steps; i++) { - int data_size, oob_size; - -- if (qcom_nandc_is_last_cw(ecc, i)) { -+ if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; -@@ -2183,6 +2289,9 @@ static int qcom_nandc_write_page_raw(str - u8 *data_buf, *oob_buf; - int i, ret; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - clear_read_regs(nandc); - clear_bam_transaction(nandc); -@@ -2201,7 +2310,7 @@ static int qcom_nandc_write_page_raw(str - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_size; - -- if (qcom_nandc_is_last_cw(ecc, i)) { -+ if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) << 2); - oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + -@@ -2261,6 +2370,9 @@ static int qcom_nandc_write_oob(struct n - int data_size, oob_size; - int ret; - -+ if (host->nr_boot_partitions) -+ qcom_nandc_codeword_fixup(host, page); -+ - host->use_ecc = true; - clear_bam_transaction(nandc); - -@@ -2922,6 +3034,74 @@ static int qcom_nandc_setup(struct qcom_ - - static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL }; - -+static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc, -+ struct qcom_nand_host *host, -+ struct device_node *dn) -+{ -+ struct nand_chip *chip = &host->chip; -+ struct mtd_info *mtd = nand_to_mtd(chip); -+ struct qcom_nand_boot_partition *boot_partition; -+ struct device *dev = nandc->dev; -+ int partitions_count, i, j, ret; -+ -+ if (!of_find_property(dn, "qcom,boot-partitions", NULL)) -+ return 0; -+ -+ partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions"); -+ if (partitions_count <= 0) { -+ dev_err(dev, "Error parsing boot partition\n"); -+ return partitions_count ? partitions_count : -EINVAL; -+ } -+ -+ host->nr_boot_partitions = partitions_count / 2; -+ host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions, -+ sizeof(*host->boot_partitions), GFP_KERNEL); -+ if (!host->boot_partitions) { -+ host->nr_boot_partitions = 0; -+ return -ENOMEM; -+ } -+ -+ for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) { -+ boot_partition = &host->boot_partitions[i]; -+ -+ ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j, -+ &boot_partition->page_offset); -+ if (ret) { -+ dev_err(dev, "Error parsing boot partition offset at index %d\n", i); -+ host->nr_boot_partitions = 0; -+ return ret; -+ } -+ -+ if (boot_partition->page_offset % mtd->writesize) { -+ dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n", -+ i); -+ host->nr_boot_partitions = 0; -+ return -EINVAL; -+ } -+ /* Convert offset to nand pages */ -+ boot_partition->page_offset /= mtd->writesize; -+ -+ ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1, -+ &boot_partition->page_size); -+ if (ret) { -+ dev_err(dev, "Error parsing boot partition size at index %d\n", i); -+ host->nr_boot_partitions = 0; -+ return ret; -+ } -+ -+ if (boot_partition->page_size % mtd->writesize) { -+ dev_err(dev, "Boot partition size not multiple of writesize at index %i\n", -+ i); -+ host->nr_boot_partitions = 0; -+ return -EINVAL; -+ } -+ /* Convert size to nand pages */ -+ boot_partition->page_size /= mtd->writesize; -+ } -+ -+ return 0; -+} -+ - static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, - struct qcom_nand_host *host, - struct device_node *dn) -@@ -2979,6 +3159,14 @@ static int qcom_nand_host_init_and_regis - if (ret) - nand_cleanup(chip); - -+ if (nandc->props->use_codeword_fixup) { -+ ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn); -+ if (ret) { -+ nand_cleanup(chip); -+ return ret; -+ } -+ } -+ - return ret; - } - -@@ -3144,6 +3332,7 @@ static int qcom_nandc_remove(struct plat - static const struct qcom_nandc_props ipq806x_nandc_props = { - .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), - .is_bam = false, -+ .use_codeword_fixup = true, - .dev_cmd_reg_start = 0x0, - }; - |