diff --git a/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch b/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch new file mode 100644 index 0000000000..ad9d1bbf4c --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch @@ -0,0 +1,382 @@ +From 074036f9de6b8c5fc642e8e2540950f6a35aa804 Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +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 +--- + .../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