ipq806x: 5.15: replace nandc patch with upstream version
Replace nandc fix patch with upstream version. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
		| @@ -1,240 +0,0 @@ | |||||||
| From 6949d651e3be3ebbfedb6bbd5b541cfda6ee58a9 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Ansuel Smith <ansuelsmth@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@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, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| From 6fb003a7a117f97a35b078ba726c84adeae29c4c Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Ansuel Smith <ansuelsmth@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@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>; |  | ||||||
| @@ -0,0 +1,268 @@ | |||||||
|  | From b360514edb4743cbf86fc377699c75e98b1264c7 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@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@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-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 */ | ||||||
| @@ -0,0 +1,406 @@ | |||||||
|  | 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, | ||||||
|  |  }; | ||||||
|  |   | ||||||
		Reference in New Issue
	
	Block a user
	 Christian Marangi
					Christian Marangi