From ae6a63bc97cf30287ac6076534ac7efa089cbc82 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 16 Jun 2022 22:19:44 +0200 Subject: [PATCH] ipq806x: 5.15: replace nandc patch with upstream version Replace nandc fix patch with upstream version. Signed-off-by: Christian Marangi --- ...m_nandc-add-boot_layout_mode-support.patch | 240 ----------- ...vicetree-mtd-qcom_nandc-document-qco.patch | 41 -- ...m_nandc-reorder-qcom_nand_host-struc.patch | 268 ++++++++++++ ...m_nandc-add-support-for-unprotected-.patch | 406 ++++++++++++++++++ 4 files changed, 674 insertions(+), 281 deletions(-) delete mode 100644 target/linux/ipq806x/patches-5.15/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch delete mode 100644 target/linux/ipq806x/patches-5.15/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch create mode 100644 target/linux/ipq806x/patches-5.15/116-v6.0-01-mtd-nand-raw-qcom_nandc-reorder-qcom_nand_host-struc.patch create mode 100644 target/linux/ipq806x/patches-5.15/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch diff --git a/target/linux/ipq806x/patches-5.15/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch b/target/linux/ipq806x/patches-5.15/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch deleted file mode 100644 index 08c067255e5..00000000000 --- a/target/linux/ipq806x/patches-5.15/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch +++ /dev/null @@ -1,240 +0,0 @@ -From 6949d651e3be3ebbfedb6bbd5b541cfda6ee58a9 Mon Sep 17 00:00:00 2001 -From: Ansuel Smith -Date: Wed, 10 Feb 2021 10:40:17 +0100 -Subject: [PATCH 1/2] mtd: nand: raw: qcom_nandc: add boot_layout_mode support - -ipq806x nand have a special ecc configuration for the boot pages. The -use of the non-boot pages configuration on boot pages cause I/O error -and can cause broken data written to the nand. Add support for this -special configuration if the page to be read/write is in the size of the -boot pages set by the dts. - -Signed-off-by: Ansuel Smith ---- - drivers/mtd/nand/raw/qcom_nandc.c | 82 +++++++++++++++++++++++++++++-- - 1 file changed, 77 insertions(+), 5 deletions(-) - ---- a/drivers/mtd/nand/raw/qcom_nandc.c -+++ b/drivers/mtd/nand/raw/qcom_nandc.c -@@ -163,6 +163,11 @@ - /* NAND_CTRL bits */ - #define BAM_MODE_EN BIT(0) - -+ -+#define UD_SIZE_BYTES_MASK (0x3ff << UD_SIZE_BYTES) -+#define SPARE_SIZE_BYTES_MASK (0xf << SPARE_SIZE_BYTES) -+#define ECC_NUM_DATA_BYTES_MASK (0x3ff << ECC_NUM_DATA_BYTES) -+ - /* - * the NAND controller performs reads/writes with ECC in 516 byte chunks. - * the driver calls the chunks 'step' or 'codeword' interchangeably -@@ -443,6 +448,13 @@ struct qcom_nand_controller { - * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for - * ecc/non-ecc mode for the current nand flash - * device -+ * -+ * @boot_pages_conf: keep track of the current ecc configuration used by -+ * the driver for read/write operation. (boot pages -+ * have different configuration than normal page) -+ * @boot_pages: number of pages starting from 0 used as boot pages -+ * where the driver will use the boot pages ecc -+ * configuration for read/write operation - */ - struct qcom_nand_host { - struct nand_chip chip; -@@ -465,6 +477,9 @@ struct qcom_nand_host { - u32 ecc_bch_cfg; - u32 clrflashstatus; - u32 clrreadstatus; -+ -+ bool boot_pages_conf; -+ u32 boot_pages; - }; - - /* -@@ -474,6 +489,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 -+ * @has_boot_pages - whether NAND has different ecc settings for boot pages - * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset - */ - struct qcom_nandc_props { -@@ -481,6 +497,7 @@ struct qcom_nandc_props { - bool is_bam; - bool is_qpic; - bool qpic_v2; -+ bool has_boot_pages; - u32 dev_cmd_reg_start; - }; - -@@ -1691,7 +1708,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->boot_pages_conf) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) * 4); - oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + -@@ -1772,7 +1789,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->boot_pages_conf) { - data_size = ecc->size - ((ecc->steps - 1) * 4); - oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; - } else { -@@ -1930,7 +1947,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->boot_pages_conf) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; -@@ -2027,6 +2044,30 @@ static int copy_last_cw(struct qcom_nand - return ret; - } - -+static void -+check_boot_pages_conf(struct qcom_nand_host *host, int page) -+{ -+ bool boot_pages_conf = page < host->boot_pages; -+ -+ /* Skip conf write if we are already in the correct mode */ -+ if (boot_pages_conf != host->boot_pages_conf) { -+ host->boot_pages_conf = boot_pages_conf; -+ -+ host->cw_data = boot_pages_conf ? 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 = (boot_pages_conf ? 0x1ff : 0x203) << -+ NUM_STEPS; -+ } -+} -+ - /* implements ecc->read_page() */ - static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -@@ -2035,6 +2076,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->boot_pages) -+ check_boot_pages_conf(host, page); -+ - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = oob_required ? chip->oob_poi : NULL; -@@ -2054,6 +2098,9 @@ static int qcom_nandc_read_page_raw(stru - int cw, ret; - u8 *data_buf = buf, *oob_buf = chip->oob_poi; - -+ if (host->boot_pages) -+ check_boot_pages_conf(host, page); -+ - for (cw = 0; cw < ecc->steps; cw++) { - ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, - page, cw); -@@ -2074,6 +2121,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->boot_pages) -+ check_boot_pages_conf(host, page); -+ - clear_read_regs(nandc); - clear_bam_transaction(nandc); - -@@ -2094,6 +2144,9 @@ static int qcom_nandc_write_page(struct - u8 *data_buf, *oob_buf; - int i, ret; - -+ if (host->boot_pages) -+ check_boot_pages_conf(host, page); -+ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - clear_read_regs(nandc); -@@ -2109,7 +2162,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->boot_pages_conf) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; -@@ -2166,6 +2219,9 @@ static int qcom_nandc_write_page_raw(str - u8 *data_buf, *oob_buf; - int i, ret; - -+ if (host->boot_pages) -+ check_boot_pages_conf(host, page); -+ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - clear_read_regs(nandc); - clear_bam_transaction(nandc); -@@ -2184,7 +2240,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->boot_pages_conf) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) << 2); - oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + -@@ -2244,6 +2300,9 @@ static int qcom_nandc_write_oob(struct n - int data_size, oob_size; - int ret; - -+ if (host->boot_pages) -+ check_boot_pages_conf(host, page); -+ - host->use_ecc = true; - clear_bam_transaction(nandc); - -@@ -2912,6 +2971,7 @@ static int qcom_nand_host_init_and_regis - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct device *dev = nandc->dev; -+ u32 boot_pages_size; - int ret; - - ret = of_property_read_u32(dn, "reg", &host->cs); -@@ -2962,6 +3022,17 @@ static int qcom_nand_host_init_and_regis - if (ret) - nand_cleanup(chip); - -+ if (nandc->props->has_boot_pages && -+ of_property_read_bool(dn, "nand-is-boot-medium")) { -+ ret = of_property_read_u32(dn, "qcom,boot_pages_size", -+ &boot_pages_size); -+ if (ret) -+ dev_warn(dev, "can't get boot pages size"); -+ else -+ /* Convert size to nand pages */ -+ host->boot_pages = boot_pages_size / mtd->writesize; -+ } -+ - return ret; - } - -@@ -3127,6 +3198,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, -+ .has_boot_pages = true, - .dev_cmd_reg_start = 0x0, - }; - diff --git a/target/linux/ipq806x/patches-5.15/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch b/target/linux/ipq806x/patches-5.15/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch deleted file mode 100644 index 91d304479a0..00000000000 --- a/target/linux/ipq806x/patches-5.15/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 6fb003a7a117f97a35b078ba726c84adeae29c4c Mon Sep 17 00:00:00 2001 -From: Ansuel Smith -Date: Wed, 10 Feb 2021 10:54:19 +0100 -Subject: [PATCH 2/2] Documentation: devicetree: mtd: qcom_nandc: document - qcom,boot_layout_size binding - -Document new qcom,boot_layout_size binding used to apply special -read/write confituation to boots partitions. - -Signed-off-by: Ansuel Smith ---- - Documentation/devicetree/bindings/mtd/qcom,nandc.yaml | 11 +++++++++++ - 1 file changed, 11 insertions(+) - ---- a/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml -+++ b/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml -@@ -78,6 +78,14 @@ allOf: - Must contain the ADM data type CRCI block instance number - specified for the NAND controller on the given platform - -+ qcom,boot_pages_size: -+ description: -+ Should contain the size of the total boot partitions -+ where the boot layout read/write specific configuration -+ should be used. The boot layout is considered from the -+ start of the nand to the value set in this binding. -+ Only used in combination with 'nand-is-boot-medium'. -+ - - if: - properties: - compatible: -@@ -135,6 +143,9 @@ examples: - nand-ecc-strength = <4>; - nand-bus-width = <8>; - -+ nand-is-boot-medium; -+ qcom,boot_pages_size: <0x58a0000>; -+ - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; diff --git a/target/linux/ipq806x/patches-5.15/116-v6.0-01-mtd-nand-raw-qcom_nandc-reorder-qcom_nand_host-struc.patch b/target/linux/ipq806x/patches-5.15/116-v6.0-01-mtd-nand-raw-qcom_nandc-reorder-qcom_nand_host-struc.patch new file mode 100644 index 00000000000..c595b10dd74 --- /dev/null +++ b/target/linux/ipq806x/patches-5.15/116-v6.0-01-mtd-nand-raw-qcom_nandc-reorder-qcom_nand_host-struc.patch @@ -0,0 +1,268 @@ +From b360514edb4743cbf86fc377699c75e98b1264c7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 16 Jun 2022 02:18:33 +0200 +Subject: [PATCH 1/2] mtd: nand: raw: qcom_nandc: reorder qcom_nand_host struct + +Reorder structs in nandc driver to save holes. + +Signed-off-by: Christian Marangi +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-2-ansuelsmth@gmail.com +--- + drivers/mtd/nand/raw/qcom_nandc.c | 107 +++++++++++++++++------------- + 1 file changed, 62 insertions(+), 45 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -237,6 +237,9 @@ nandc_set_reg(chip, reg, \ + * @bam_ce - the array of BAM command elements + * @cmd_sgl - sgl for NAND BAM command pipe + * @data_sgl - sgl for NAND BAM consumer/producer pipe ++ * @last_data_desc - last DMA desc in data channel (tx/rx). ++ * @last_cmd_desc - last DMA desc in command channel. ++ * @txn_done - completion for NAND transfer. + * @bam_ce_pos - the index in bam_ce which is available for next sgl + * @bam_ce_start - the index in bam_ce which marks the start position ce + * for current sgl. It will be used for size calculation +@@ -249,14 +252,14 @@ nandc_set_reg(chip, reg, \ + * @rx_sgl_start - start index in data sgl for rx. + * @wait_second_completion - wait for second DMA desc completion before making + * the NAND transfer completion. +- * @txn_done - completion for NAND transfer. +- * @last_data_desc - last DMA desc in data channel (tx/rx). +- * @last_cmd_desc - last DMA desc in command channel. + */ + struct bam_transaction { + struct bam_cmd_element *bam_ce; + struct scatterlist *cmd_sgl; + struct scatterlist *data_sgl; ++ struct dma_async_tx_descriptor *last_data_desc; ++ struct dma_async_tx_descriptor *last_cmd_desc; ++ struct completion txn_done; + u32 bam_ce_pos; + u32 bam_ce_start; + u32 cmd_sgl_pos; +@@ -266,25 +269,23 @@ struct bam_transaction { + u32 rx_sgl_pos; + u32 rx_sgl_start; + bool wait_second_completion; +- struct completion txn_done; +- struct dma_async_tx_descriptor *last_data_desc; +- struct dma_async_tx_descriptor *last_cmd_desc; + }; + + /* + * This data type corresponds to the nand dma descriptor ++ * @dma_desc - low level DMA engine descriptor + * @list - list for desc_info +- * @dir - DMA transfer direction ++ * + * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by + * ADM + * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM + * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM +- * @dma_desc - low level DMA engine descriptor ++ * @dir - DMA transfer direction + */ + struct desc_info { ++ struct dma_async_tx_descriptor *dma_desc; + struct list_head node; + +- enum dma_data_direction dir; + union { + struct scatterlist adm_sgl; + struct { +@@ -292,7 +293,7 @@ struct desc_info { + int sgl_cnt; + }; + }; +- struct dma_async_tx_descriptor *dma_desc; ++ enum dma_data_direction dir; + }; + + /* +@@ -336,52 +337,64 @@ struct nandc_regs { + /* + * NAND controller data struct + * +- * @controller: base controller structure +- * @host_list: list containing all the chips attached to the +- * controller + * @dev: parent device ++ * + * @base: MMIO base +- * @base_phys: physical base address of controller registers +- * @base_dma: dma base address of controller registers ++ * + * @core_clk: controller clock + * @aon_clk: another controller clock + * ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes. contains the register values to be ++ * written to controller ++ * ++ * @props: properties of current NAND controller, ++ * initialized via DT match data ++ * ++ * @controller: base controller structure ++ * @host_list: list containing all the chips attached to the ++ * controller ++ * + * @chan: dma channel + * @cmd_crci: ADM DMA CRCI for command flow control + * @data_crci: ADM DMA CRCI for data flow control ++ * + * @desc_list: DMA descriptor list (list of desc_infos) + * + * @data_buffer: our local DMA buffer for page read/writes, + * used when we can't use the buffer provided + * by upper layers directly +- * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf +- * functions + * @reg_read_buf: local buffer for reading back registers via DMA ++ * ++ * @base_phys: physical base address of controller registers ++ * @base_dma: dma base address of controller registers + * @reg_read_dma: contains dma address for register read buffer +- * @reg_read_pos: marker for data read in reg_read_buf + * +- * @regs: a contiguous chunk of memory for DMA register +- * writes. contains the register values to be +- * written to controller +- * @cmd1/vld: some fixed controller register values +- * @props: properties of current NAND controller, +- * initialized via DT match data ++ * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf ++ * functions + * @max_cwperpage: maximum QPIC codewords required. calculated + * from all connected NAND devices pagesize ++ * ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * ++ * @cmd1/vld: some fixed controller register values + */ + struct qcom_nand_controller { +- struct nand_controller controller; +- struct list_head host_list; +- + struct device *dev; + + void __iomem *base; +- phys_addr_t base_phys; +- dma_addr_t base_dma; + + struct clk *core_clk; + struct clk *aon_clk; + ++ struct nandc_regs *regs; ++ struct bam_transaction *bam_txn; ++ ++ const struct qcom_nandc_props *props; ++ ++ struct nand_controller controller; ++ struct list_head host_list; ++ + union { + /* will be used only by QPIC for BAM DMA */ + struct { +@@ -399,22 +412,22 @@ struct qcom_nand_controller { + }; + + struct list_head desc_list; +- struct bam_transaction *bam_txn; + + u8 *data_buffer; ++ __le32 *reg_read_buf; ++ ++ phys_addr_t base_phys; ++ dma_addr_t base_dma; ++ dma_addr_t reg_read_dma; ++ + int buf_size; + int buf_count; + int buf_start; + unsigned int max_cwperpage; + +- __le32 *reg_read_buf; +- dma_addr_t reg_read_dma; + int reg_read_pos; + +- struct nandc_regs *regs; +- + u32 cmd1, vld; +- const struct qcom_nandc_props *props; + }; + + /* +@@ -430,19 +443,21 @@ struct qcom_nand_controller { + * and reserved bytes + * @cw_data: the number of bytes within a codeword protected + * by ECC +- * @use_ecc: request the controller to use ECC for the +- * upcoming read/write +- * @bch_enabled: flag to tell whether BCH ECC mode is used + * @ecc_bytes_hw: ECC bytes used by controller hardware for this + * chip +- * @status: value to be returned if NAND_CMD_STATUS command +- * is executed ++ * + * @last_command: keeps track of last command on this chip. used + * for reading correct status + * + * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for + * ecc/non-ecc mode for the current nand flash + * device ++ * ++ * @status: value to be returned if NAND_CMD_STATUS command ++ * is executed ++ * @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 nand_chip chip; +@@ -451,12 +466,10 @@ struct qcom_nand_host { + int cs; + int cw_size; + int cw_data; +- bool use_ecc; +- bool bch_enabled; + int ecc_bytes_hw; + int spare_bytes; + int bbm_size; +- u8 status; ++ + int last_command; + + u32 cfg0, cfg1; +@@ -465,23 +478,27 @@ struct qcom_nand_host { + u32 ecc_bch_cfg; + u32 clrflashstatus; + u32 clrreadstatus; ++ ++ u8 status; ++ bool use_ecc; ++ bool bch_enabled; + }; + + /* + * This data type corresponds to the NAND controller properties which varies + * among different NAND controllers. + * @ecc_modes - ecc mode for NAND ++ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset + * @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 +- * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset + */ + struct qcom_nandc_props { + u32 ecc_modes; ++ u32 dev_cmd_reg_start; + bool is_bam; + bool is_qpic; + bool qpic_v2; +- u32 dev_cmd_reg_start; + }; + + /* Frees the BAM transaction memory */ diff --git a/target/linux/ipq806x/patches-5.15/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch b/target/linux/ipq806x/patches-5.15/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch new file mode 100644 index 00000000000..2a661953044 --- /dev/null +++ b/target/linux/ipq806x/patches-5.15/116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch @@ -0,0 +1,406 @@ +From 862bdedd7f4b8aebf00fdb422062e64896e97809 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +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, + }; + -- 2.30.2