diff mbox series

[RFC,1/5] mtd: nand: ecc-qcom: Add support for ECC Engine Driver

Message ID 20231031120307.1600689-2-quic_mdalam@quicinc.com
State New
Headers show
Series Add QPIC SPI NAND driver support | expand

Commit Message

Md Sadre Alam Oct. 31, 2023, 12:03 p.m. UTC
Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
Signed-off-by: Sricharan R <quic_srichara@quicinc.com>
---
 drivers/mtd/nand/Kconfig    |   7 ++
 drivers/mtd/nand/Makefile   |   1 +
 drivers/mtd/nand/ecc-qcom.c | 198 ++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+)
 create mode 100644 drivers/mtd/nand/ecc-qcom.c
diff mbox series

Patch

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5b0c2c95f10c..333cec8187c8 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -61,6 +61,13 @@  config MTD_NAND_ECC_MEDIATEK
 	help
 	  This enables support for the hardware ECC engine from Mediatek.
 
+config MTD_NAND_ECC_QCOM
+	tristate "Qualcomm hardware ECC engine"
+	depends on ARCH_QCOM
+	select MTD_NAND_ECC
+	help
+	  This enables support for the hardware ECC engine from Qualcomm.
+
 endmenu
 
 endmenu
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 19e1291ac4d5..c73b8a3456ec 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -3,6 +3,7 @@ 
 nandcore-objs := core.o bbt.o
 obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
 obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
+obj-$(CONFIG_MTD_NAND_ECC_QCOM) += ecc-qcom.o qpic_common.o
 
 obj-y	+= onenand/
 obj-y	+= raw/
diff --git a/drivers/mtd/nand/ecc-qcom.c b/drivers/mtd/nand/ecc-qcom.c
new file mode 100644
index 000000000000..a85423ed368a
--- /dev/null
+++ b/drivers/mtd/nand/ecc-qcom.c
@@ -0,0 +1,198 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * QCOM ECC Engine  Driver.
+ * Copyright (C) 2023  Qualcomm Inc.
+ * Authors:	Md sadre Alam		<quic_mdalam@quicinc.com>
+ *		Sricharan R		<quic_srichara@quicinc.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mutex.h>
+#include <linux/mtd/nand-qpic-common.h>
+
+
+
+/* ECC modes supported by the controller */
+#define ECC_NONE        BIT(0)
+#define ECC_RS_4BIT     BIT(1)
+#define ECC_BCH_4BIT    BIT(2)
+#define ECC_BCH_8BIT    BIT(3)
+
+struct qpic_ecc_caps {
+	u32 err_mask;
+	u32 err_shift;
+	const u8 *ecc_strength;
+	const u32 *ecc_regs;
+	u8 num_ecc_strength;
+	u8 ecc_mode_shift;
+	u32 parity_bits;
+	int pg_irq_sel;
+};
+
+
+struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
+{
+	return container_of(chip, struct qcom_nand_host, chip);
+}
+EXPORT_SYMBOL(to_qcom_nand_host);
+
+struct qcom_nand_controller *
+get_qcom_nand_controller(struct nand_chip *chip)
+{
+	return container_of(chip->controller, struct qcom_nand_controller,
+			    controller);
+}
+EXPORT_SYMBOL(get_qcom_nand_controller);
+
+static struct qpic_ecc *qpic_ecc_get(struct device_node *np)
+{
+	struct platform_device *pdev;
+	struct qpic_ecc *ecc;
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	ecc = platform_get_drvdata(pdev);
+	if (!ecc) {
+		put_device(&pdev->dev);
+		return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	return ecc;
+}
+
+struct qpic_ecc *of_qpic_ecc_get(struct device_node *of_node)
+{
+	struct qpic_ecc *ecc = NULL;
+	struct device_node *np;
+
+	np = of_parse_phandle(of_node, "nand-ecc-engine", 0);
+	/* for backward compatibility */
+	if (!np)
+		np = of_parse_phandle(of_node, "ecc-engine", 0);
+	if (np) {
+		ecc = qpic_ecc_get(np);
+		of_node_put(np);
+	}
+
+	return ecc;
+}
+EXPORT_SYMBOL(of_qpic_ecc_get);
+
+int qcom_ecc_config(struct qpic_ecc  *ecc, int ecc_strength,
+			bool wide_bus)
+{
+	ecc->ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT);
+
+	if (ecc_strength >= 8) {
+		/* 8 bit ECC defaults to BCH ECC on all platforms */
+		ecc->bch_enabled = true;
+		ecc->ecc_mode = 1;
+
+		if (wide_bus) {
+			ecc->ecc_bytes_hw = 14;
+			ecc->spare_bytes = 0;
+			ecc->bbm_size = 2;
+		} else {
+			ecc->ecc_bytes_hw = 13;
+			ecc->spare_bytes = 2;
+			ecc->bbm_size = 1;
+		}
+	} else {
+		/*
+		 * if the controller supports BCH for 4 bit ECC, the controller
+		 * uses lesser bytes for ECC. If RS is used, the ECC bytes is
+		 * always 10 bytes
+		 */
+		if (ecc->ecc_modes & ECC_BCH_4BIT) {
+			/* BCH */
+			ecc->bch_enabled = true;
+			ecc->ecc_mode = 0;
+			if (wide_bus) {
+				ecc->ecc_bytes_hw = 8;
+				ecc->spare_bytes = 2;
+				ecc->bbm_size = 2;
+			} else {
+				ecc->ecc_bytes_hw = 7;
+				ecc->spare_bytes = 4;
+				ecc->bbm_size = 1;
+			}
+		} else {
+			/* RS */
+			ecc->ecc_bytes_hw = 10;
+			if (wide_bus) {
+				ecc->spare_bytes = 0;
+				ecc->bbm_size = 2;
+			} else {
+				ecc->spare_bytes = 1;
+				ecc->bbm_size = 1;
+			}
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qcom_ecc_config);
+
+void qcom_ecc_enable(struct qcom_ecc *ecc)
+{
+	ecc->use_ecc = true;
+}
+EXPORT_SYMBOL(qcom_ecc_enable);
+
+void qcom_ecc_disable(struct qcom_ecc *ecc)
+{
+	ecc->use_ecc = false;
+}
+EXPORT_SYMBOL(qcom_ecc_disable);
+
+static const struct of_device_id qpic_ecc_dt_match[] = {
+	{
+		.compatible = "qcom,ipq9574-ecc",
+	},
+	{},
+};
+
+static int qpic_ecc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qpic_ecc *ecc;
+	u32 max_eccdata_size;
+
+	ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
+	if (!ecc)
+		return -ENOMEM;
+
+	ecc->caps = of_device_get_match_data(dev);
+
+	ecc->dev = dev;
+	platform_set_drvdata(pdev, ecc);
+	dev_info(dev, "probed\n");
+
+	return 0;
+}
+
+
+MODULE_DEVICE_TABLE(of, qpic_ecc_dt_match);
+
+static struct platform_driver qpic_ecc_driver = {
+	.probe  = qpic_ecc_probe,
+	.driver = {
+		.name  = "qpic-ecc",
+		.of_match_table = qpic_ecc_dt_match,
+	},
+};
+
+module_platform_driver(qpic_ecc_driver);
+
+MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>");
+MODULE_DESCRIPTION("QPIC Nand ECC Driver");
+MODULE_LICENSE("Dual MIT/GPL");