[openwrt/openwrt] ipq806x: 5.15: replace nandc patch with upstream version

LEDE Commits lede-commits at lists.infradead.org
Tue Oct 11 12:29:09 PDT 2022


ansuel pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/ae6a63bc97cf30287ac6076534ac7efa089cbc82

commit ae6a63bc97cf30287ac6076534ac7efa089cbc82
Author: Christian Marangi <ansuelsmth at gmail.com>
AuthorDate: Thu Jun 16 22:19:44 2022 +0200

    ipq806x: 5.15: replace nandc patch with upstream version
    
    Replace nandc fix patch with upstream version.
    
    Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 ...w-qcom_nandc-add-boot_layout_mode-support.patch | 240 ------------
 ...on-devicetree-mtd-qcom_nandc-document-qco.patch |  41 ---
 ...w-qcom_nandc-reorder-qcom_nand_host-struc.patch | 268 ++++++++++++++
 ...w-qcom_nandc-add-support-for-unprotected-.patch | 406 +++++++++++++++++++++
 4 files changed, 674 insertions(+), 281 deletions(-)

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 08c067255e..0000000000
--- 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 <ansuelsmth at gmail.com>
-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 <ansuelsmth at gmail.com>
----
- 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 91d304479a..0000000000
--- 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 <ansuelsmth at gmail.com>
-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 <ansuelsmth at gmail.com>
----
- 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 0000000000..c595b10dd7
--- /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 <ansuelsmth at gmail.com>
+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 <ansuelsmth at gmail.com>
+Reviewed-by: Manivannan Sadhasivam <mani at kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
+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 0000000000..2a66195304
--- /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 <ansuelsmth at 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 at gmail.com>
+Reviewed-by: Manivannan Sadhasivam <mani at kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal at 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,
+ };
+ 




More information about the lede-commits mailing list