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:
parent
a8d7aed033
commit
ae6a63bc97
4 changed files with 674 additions and 281 deletions
|
@ -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,
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue