--- /dev/null
+From 074036f9de6b8c5fc642e8e2540950f6a35aa804 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Thu, 20 Apr 2017 10:31:10 +0530
+Subject: [PATCH] qcom: mtd: nand: Add bam_dma support in qcom_nand driver
+
+The current driver only support ADM DMA so this patch adds the
+BAM DMA support in current NAND driver with compatible string
+qcom,ebi2-nandc-bam.
+Added bam channels and data buffers, NAND BAM uses 3 channels:
+command, data tx and data rx, while ADM uses only single channel.
+So this patch adds the BAM channel in device tree and using the
+same in NAND driver allocation function.
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ .../devicetree/bindings/mtd/qcom_nandc.txt | 69 +++++++--
+ drivers/mtd/nand/qcom_nandc.c | 160 +++++++++++++++++----
+ 2 files changed, 190 insertions(+), 39 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+index 70dd511..9e5c9be 100644
+--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+@@ -1,21 +1,26 @@
+ * Qualcomm NAND controller
+
+ Required properties:
+-- compatible: should be "qcom,ipq806x-nand"
++- compatible: "qcom,ipq806x-nand" for IPQ8064 which uses
++ ADM DMA.
++ "qcom,ebi2-nand-bam" - nand drivers using BAM DMA
++ like IPQ4019.
+ - reg: MMIO address range
+ - clocks: must contain core clock and always on clock
+ - clock-names: must contain "core" for the core clock and "aon" for the
+ always on clock
+ - dmas: DMA specifier, consisting of a phandle to the ADM DMA
+- controller node and the channel number to be used for
+- NAND. Refer to dma.txt and qcom_adm.txt for more details
+-- dma-names: must be "rxtx"
+-- qcom,cmd-crci: must contain the ADM command type CRCI block instance
+- number specified for the NAND controller on the given
+- platform
+-- qcom,data-crci: must contain the ADM data type CRCI block instance
+- number specified for the NAND controller on the given
+- platform
++ or BAM DMA controller node and the channel number to
++ be used for NAND. Refer to dma.txt, qcom_adm.txt(ADM)
++ and qcom_bam_dma.txt(BAM) for more details
++- dma-names: "rxtx" - ADM
++ "tx", "rx", "cmd" - BAM
++- qcom,cmd-crci: Only required for ADM DMA. must contain the ADM command
++ type CRCI block instance number specified for the NAND
++ controller on the given platform.
++- qcom,data-crci: Only required for ADM DMA. must contain the ADM data
++ type CRCI block instance number specified for the NAND
++ controller on the given platform.
+ - #address-cells: <1> - subnodes give the chip-select number
+ - #size-cells: <0>
+
+@@ -44,7 +49,7 @@ partition.txt for more detail.
+ Example:
+
+ nand@1ac00000 {
+- compatible = "qcom,ebi2-nandc";
++ compatible = "qcom,ipq806x-nand","qcom.qcom_nand";
+ reg = <0x1ac00000 0x800>;
+
+ clocks = <&gcc EBI2_CLK>,
+@@ -84,3 +89,45 @@ nand@1ac00000 {
+ };
+ };
+ };
++
++nand@79B0000 {
++ compatible = "qcom,ebi2-nandc-bam";
++ reg = <0x79B0000 0x1000>;
++
++ clocks = <&gcc EBI2_CLK>,
++ <&gcc EBI2_AON_CLK>;
++ clock-names = "core", "aon";
++
++ dmas = <&qpicbam 0>,
++ <&qpicbam 1>,
++ <&qpicbam 2>;
++ dma-names = "tx", "rx", "cmd";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ nandcs@0 {
++ compatible = "qcom,nandcs";
++ reg = <0>;
++
++ nand-ecc-strength = <4>;
++ nand-ecc-step-size = <512>;
++ nand-bus-width = <8>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "boot-nand";
++ reg = <0 0x58a0000>;
++ };
++
++ partition@58a0000 {
++ label = "fs-nand";
++ reg = <0x58a0000 0x4000000>;
++ };
++ };
++ };
++};
+diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
+index 57d483a..76a0ffc 100644
+--- a/drivers/mtd/nand/qcom_nandc.c
++++ b/drivers/mtd/nand/qcom_nandc.c
+@@ -226,6 +226,7 @@ struct nandc_regs {
+ * by upper layers directly
+ * @buf_size/count/start: markers for chip->read_buf/write_buf functions
+ * @reg_read_buf: local buffer for reading back registers via DMA
++ * @reg_read_buf_phys: 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
+@@ -234,7 +235,10 @@ struct nandc_regs {
+ * @cmd1/vld: some fixed controller register values
+ * @ecc_modes: supported ECC modes by the current controller,
+ * initialized via DT match data
+- */
++ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used
++ * @dma_bam_enabled: flag to tell whether nand controller is using
++ * bam dma
++*/
+ struct qcom_nand_controller {
+ struct nand_hw_control controller;
+ struct list_head host_list;
+@@ -247,17 +251,28 @@ struct qcom_nand_controller {
+ struct clk *core_clk;
+ struct clk *aon_clk;
+
+- struct dma_chan *chan;
+- unsigned int cmd_crci;
+- unsigned int data_crci;
+ struct list_head desc_list;
++ union {
++ struct {
++ struct dma_chan *tx_chan;
++ struct dma_chan *rx_chan;
++ struct dma_chan *cmd_chan;
++ };
++ struct {
++ struct dma_chan *chan;
++ unsigned int cmd_crci;
++ unsigned int data_crci;
++ };
++ };
+
+ u8 *data_buffer;
++ bool dma_bam_enabled;
+ int buf_size;
+ int buf_count;
+ int buf_start;
+
+ __le32 *reg_read_buf;
++ dma_addr_t reg_read_buf_phys;
+ int reg_read_pos;
+
+ struct nandc_regs *regs;
+@@ -316,6 +331,17 @@ struct qcom_nand_host {
+ u32 clrreadstatus;
+ };
+
++/*
++ * This data type corresponds to the nand driver data which will be used at
++ * driver probe time
++ * @ecc_modes - ecc mode for nand
++ * @dma_bam_enabled - whether this driver is using bam
++ */
++struct qcom_nand_driver_data {
++ u32 ecc_modes;
++ bool dma_bam_enabled;
++};
++
+ static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
+ {
+ return container_of(chip, struct qcom_nand_host, chip);
+@@ -1893,7 +1919,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
+ | wide_bus << WIDE_FLASH
+ | 1 << DEV0_CFG1_ECC_DISABLE;
+
+- host->ecc_bch_cfg = host->bch_enabled << ECC_CFG_ECC_DISABLE
++ host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
+ | 0 << ECC_SW_RESET
+ | host->cw_data << ECC_NUM_DATA_BYTES
+ | 1 << ECC_FORCE_CLK_OPEN
+@@ -1942,16 +1968,46 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
+ if (!nandc->regs)
+ return -ENOMEM;
+
+- nandc->reg_read_buf = devm_kzalloc(nandc->dev,
+- MAX_REG_RD * sizeof(*nandc->reg_read_buf),
+- GFP_KERNEL);
+- if (!nandc->reg_read_buf)
+- return -ENOMEM;
++ if (!nandc->dma_bam_enabled) {
++ nandc->reg_read_buf = devm_kzalloc(nandc->dev,
++ MAX_REG_RD *
++ sizeof(*nandc->reg_read_buf),
++ GFP_KERNEL);
+
+- nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
+- if (!nandc->chan) {
+- dev_err(nandc->dev, "failed to request slave channel\n");
+- return -ENODEV;
++ if (!nandc->reg_read_buf)
++ return -ENOMEM;
++
++ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
++ if (!nandc->chan) {
++ dev_err(nandc->dev, "failed to request slave channel\n");
++ return -ENODEV;
++ }
++ } else {
++ nandc->reg_read_buf = dmam_alloc_coherent(nandc->dev,
++ MAX_REG_RD *
++ sizeof(*nandc->reg_read_buf),
++ &nandc->reg_read_buf_phys, GFP_KERNEL);
++
++ if (!nandc->reg_read_buf)
++ return -ENOMEM;
++
++ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
++ if (!nandc->tx_chan) {
++ dev_err(nandc->dev, "failed to request tx channel\n");
++ return -ENODEV;
++ }
++
++ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx");
++ if (!nandc->rx_chan) {
++ dev_err(nandc->dev, "failed to request rx channel\n");
++ return -ENODEV;
++ }
++
++ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd");
++ if (!nandc->cmd_chan) {
++ dev_err(nandc->dev, "failed to request cmd channel\n");
++ return -ENODEV;
++ }
+ }
+
+ INIT_LIST_HEAD(&nandc->desc_list);
+@@ -1964,8 +2020,35 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
+
+ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
+ {
+- dma_release_channel(nandc->chan);
+-}
++ if (nandc->dma_bam_enabled) {
++ if (nandc->tx_chan)
++ dma_release_channel(nandc->tx_chan);
++
++ if (nandc->rx_chan)
++ dma_release_channel(nandc->rx_chan);
++
++ if (nandc->cmd_chan)
++ dma_release_channel(nandc->tx_chan);
++
++ if (nandc->reg_read_buf)
++ dmam_free_coherent(nandc->dev, MAX_REG_RD *
++ sizeof(*nandc->reg_read_buf),
++ nandc->reg_read_buf,
++ nandc->reg_read_buf_phys);
++ } else {
++ if (nandc->chan)
++ dma_release_channel(nandc->chan);
++
++ if (nandc->reg_read_buf)
++ devm_kfree(nandc->dev, nandc->reg_read_buf);
++ }
++
++ if (nandc->regs)
++ devm_kfree(nandc->dev, nandc->regs);
++
++ if (nandc->data_buffer)
++ devm_kfree(nandc->dev, nandc->data_buffer);
++ }
+
+ /* one time setup of a few nand controller registers */
+ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
+@@ -2002,6 +2085,8 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = dev;
++ mtd->priv = chip;
++ chip->priv = nandc;
+
+ chip->cmdfunc = qcom_nandc_command;
+ chip->select_chip = qcom_nandc_select_chip;
+@@ -2049,16 +2134,20 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
+ struct device_node *np = nandc->dev->of_node;
+ int ret;
+
+- ret = of_property_read_u32(np, "qcom,cmd-crci", &nandc->cmd_crci);
+- if (ret) {
+- dev_err(nandc->dev, "command CRCI unspecified\n");
+- return ret;
+- }
++ if (!nandc->dma_bam_enabled) {
++ ret = of_property_read_u32(np, "qcom,cmd-crci",
++ &nandc->cmd_crci);
++ if (ret) {
++ dev_err(nandc->dev, "command CRCI unspecified\n");
++ return ret;
++ }
+
+- ret = of_property_read_u32(np, "qcom,data-crci", &nandc->data_crci);
+- if (ret) {
+- dev_err(nandc->dev, "data CRCI unspecified\n");
+- return ret;
++ ret = of_property_read_u32(np, "qcom,data-crci",
++ &nandc->data_crci);
++ if (ret) {
++ dev_err(nandc->dev, "data CRCI unspecified\n");
++ return ret;
++ }
+ }
+
+ return 0;
+@@ -2073,6 +2162,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
+ struct device_node *dn = dev->of_node, *child;
+ struct resource *res;
+ int ret;
++ struct qcom_nand_driver_data *driver_data;
+
+ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
+ if (!nandc)
+@@ -2087,7 +2177,10 @@ static int qcom_nandc_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
+- nandc->ecc_modes = (unsigned long)dev_data;
++ driver_data = (struct qcom_nand_driver_data *)dev_data;
++
++ nandc->ecc_modes = driver_data->ecc_modes;
++ nandc->dma_bam_enabled = driver_data->dma_bam_enabled;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nandc->base = devm_ioremap_resource(dev, res);
+@@ -2179,7 +2272,15 @@ static int qcom_nandc_remove(struct platform_device *pdev)
+ return 0;
+ }
+
+-#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
++struct qcom_nand_driver_data ebi2_nandc_bam_data = {
++ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
++ .dma_bam_enabled = true,
++};
++
++struct qcom_nand_driver_data ebi2_nandc_data = {
++ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
++ .dma_bam_enabled = false,
++};
+
+ /*
+ * data will hold a struct pointer containing more differences once we support
+@@ -2187,7 +2288,10 @@ static int qcom_nandc_remove(struct platform_device *pdev)
+ */
+ static const struct of_device_id qcom_nandc_of_match[] = {
+ { .compatible = "qcom,ipq806x-nand",
+- .data = (void *)EBI2_NANDC_ECC_MODES,
++ .data = (void *) &ebi2_nandc_data,
++ },
++ { .compatible = "qcom,ebi2-nandc-bam",
++ .data = (void *) &ebi2_nandc_bam_data,
+ },
+ {}
+ };
+--
+2.7.2