This change add nand boot support for IPQ40xx based AP-DK04.1-C1 board using ubi image, also add sysupgrage support for AP-DK04.1-C1 and generates a sysupgrade.tar image.
Testing: *Tested on IPQ40xx AP-DK04.1-C1 and IPQ806x AP148 Board: a. NAND boot b. ubi sysupgrade Signed-off-by: Ram Chandra Jangir <rjan...@codeaurora.org> --- .../ipq806x/base-files/lib/upgrade/platform.sh | 1 + target/linux/ipq806x/image/Makefile | 1 + ...nd-Add-bam_dma-support-in-qcom_nand-drive.patch | 382 ++++++ ...nd-Added-bam-transaction-and-support-addi.patch | 1273 ++++++++++++++++++++ ...gine-qcom-bam_dma-Add-custom-data-mapping.patch | 217 ++++ ...ts-ipq4019-add-nand-and-qpic-bam-dma-node.patch | 83 ++ 6 files changed, 1957 insertions(+) create mode 100644 target/linux/ipq806x/patches-4.9/859-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch create mode 100644 target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch create mode 100644 target/linux/ipq806x/patches-4.9/861-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch create mode 100644 target/linux/ipq806x/patches-4.9/862-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh index 8970285..fd08db3 100644 --- a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh +++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh @@ -12,6 +12,7 @@ platform_pre_upgrade() { case "$board" in ap148 |\ + ap-dk04.1-c1 |\ d7800 |\ nbg6817 |\ r7500 |\ diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile index 6ebcde5..c49a73e 100644 --- a/target/linux/ipq806x/image/Makefile +++ b/target/linux/ipq806x/image/Makefile @@ -264,6 +264,7 @@ endef define Device/AP-DK04.1-C1 $(call Device/FitImage) + $(call Device/UbiFit) DEVICE_DTS := qcom-ipq4019-ap.dk04.1-c1 KERNEL_LOADADDR := 0x80208000 KERNEL_INSTALL := 1 diff --git a/target/linux/ipq806x/patches-4.9/859-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch b/target/linux/ipq806x/patches-4.9/859-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch new file mode 100644 index 0000000..ad9d1bb --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/859-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 <rjan...@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 <rjan...@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 diff --git a/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch b/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch new file mode 100644 index 0000000..674de77 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch @@ -0,0 +1,1273 @@ +From 645c7805f2602569263d7ac78050b2c9e91e3377 Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir <rjan...@codeaurora.org> +Date: Thu, 20 Apr 2017 10:23:00 +0530 +Subject: [PATCH] qcom: mtd: nand: Added bam transaction and support + additional CSRs + +This patch adds the following for NAND BAM DMA support + - Bam transaction which will be used for any NAND request. + It contains the array of command elements, command and + data sgl. This transaction will be resetted before every + request. + - Allocation function for NAND BAM transaction which will be + called only once at probe time. + - Reset function for NAND BAM transaction which will be called + before any new NAND request. + - Add support for additional CSRs. + NAND_READ_LOCATION - page offset for reading in BAM DMA mode + NAND_ERASED_CW_DETECT_CFG - status for erased code words + NAND_BUFFER_STATUS - status for ECC + +Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> +Signed-off-by: Ram Chandra Jangir <rjan...@codeaurora.org> +--- + drivers/mtd/nand/qcom_nandc.c | 631 +++++++++++++++++++++++++++++++++++---- + include/linux/dma/qcom_bam_dma.h | 149 +++++++++ + 2 files changed, 721 insertions(+), 59 deletions(-) + create mode 100644 include/linux/dma/qcom_bam_dma.h + +diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c +index 76a0ffc..9d941e3 100644 +--- a/drivers/mtd/nand/qcom_nandc.c ++++ b/drivers/mtd/nand/qcom_nandc.c +@@ -22,6 +22,7 @@ + #include <linux/of.h> + #include <linux/of_device.h> + #include <linux/delay.h> ++#include <linux/dma/qcom_bam_dma.h> + + /* NANDc reg offsets */ + #define NAND_FLASH_CMD 0x00 +@@ -53,6 +54,8 @@ + #define NAND_VERSION 0xf08 + #define NAND_READ_LOCATION_0 0xf20 + #define NAND_READ_LOCATION_1 0xf24 ++#define NAND_READ_LOCATION_2 0xf28 ++#define NAND_READ_LOCATION_3 0xf2c + + /* dummy register offsets, used by write_reg_dma */ + #define NAND_DEV_CMD1_RESTORE 0xdead +@@ -131,6 +134,11 @@ + #define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) + #define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) + ++/* NAND_READ_LOCATION_n bits */ ++#define READ_LOCATION_OFFSET 0 ++#define READ_LOCATION_SIZE 16 ++#define READ_LOCATION_LAST 31 ++ + /* Version Mask */ + #define NAND_VERSION_MAJOR_MASK 0xf0000000 + #define NAND_VERSION_MAJOR_SHIFT 28 +@@ -148,6 +156,9 @@ + #define FETCH_ID 0xb + #define RESET_DEVICE 0xd + ++/* NAND_CTRL bits */ ++#define BAM_MODE_EN BIT(0) ++ + /* + * the NAND controller performs reads/writes with ECC in 516 byte chunks. + * the driver calls the chunks 'step' or 'codeword' interchangeably +@@ -169,12 +180,77 @@ + #define ECC_BCH_4BIT BIT(2) + #define ECC_BCH_8BIT BIT(3) + ++/* Flags used for BAM DMA desc preparation*/ ++/* Don't set the EOT in current tx sgl */ ++#define DMA_DESC_FLAG_NO_EOT (0x0001) ++/* Set the NWD flag in current sgl */ ++#define DMA_DESC_FLAG_BAM_NWD (0x0002) ++/* Close current sgl and start writing in another sgl */ ++#define DMA_DESC_FLAG_BAM_NEXT_SGL (0x0004) ++/* ++ * Erased codeword status is being used two times in single transfer so this ++ * flag will determine the current value of erased codeword status register ++ */ ++#define DMA_DESC_ERASED_CW_SET (0x0008) ++ ++/* Returns the dma address for reg read buffer */ ++#define REG_BUF_DMA_ADDR(chip, vaddr) \ ++ ((chip)->reg_read_buf_phys + \ ++ ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) ++ ++/* Returns the nand register physical address */ ++#define NAND_REG_PHYS_ADDRESS(chip, addr) \ ++ ((chip)->base_dma + (addr)) ++ ++/* command element array size in bam transaction */ ++#define BAM_CMD_ELEMENT_SIZE (256) ++/* command sgl size in bam transaction */ ++#define BAM_CMD_SGL_SIZE (256) ++/* data sgl size in bam transaction */ ++#define BAM_DATA_SGL_SIZE (128) ++ ++/* ++ * This data type corresponds to the BAM transaction which will be used for any ++ * nand request. ++ * @bam_ce - the array of bam command elements ++ * @cmd_sgl - sgl for nand bam command pipe ++ * @tx_sgl - sgl for nand bam consumer pipe ++ * @rx_sgl - sgl for nand bam producer pipe ++ * @bam_ce_index - the index in bam_ce which is available for next sgl request ++ * @pre_bam_ce_index - the index in bam_ce which marks the start position ce ++ * for current sgl. It will be used for size calculation ++ * for current sgl ++ * @cmd_sgl_cnt - no of entries in command sgl. ++ * @tx_sgl_cnt - no of entries in tx sgl. ++ * @rx_sgl_cnt - no of entries in rx sgl. ++ */ ++struct bam_transaction { ++ struct bam_cmd_element bam_ce[BAM_CMD_ELEMENT_SIZE]; ++ struct qcom_bam_sgl cmd_sgl[BAM_CMD_SGL_SIZE]; ++ struct qcom_bam_sgl tx_sgl[BAM_DATA_SGL_SIZE]; ++ struct qcom_bam_sgl rx_sgl[BAM_DATA_SGL_SIZE]; ++ uint32_t bam_ce_index; ++ uint32_t pre_bam_ce_index; ++ uint32_t cmd_sgl_cnt; ++ uint32_t tx_sgl_cnt; ++ uint32_t rx_sgl_cnt; ++}; ++ ++/** ++ * This data type corresponds to the nand dma descriptor ++ * @list - list for desc_info ++ * @dir - DMA transfer direction ++ * @sgl - sgl which will be used for single sgl dma descriptor ++ * @dma_desc - low level dma engine descriptor ++ * @bam_desc_data - used for bam desc mappings ++ */ + struct desc_info { + struct list_head node; + + enum dma_data_direction dir; + struct scatterlist sgl; + struct dma_async_tx_descriptor *dma_desc; ++ struct qcom_bam_custom_data bam_desc_data; + }; + + /* +@@ -202,6 +278,13 @@ struct nandc_regs { + __le32 orig_vld; + + __le32 ecc_buf_cfg; ++ __le32 read_location0; ++ __le32 read_location1; ++ __le32 read_location2; ++ __le32 read_location3; ++ ++ __le32 erased_cw_detect_cfg_clr; ++ __le32 erased_cw_detect_cfg_set; + }; + + /* +@@ -217,6 +300,7 @@ struct nandc_regs { + * @aon_clk: another controller clock + * + * @chan: dma channel ++ * @bam_txn: contains the bam transaction address + * @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) +@@ -242,6 +326,7 @@ struct nandc_regs { + struct qcom_nand_controller { + struct nand_hw_control controller; + struct list_head host_list; ++ struct bam_transaction *bam_txn; + + struct device *dev; + +@@ -342,6 +427,45 @@ struct qcom_nand_driver_data { + bool dma_bam_enabled; + }; + ++/* Allocates and Initializes the BAM transaction */ ++struct bam_transaction *alloc_bam_transaction( ++ struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn; ++ ++ bam_txn = kzalloc(sizeof(*bam_txn), GFP_KERNEL); ++ ++ if (!bam_txn) ++ return NULL; ++ ++ bam_txn->bam_ce_index = 0; ++ bam_txn->pre_bam_ce_index = 0; ++ bam_txn->cmd_sgl_cnt = 0; ++ bam_txn->tx_sgl_cnt = 0; ++ bam_txn->rx_sgl_cnt = 0; ++ ++ qcom_bam_sg_init_table(bam_txn->cmd_sgl, BAM_CMD_SGL_SIZE); ++ qcom_bam_sg_init_table(bam_txn->tx_sgl, BAM_DATA_SGL_SIZE); ++ qcom_bam_sg_init_table(bam_txn->rx_sgl, BAM_DATA_SGL_SIZE); ++ ++ return bam_txn; ++} ++ ++/* Clears the BAM transaction index */ ++void clear_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (!nandc->dma_bam_enabled) ++ return; ++ ++ bam_txn->bam_ce_index = 0; ++ bam_txn->pre_bam_ce_index = 0; ++ bam_txn->cmd_sgl_cnt = 0; ++ bam_txn->tx_sgl_cnt = 0; ++ bam_txn->rx_sgl_cnt = 0; ++} ++ + static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) + { + return container_of(chip, struct qcom_nand_host, chip); +@@ -398,6 +522,16 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) + return ®s->orig_vld; + case NAND_EBI2_ECC_BUF_CFG: + return ®s->ecc_buf_cfg; ++ case NAND_BUFFER_STATUS: ++ return ®s->clrreadstatus; ++ case NAND_READ_LOCATION_0: ++ return ®s->read_location0; ++ case NAND_READ_LOCATION_1: ++ return ®s->read_location1; ++ case NAND_READ_LOCATION_2: ++ return ®s->read_location2; ++ case NAND_READ_LOCATION_3: ++ return ®s->read_location3; + default: + return NULL; + } +@@ -439,7 +573,7 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) + { + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); +- u32 cmd, cfg0, cfg1, ecc_bch_cfg; ++ u32 cmd, cfg0, cfg1, ecc_bch_cfg, read_location0; + + if (read) { + if (host->use_ecc) +@@ -456,12 +590,20 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) + + cfg1 = host->cfg1; + ecc_bch_cfg = host->ecc_bch_cfg; ++ if (read) ++ read_location0 = (0 << READ_LOCATION_OFFSET) | ++ (host->cw_data << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST); + } else { + cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | + (num_cw - 1) << CW_PER_PAGE; + + cfg1 = host->cfg1_raw; + ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ if (read) ++ read_location0 = (0 << READ_LOCATION_OFFSET) | ++ (host->cw_size << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST); + } + + nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); +@@ -472,8 +614,104 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) + nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ ++ if (read) ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, read_location0); + } + ++/* ++ * Prepares the command descriptor for BAM DMA which will be used for NAND ++ * register read and write. The command descriptor requires the command ++ * to be formed in command element type so this function uses the command ++ * element from bam transaction ce array and fills the same with required ++ * data. A single SGL can contain multiple command elements so ++ * DMA_DESC_FLAG_BAM_NEXT_SGL will be used for starting the separate SGL ++ * after the current command element. ++ */ ++static int prep_dma_desc_command(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) ++{ ++ int bam_ce_size; ++ int i; ++ struct bam_cmd_element *bam_ce_buffer; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_index]; ++ ++ /* fill the command desc */ ++ for (i = 0; i < size; i++) { ++ if (read) { ++ qcom_prep_bam_ce(&bam_ce_buffer[i], ++ NAND_REG_PHYS_ADDRESS(nandc, reg_off + 4 * i), ++ BAM_READ_COMMAND, ++ REG_BUF_DMA_ADDR(nandc, ++ (unsigned int *)vaddr + i)); ++ } else { ++ qcom_prep_bam_ce(&bam_ce_buffer[i], ++ NAND_REG_PHYS_ADDRESS(nandc, reg_off + 4 * i), ++ BAM_WRITE_COMMAND, ++ *((unsigned int *)vaddr + i)); ++ } ++ } ++ ++ /* use the separate sgl after this command */ ++ if (flags & DMA_DESC_FLAG_BAM_NEXT_SGL) { ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->pre_bam_ce_index]; ++ bam_txn->bam_ce_index += size; ++ bam_ce_size = (bam_txn->bam_ce_index - ++ bam_txn->pre_bam_ce_index) * ++ sizeof(struct bam_cmd_element); ++ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].sgl, ++ bam_ce_buffer, ++ bam_ce_size); ++ if (flags & DMA_DESC_FLAG_BAM_NWD) ++ bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].dma_flags = ++ DESC_FLAG_NWD | DESC_FLAG_CMD; ++ else ++ bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].dma_flags = ++ DESC_FLAG_CMD; ++ ++ bam_txn->cmd_sgl_cnt++; ++ bam_txn->pre_bam_ce_index = bam_txn->bam_ce_index; ++ } else { ++ bam_txn->bam_ce_index += size; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Prepares the data descriptor for BAM DMA which will be used for NAND ++ * data read and write. ++ */ ++static int prep_dma_desc_data_bam(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (read) { ++ sg_set_buf(&bam_txn->rx_sgl[bam_txn->rx_sgl_cnt].sgl, ++ vaddr, size); ++ bam_txn->rx_sgl[bam_txn->rx_sgl_cnt].dma_flags = 0; ++ bam_txn->rx_sgl_cnt++; ++ } else { ++ sg_set_buf(&bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].sgl, ++ vaddr, size); ++ if (flags & DMA_DESC_FLAG_NO_EOT) ++ bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].dma_flags = 0; ++ else ++ bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].dma_flags = ++ DESC_FLAG_EOT; ++ ++ bam_txn->tx_sgl_cnt++; ++ } ++ ++ return 0; ++} ++ ++/* Prepares the dma desciptor for adm dma engine */ + static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, + int reg_off, const void *vaddr, int size, + bool flow_control) +@@ -552,7 +790,7 @@ static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, + * @num_regs: number of registers to read + */ + static int read_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs) ++ int num_regs, unsigned int flags) + { + bool flow_control = false; + void *vaddr; +@@ -561,10 +799,18 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) + flow_control = true; + +- size = num_regs * sizeof(u32); + vaddr = nandc->reg_read_buf + nandc->reg_read_pos; + nandc->reg_read_pos += num_regs; + ++ if (nandc->dma_bam_enabled) { ++ size = num_regs; ++ ++ return prep_dma_desc_command(nandc, true, first, vaddr, size, ++ flags); ++ } ++ ++ size = num_regs * sizeof(u32); ++ + return prep_dma_desc(nandc, true, first, vaddr, size, flow_control); + } + +@@ -576,7 +822,7 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, + * @num_regs: number of registers to write + */ + static int write_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs) ++ int num_regs, unsigned int flags) + { + bool flow_control = false; + struct nandc_regs *regs = nandc->regs; +@@ -588,12 +834,29 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, + if (first == NAND_FLASH_CMD) + flow_control = true; + ++ if (first == NAND_ERASED_CW_DETECT_CFG) { ++ if (flags & DMA_DESC_ERASED_CW_SET) ++ vaddr = ®s->erased_cw_detect_cfg_set; ++ else ++ vaddr = ®s->erased_cw_detect_cfg_clr; ++ } ++ ++ if (first == NAND_EXEC_CMD) ++ flags |= DMA_DESC_FLAG_BAM_NWD; ++ + if (first == NAND_DEV_CMD1_RESTORE) + first = NAND_DEV_CMD1; + + if (first == NAND_DEV_CMD_VLD_RESTORE) + first = NAND_DEV_CMD_VLD; + ++ if (nandc->dma_bam_enabled) { ++ size = num_regs; ++ ++ return prep_dma_desc_command(nandc, false, first, vaddr, size, ++ flags); ++ } ++ + size = num_regs * sizeof(u32); + + return prep_dma_desc(nandc, false, first, vaddr, size, flow_control); +@@ -608,8 +871,12 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, + * @size: DMA transaction size in bytes + */ + static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size) ++ const u8 *vaddr, int size, unsigned int flags) + { ++ if (nandc->dma_bam_enabled) ++ return prep_dma_desc_data_bam(nandc, true, reg_off, vaddr, size, ++ flags); ++ + return prep_dma_desc(nandc, true, reg_off, vaddr, size, false); + } + +@@ -622,8 +889,12 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, + * @size: DMA transaction size in bytes + */ + static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size) ++ const u8 *vaddr, int size, unsigned int flags) + { ++ if (nandc->dma_bam_enabled) ++ return prep_dma_desc_data_bam(nandc, false, reg_off, vaddr, ++ size, flags); ++ + return prep_dma_desc(nandc, false, reg_off, vaddr, size, false); + } + +@@ -633,14 +904,57 @@ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, + */ + static void config_cw_read(struct qcom_nand_controller *nandc) + { +- write_reg_dma(nandc, NAND_FLASH_CMD, 3); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3); +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); + +- write_reg_dma(nandc, NAND_EXEC_CMD, 1); ++ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); ++ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, ++ DMA_DESC_ERASED_CW_SET); ++ if (nandc->dma_bam_enabled) ++ write_reg_dma(nandc, NAND_READ_LOCATION_0, 1, ++ DMA_DESC_FLAG_BAM_NEXT_SGL); ++ + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 2); +- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NWD | ++ DMA_DESC_FLAG_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); ++ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ DMA_DESC_FLAG_BAM_NEXT_SGL); ++} ++ ++/* ++ * Helpers to prepare DMA descriptors for configuring registers ++ * before reading a NAND page with BAM. ++ */ ++static void config_bam_page_read(struct qcom_nand_controller *nandc) ++{ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); ++ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, ++ DMA_DESC_ERASED_CW_SET | ++ DMA_DESC_FLAG_BAM_NEXT_SGL); ++} ++ ++/* ++ * Helpers to prepare DMA descriptors for configuring registers ++ * before reading each codeword in NAND page with BAM. ++ */ ++static void config_bam_cw_read(struct qcom_nand_controller *nandc) ++{ ++ if (nandc->dma_bam_enabled) ++ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, 0); ++ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); ++ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ DMA_DESC_FLAG_BAM_NEXT_SGL); + } + + /* +@@ -649,19 +963,20 @@ static void config_cw_read(struct qcom_nand_controller *nandc) + */ + static void config_cw_write_pre(struct qcom_nand_controller *nandc) + { +- write_reg_dma(nandc, NAND_FLASH_CMD, 3); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3); +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); ++ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); ++ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, ++ DMA_DESC_FLAG_BAM_NEXT_SGL); + } + + static void config_cw_write_post(struct qcom_nand_controller *nandc) + { +- write_reg_dma(nandc, NAND_EXEC_CMD, 1); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_STATUS, 1); +- write_reg_dma(nandc, NAND_READ_STATUS, 1); ++ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, NAND_READ_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + } + + /* +@@ -675,6 +990,8 @@ static int nandc_param(struct qcom_nand_host *host) + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + ++ clear_bam_transaction(nandc); ++ + /* + * NAND_CMD_PARAM is called before we know much about the FLASH chip + * in use. we configure the controller to perform a raw read of 512 +@@ -708,9 +1025,13 @@ static int nandc_param(struct qcom_nand_host *host) + + nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); + nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (0 << READ_LOCATION_OFFSET) | ++ (512 << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); + +- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1); +- write_reg_dma(nandc, NAND_DEV_CMD1, 1); ++ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); ++ write_reg_dma(nandc, NAND_DEV_CMD1, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + + nandc->buf_count = 512; + memset(nandc->data_buffer, 0xff, nandc->buf_count); +@@ -718,11 +1039,12 @@ static int nandc_param(struct qcom_nand_host *host) + config_cw_read(nandc); + + read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, +- nandc->buf_count); ++ nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ +- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1); +- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1); ++ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); ++ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, ++ DMA_DESC_FLAG_BAM_NEXT_SGL); + + return 0; + } +@@ -733,6 +1055,8 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + ++ clear_bam_transaction(nandc); ++ + nandc_set_reg(nandc, NAND_FLASH_CMD, + BLOCK_ERASE | PAGE_ACC | LAST_PAGE); + nandc_set_reg(nandc, NAND_ADDR0, page_addr); +@@ -744,14 +1068,15 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) + nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 3); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 2); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1); + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); ++ write_reg_dma(nandc, NAND_FLASH_CMD, 3, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_STATUS, 1); +- write_reg_dma(nandc, NAND_READ_STATUS, 1); ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ ++ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, NAND_READ_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + + return 0; + } +@@ -765,16 +1090,19 @@ static int read_id(struct qcom_nand_host *host, int column) + if (column == -1) + return 0; + ++ clear_bam_transaction(nandc); ++ + nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); + nandc_set_reg(nandc, NAND_ADDR0, column); + nandc_set_reg(nandc, NAND_ADDR1, 0); +- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); ++ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, ++ nandc->dma_bam_enabled ? 0 : DM_EN); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 4); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1); ++ write_reg_dma(nandc, NAND_FLASH_CMD, 4, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_READ_ID, 1); ++ read_reg_dma(nandc, NAND_READ_ID, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); + + return 0; + } +@@ -785,15 +1113,61 @@ static int reset(struct qcom_nand_host *host) + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + ++ clear_bam_transaction(nandc); ++ + nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1); ++ write_reg_dma(nandc, NAND_FLASH_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); ++ ++ return 0; ++} ++ ++static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, ++ struct qcom_bam_sgl *bam_sgl, ++ int sgl_cnt, ++ enum dma_transfer_direction direction) ++{ ++ struct desc_info *desc; ++ struct dma_async_tx_descriptor *dma_desc; ++ ++ if (!qcom_bam_map_sg(nandc->dev, bam_sgl, sgl_cnt, direction)) { ++ dev_err(nandc->dev, "failure in mapping sgl\n"); ++ return -ENOMEM; ++ } ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) { ++ qcom_bam_unmap_sg(nandc->dev, bam_sgl, sgl_cnt, direction); ++ return -ENOMEM; ++ } ++ ++ ++ desc->bam_desc_data.dir = direction; ++ desc->bam_desc_data.sgl_cnt = sgl_cnt; ++ desc->bam_desc_data.bam_sgl = bam_sgl; ++ ++ dma_desc = dmaengine_prep_dma_custom_mapping(chan, ++ &desc->bam_desc_data, ++ 0); ++ ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failure in cmd prep desc\n"); ++ qcom_bam_unmap_sg(nandc->dev, bam_sgl, sgl_cnt, direction); ++ kfree(desc); ++ return -EINVAL; ++ } ++ ++ desc->dma_desc = dma_desc; + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); ++ list_add_tail(&desc->node, &nandc->desc_list); + + return 0; ++ + } + + /* helpers to submit/free our list of dma descriptors */ +@@ -801,12 +1175,46 @@ static int submit_descs(struct qcom_nand_controller *nandc) + { + struct desc_info *desc; + dma_cookie_t cookie = 0; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ int r; ++ ++ if (nandc->dma_bam_enabled) { ++ if (bam_txn->rx_sgl_cnt) { ++ r = prepare_bam_async_desc(nandc, nandc->rx_chan, ++ bam_txn->rx_sgl, bam_txn->rx_sgl_cnt, ++ DMA_DEV_TO_MEM); ++ if (r) ++ return r; ++ } ++ ++ if (bam_txn->tx_sgl_cnt) { ++ r = prepare_bam_async_desc(nandc, nandc->tx_chan, ++ bam_txn->tx_sgl, bam_txn->tx_sgl_cnt, ++ DMA_MEM_TO_DEV); ++ if (r) ++ return r; ++ } ++ ++ r = prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ bam_txn->cmd_sgl, bam_txn->cmd_sgl_cnt, ++ DMA_MEM_TO_DEV); ++ if (r) ++ return r; ++ } + + list_for_each_entry(desc, &nandc->desc_list, node) + cookie = dmaengine_submit(desc->dma_desc); + +- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) +- return -ETIMEDOUT; ++ if (nandc->dma_bam_enabled) { ++ dma_async_issue_pending(nandc->tx_chan); ++ dma_async_issue_pending(nandc->rx_chan); ++ ++ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) ++ return -ETIMEDOUT; ++ } else { ++ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) ++ return -ETIMEDOUT; ++ } + + return 0; + } +@@ -817,7 +1225,16 @@ static void free_descs(struct qcom_nand_controller *nandc) + + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); +- dma_unmap_sg(nandc->dev, &desc->sgl, 1, desc->dir); ++ ++ if (nandc->dma_bam_enabled) ++ qcom_bam_unmap_sg(nandc->dev, ++ desc->bam_desc_data.bam_sgl, ++ desc->bam_desc_data.sgl_cnt, ++ desc->bam_desc_data.dir); ++ else ++ dma_unmap_sg(nandc->dev, &desc->sgl, 1, ++ desc->dir); ++ + kfree(desc); + } + } +@@ -1128,6 +1545,9 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, ret; + ++ if (nandc->dma_bam_enabled) ++ config_bam_page_read(nandc); ++ + /* queue cmd descs for each codeword */ + for (i = 0; i < ecc->steps; i++) { + int data_size, oob_size; +@@ -1141,11 +1561,36 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, + oob_size = host->ecc_bytes_hw + host->spare_bytes; + } + +- config_cw_read(nandc); ++ if (nandc->dma_bam_enabled) { ++ if (data_buf && oob_buf) { ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (0 << READ_LOCATION_OFFSET) | ++ (data_size << READ_LOCATION_SIZE) | ++ (0 << READ_LOCATION_LAST)); ++ nandc_set_reg(nandc, NAND_READ_LOCATION_1, ++ (data_size << READ_LOCATION_OFFSET) | ++ (oob_size << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); ++ } else if (data_buf) { ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (0 << READ_LOCATION_OFFSET) | ++ (data_size << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); ++ } else { ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (data_size << READ_LOCATION_OFFSET) | ++ (oob_size << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); ++ } ++ ++ config_bam_cw_read(nandc); ++ } else { ++ config_cw_read(nandc); ++ } + + if (data_buf) + read_data_dma(nandc, FLASH_BUF_ACC, data_buf, +- data_size); ++ data_size, 0); + + /* + * when ecc is enabled, the controller doesn't read the real +@@ -1161,7 +1606,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, + *oob_buf++ = 0xff; + + read_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size); ++ oob_buf, oob_size, 0); + } + + if (data_buf) +@@ -1200,10 +1645,14 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) + + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, true); ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (0 << READ_LOCATION_OFFSET) | ++ (size << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); + + config_cw_read(nandc); + +- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size); ++ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); + + ret = submit_descs(nandc); + if (ret) +@@ -1226,6 +1675,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; + ++ clear_bam_transaction(nandc); + ret = read_page_ecc(host, data_buf, oob_buf); + if (ret) { + dev_err(nandc->dev, "failure to read page\n"); +@@ -1245,13 +1695,19 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, + u8 *data_buf, *oob_buf; + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, ret; ++ int read_location; + + data_buf = buf; + oob_buf = chip->oob_poi; + + host->use_ecc = false; ++ ++ clear_bam_transaction(nandc); + update_rw_regs(host, ecc->steps, true); + ++ if (nandc->dma_bam_enabled) ++ config_bam_page_read(nandc); ++ + for (i = 0; i < ecc->steps; i++) { + int data_size1, data_size2, oob_size1, oob_size2; + int reg_off = FLASH_BUF_ACC; +@@ -1269,21 +1725,49 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + +- config_cw_read(nandc); ++ if (nandc->dma_bam_enabled) { ++ read_location = 0; ++ nandc_set_reg(nandc, NAND_READ_LOCATION_0, ++ (read_location << READ_LOCATION_OFFSET) | ++ (data_size1 << READ_LOCATION_SIZE) | ++ (0 << READ_LOCATION_LAST)); ++ read_location += data_size1; ++ ++ nandc_set_reg(nandc, NAND_READ_LOCATION_1, ++ (read_location << READ_LOCATION_OFFSET) | ++ (oob_size1 << READ_LOCATION_SIZE) | ++ (0 << READ_LOCATION_LAST)); ++ read_location += oob_size1; ++ ++ nandc_set_reg(nandc, NAND_READ_LOCATION_2, ++ (read_location << READ_LOCATION_OFFSET) | ++ (data_size2 << READ_LOCATION_SIZE) | ++ (0 << READ_LOCATION_LAST)); ++ read_location += data_size2; ++ ++ nandc_set_reg(nandc, NAND_READ_LOCATION_3, ++ (read_location << READ_LOCATION_OFFSET) | ++ (oob_size2 << READ_LOCATION_SIZE) | ++ (1 << READ_LOCATION_LAST)); ++ ++ config_bam_cw_read(nandc); ++ } else { ++ config_cw_read(nandc); ++ } + +- read_data_dma(nandc, reg_off, data_buf, data_size1); ++ read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + data_buf += data_size1; + +- read_data_dma(nandc, reg_off, oob_buf, oob_size1); ++ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + oob_buf += oob_size1; + +- read_data_dma(nandc, reg_off, data_buf, data_size2); ++ read_data_dma(nandc, reg_off, data_buf, data_size2, 0); + reg_off += data_size2; + data_buf += data_size2; + +- read_data_dma(nandc, reg_off, oob_buf, oob_size2); ++ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + } + +@@ -1306,6 +1790,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int ret; + + clear_read_regs(nandc); ++ clear_bam_transaction(nandc); + + host->use_ecc = true; + set_address(host, 0, page); +@@ -1329,6 +1814,7 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + int i, ret; + + clear_read_regs(nandc); ++ clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -1350,7 +1836,8 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + + config_cw_write_pre(nandc); + +- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size); ++ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (ecc->steps - 1) ? DMA_DESC_FLAG_NO_EOT : 0); + + /* + * when ECC is enabled, we don't really need to write anything +@@ -1363,7 +1850,7 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + oob_buf += host->bbm_size; + + write_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size); ++ oob_buf, oob_size, 0); + } + + config_cw_write_post(nandc); +@@ -1393,6 +1880,7 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, + int i, ret; + + clear_read_regs(nandc); ++ clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -1419,19 +1907,22 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, + + config_cw_write_pre(nandc); + +- write_data_dma(nandc, reg_off, data_buf, data_size1); ++ write_data_dma(nandc, reg_off, data_buf, data_size1, ++ DMA_DESC_FLAG_NO_EOT); + reg_off += data_size1; + data_buf += data_size1; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size1); ++ write_data_dma(nandc, reg_off, oob_buf, oob_size1, ++ DMA_DESC_FLAG_NO_EOT); + reg_off += oob_size1; + oob_buf += oob_size1; + +- write_data_dma(nandc, reg_off, data_buf, data_size2); ++ write_data_dma(nandc, reg_off, data_buf, data_size2, ++ DMA_DESC_FLAG_NO_EOT); + reg_off += data_size2; + data_buf += data_size2; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size2); ++ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + + config_cw_write_post(nandc); +@@ -1467,6 +1958,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + + host->use_ecc = true; + ++ clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + return ret; +@@ -1486,7 +1978,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + + config_cw_write_pre(nandc); + write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, +- data_size + oob_size); ++ data_size + oob_size, 0); + config_cw_write_post(nandc); + + ret = submit_descs(nandc); +@@ -1524,6 +2016,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) + */ + host->use_ecc = false; + ++ clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + goto err; +@@ -1554,6 +2047,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) + int page, ret, status = 0; + + clear_read_regs(nandc); ++ clear_bam_transaction(nandc); + + /* + * to mark the BBM as bad, we flash the entire last codeword with 0s. +@@ -1570,7 +2064,8 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) + update_rw_regs(host, 1, false); + + config_cw_write_pre(nandc); +- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size); ++ write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, ++ host->cw_size, 0); + config_cw_write_post(nandc); + + ret = submit_descs(nandc); +@@ -1930,6 +2425,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) + + host->clrflashstatus = FS_READY_BSY_N; + host->clrreadstatus = 0xc0; ++ nandc->regs->erased_cw_detect_cfg_clr = CLR_ERASED_PAGE_DET; ++ nandc->regs->erased_cw_detect_cfg_set = SET_ERASED_PAGE_DET; + + dev_dbg(nandc->dev, + "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", +@@ -2008,6 +2505,12 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) + dev_err(nandc->dev, "failed to request cmd channel\n"); + return -ENODEV; + } ++ ++ nandc->bam_txn = alloc_bam_transaction(nandc); ++ if (!nandc->bam_txn) { ++ dev_err(nandc->dev, "failed to allocate bam transaction\n"); ++ return -ENOMEM; ++ } + } + + INIT_LIST_HEAD(&nandc->desc_list); +@@ -2043,6 +2546,9 @@ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) + devm_kfree(nandc->dev, nandc->reg_read_buf); + } + ++ if (nandc->bam_txn) ++ devm_kfree(nandc->dev, nandc->bam_txn); ++ + if (nandc->regs) + devm_kfree(nandc->dev, nandc->regs); + +@@ -2053,11 +2559,18 @@ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) + /* one time setup of a few nand controller registers */ + static int qcom_nandc_setup(struct qcom_nand_controller *nandc) + { ++ u32 nand_ctrl; ++ + /* kill onenand */ + nandc_write(nandc, SFLASHC_BURST_CFG, 0); + +- /* enable ADM DMA */ +- nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); ++ /* enable ADM or BAM DMA */ ++ if (!nandc->dma_bam_enabled) { ++ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); ++ } else { ++ nand_ctrl = nandc_read(nandc, NAND_CTRL); ++ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); ++ } + + /* save the original values of these registers */ + nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1); +diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h +new file mode 100644 +index 0000000..7e87a85 +--- /dev/null ++++ b/include/linux/dma/qcom_bam_dma.h +@@ -0,0 +1,149 @@ ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#ifndef _QCOM_BAM_DMA_H ++#define _QCOM_BAM_DMA_H ++ ++#include <linux/dma-mapping.h> ++ ++#define DESC_FLAG_INT BIT(15) ++#define DESC_FLAG_EOT BIT(14) ++#define DESC_FLAG_EOB BIT(13) ++#define DESC_FLAG_NWD BIT(12) ++#define DESC_FLAG_CMD BIT(11) ++ ++/* ++ * QCOM BAM DMA SGL struct ++ * ++ * @sgl: DMA SGL ++ * @dma_flags: BAM DMA flags ++ */ ++struct qcom_bam_sgl { ++ struct scatterlist sgl; ++ unsigned int dma_flags; ++}; ++ ++/* ++ * This data type corresponds to the native Command Element ++ * supported by BAM DMA Engine. ++ * ++ * @addr - register address. ++ * @command - command type. ++ * @data - for write command: content to be written into peripheral register. ++ * for read command: dest addr to write peripheral register value to. ++ * @mask - register mask. ++ * @reserved - for future usage. ++ * ++ */ ++struct bam_cmd_element { ++ __le32 addr:24; ++ __le32 command:8; ++ __le32 data; ++ __le32 mask; ++ __le32 reserved; ++}; ++ ++/* ++ * This enum indicates the command type in a command element ++ */ ++enum bam_command_type { ++ BAM_WRITE_COMMAND = 0, ++ BAM_READ_COMMAND, ++}; ++ ++/* ++ * qcom_bam_sg_init_table - Init QCOM BAM SGL ++ * @bam_sgl: bam sgl ++ * @nents: number of entries in bam sgl ++ * ++ * This function performs the initialization for each SGL in BAM SGL ++ * with generic SGL API. ++ */ ++static inline void qcom_bam_sg_init_table(struct qcom_bam_sgl *bam_sgl, ++ unsigned int nents) ++{ ++ int i; ++ ++ for (i = 0; i < nents; i++) ++ sg_init_table(&bam_sgl[i].sgl, 1); ++} ++ ++/* ++ * qcom_bam_unmap_sg - Unmap QCOM BAM SGL ++ * @dev: device for which unmapping needs to be done ++ * @bam_sgl: bam sgl ++ * @nents: number of entries in bam sgl ++ * @dir: dma transfer direction ++ * ++ * This function performs the DMA unmapping for each SGL in BAM SGL ++ * with generic SGL API. ++ */ ++static inline void qcom_bam_unmap_sg(struct device *dev, ++ struct qcom_bam_sgl *bam_sgl, int nents, enum dma_data_direction dir) ++{ ++ int i; ++ ++ for (i = 0; i < nents; i++) ++ dma_unmap_sg(dev, &bam_sgl[i].sgl, 1, dir); ++} ++ ++/* ++ * qcom_bam_map_sg - Map QCOM BAM SGL ++ * @dev: device for which mapping needs to be done ++ * @bam_sgl: bam sgl ++ * @nents: number of entries in bam sgl ++ * @dir: dma transfer direction ++ * ++ * This function performs the DMA mapping for each SGL in BAM SGL ++ * with generic SGL API. ++ * ++ * returns 0 on error and > 0 on success ++ */ ++static inline int qcom_bam_map_sg(struct device *dev, ++ struct qcom_bam_sgl *bam_sgl, int nents, enum dma_data_direction dir) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < nents; i++) { ++ ret = dma_map_sg(dev, &bam_sgl[i].sgl, 1, dir); ++ if (!ret) ++ break; ++ } ++ ++ /* unmap the mapped sgl from previous loop in case of error */ ++ if (!ret) ++ qcom_bam_unmap_sg(dev, bam_sgl, i, dir); ++ ++ return ret; ++} ++ ++/* ++ * qcom_prep_bam_ce - Wrapper function to prepare a single BAM command element ++ * with the data that is passed to this function. ++ * @bam_ce: bam command element ++ * @addr: target address ++ * @command: command in bam_command_type ++ * @data: actual data for write and dest addr for read ++ */ ++static inline void qcom_prep_bam_ce(struct bam_cmd_element *bam_ce, ++ uint32_t addr, uint32_t command, uint32_t data) ++{ ++ bam_ce->addr = cpu_to_le32(addr); ++ bam_ce->command = cpu_to_le32(command); ++ bam_ce->data = cpu_to_le32(data); ++ bam_ce->mask = 0xFFFFFFFF; ++} ++#endif +-- +2.7.2 diff --git a/target/linux/ipq806x/patches-4.9/861-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch b/target/linux/ipq806x/patches-4.9/861-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch new file mode 100644 index 0000000..3b26120 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/861-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch @@ -0,0 +1,217 @@ +From 5a7ccdf845d64b385affdcffaf2defbe9848be15 Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir <rjan...@codeaurora.org> +Date: Thu, 20 Apr 2017 10:39:00 +0530 +Subject: [PATCH] dmaengine: qcom: bam_dma: Add custom data mapping + +Add a new function to support for preparing DMA descriptor +for custom data. + +Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> +Signed-off-by: Ram Chandra Jangir <rjan...@codeaurora.org> +--- + drivers/dma/qcom/bam_dma.c | 97 +++++++++++++++++++++++++++++++++++++--- + include/linux/dma/qcom_bam_dma.h | 14 ++++++ + include/linux/dmaengine.h | 14 ++++++ + 3 files changed, 119 insertions(+), 6 deletions(-) + +diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c +index 03c4eb3..bde8d70 100644 +--- a/drivers/dma/qcom/bam_dma.c ++++ b/drivers/dma/qcom/bam_dma.c +@@ -49,6 +49,7 @@ + #include <linux/clk.h> + #include <linux/dmaengine.h> + #include <linux/pm_runtime.h> ++#include <linux/dma/qcom_bam_dma.h> + + #include "../dmaengine.h" + #include "../virt-dma.h" +@@ -61,11 +62,6 @@ struct bam_desc_hw { + + #define BAM_DMA_AUTOSUSPEND_DELAY 100 + +-#define DESC_FLAG_INT BIT(15) +-#define DESC_FLAG_EOT BIT(14) +-#define DESC_FLAG_EOB BIT(13) +-#define DESC_FLAG_NWD BIT(12) +- + struct bam_async_desc { + struct virt_dma_desc vd; + +@@ -670,6 +666,93 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, + } + + /** ++ * bam_prep_dma_custom_mapping - Prep DMA descriptor from custom data ++ * ++ * @chan: dma channel ++ * @data: custom data ++ * @flags: DMA flags ++ */ ++static struct dma_async_tx_descriptor *bam_prep_dma_custom_mapping( ++ struct dma_chan *chan, ++ void *data, unsigned long flags) ++{ ++ struct bam_chan *bchan = to_bam_chan(chan); ++ struct bam_device *bdev = bchan->bdev; ++ struct bam_async_desc *async_desc; ++ struct qcom_bam_custom_data *desc_data = data; ++ u32 i; ++ struct bam_desc_hw *desc; ++ unsigned int num_alloc = 0; ++ ++ ++ if (!is_slave_direction(desc_data->dir)) { ++ dev_err(bdev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* calculate number of required entries */ ++ for (i = 0; i < desc_data->sgl_cnt; i++) ++ num_alloc += DIV_ROUND_UP( ++ sg_dma_len(&desc_data->bam_sgl[i].sgl), BAM_FIFO_SIZE); ++ ++ /* allocate enough room to accommodate the number of entries */ ++ async_desc = kzalloc(sizeof(*async_desc) + ++ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); ++ ++ if (!async_desc) ++ goto err_out; ++ ++ if (flags & DMA_PREP_FENCE) ++ async_desc->flags |= DESC_FLAG_NWD; ++ ++ if (flags & DMA_PREP_INTERRUPT) ++ async_desc->flags |= DESC_FLAG_EOT; ++ else ++ async_desc->flags |= DESC_FLAG_INT; ++ ++ async_desc->num_desc = num_alloc; ++ async_desc->curr_desc = async_desc->desc; ++ async_desc->dir = desc_data->dir; ++ ++ /* fill in temporary descriptors */ ++ desc = async_desc->desc; ++ for (i = 0; i < desc_data->sgl_cnt; i++) { ++ unsigned int remainder; ++ unsigned int curr_offset = 0; ++ ++ remainder = sg_dma_len(&desc_data->bam_sgl[i].sgl); ++ ++ do { ++ desc->addr = cpu_to_le32( ++ sg_dma_address(&desc_data->bam_sgl[i].sgl) + ++ curr_offset); ++ ++ if (desc_data->bam_sgl[i].dma_flags) ++ desc->flags |= cpu_to_le16( ++ desc_data->bam_sgl[i].dma_flags); ++ ++ if (remainder > BAM_FIFO_SIZE) { ++ desc->size = cpu_to_le16(BAM_FIFO_SIZE); ++ remainder -= BAM_FIFO_SIZE; ++ curr_offset += BAM_FIFO_SIZE; ++ } else { ++ desc->size = cpu_to_le16(remainder); ++ remainder = 0; ++ } ++ ++ async_desc->length += desc->size; ++ desc++; ++ } while (remainder > 0); ++ } ++ ++ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); ++ ++err_out: ++ kfree(async_desc); ++ return NULL; ++} ++ ++/** + * bam_dma_terminate_all - terminate all transactions on a channel + * @bchan: bam dma channel + * +@@ -960,7 +1043,7 @@ static void bam_start_dma(struct bam_chan *bchan) + + /* set any special flags on the last descriptor */ + if (async_desc->num_desc == async_desc->xfer_len) +- desc[async_desc->xfer_len - 1].flags = ++ desc[async_desc->xfer_len - 1].flags |= + cpu_to_le16(async_desc->flags); + else + desc[async_desc->xfer_len - 1].flags |= +@@ -1237,6 +1320,8 @@ static int bam_dma_probe(struct platform_device *pdev) + bdev->common.device_alloc_chan_resources = bam_alloc_chan; + bdev->common.device_free_chan_resources = bam_free_chan; + bdev->common.device_prep_slave_sg = bam_prep_slave_sg; ++ bdev->common.device_prep_dma_custom_mapping = ++ bam_prep_dma_custom_mapping; + bdev->common.device_config = bam_slave_config; + bdev->common.device_pause = bam_pause; + bdev->common.device_resume = bam_resume; +diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h +index 7e87a85..7113c77 100644 +--- a/include/linux/dma/qcom_bam_dma.h ++++ b/include/linux/dma/qcom_bam_dma.h +@@ -65,6 +65,19 @@ enum bam_command_type { + }; + + /* ++ * QCOM BAM DMA custom data ++ * ++ * @sgl_cnt: number of sgl in bam_sgl ++ * @dir: DMA data transfer direction ++ * @bam_sgl: BAM SGL pointer ++ */ ++struct qcom_bam_custom_data { ++ u32 sgl_cnt; ++ enum dma_transfer_direction dir; ++ struct qcom_bam_sgl *bam_sgl; ++}; ++ ++/* + * qcom_bam_sg_init_table - Init QCOM BAM SGL + * @bam_sgl: bam sgl + * @nents: number of entries in bam sgl +diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h +index cc535a4..627c125 100644 +--- a/include/linux/dmaengine.h ++++ b/include/linux/dmaengine.h +@@ -692,6 +692,8 @@ struct dma_filter { + * be called after period_len bytes have been transferred. + * @device_prep_interleaved_dma: Transfer expression in a generic way. + * @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address ++ * @device_prep_dma_custom_mapping: prepares a dma operation from dma driver ++ * specific custom data + * @device_config: Pushes a new configuration to a channel, return 0 or an error + * code + * @device_pause: Pauses any transfer happening on a channel. Returns +@@ -783,6 +785,9 @@ struct dma_device { + struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)( + struct dma_chan *chan, dma_addr_t dst, u64 data, + unsigned long flags); ++ struct dma_async_tx_descriptor *(*device_prep_dma_custom_mapping)( ++ struct dma_chan *chan, void *data, ++ unsigned long flags); + + int (*device_config)(struct dma_chan *chan, + struct dma_slave_config *config); +@@ -899,6 +904,15 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg( + src_sg, src_nents, flags); + } + ++static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_custom_mapping( ++ struct dma_chan *chan, ++ void *data, ++ unsigned long flags) ++{ ++ return chan->device->device_prep_dma_custom_mapping(chan, data, ++ flags); ++} ++ + /** + * dmaengine_terminate_all() - Terminate all active DMA transfers + * @chan: The channel for which to terminate the transfers +-- +2.7.2 diff --git a/target/linux/ipq806x/patches-4.9/862-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch b/target/linux/ipq806x/patches-4.9/862-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch new file mode 100644 index 0000000..032e2de --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/862-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch @@ -0,0 +1,83 @@ +From 02bbf3c46e1e38e9ca699143566903683e3a015d Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir <rjan...@codeaurora.org> +Date: Thu, 20 Apr 2017 10:45:00 +0530 +Subject: [PATCH] dts: ipq4019: add nand and qpic bam dma node + +This change adds QPIC BAM dma and NAND driver node's in +IPQ4019 device tree, also enable this for AP-DK04.1-C1 +board. + +Signed-off-by: Ram Chandra Jangir <rjan...@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 8 ++++++ + arch/arm/boot/dts/qcom-ipq4019.dtsi | 38 +++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi +index 09fb047..f37641e 100644 +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi +@@ -204,5 +204,13 @@ + wifi@a800000 { + status = "ok"; + }; ++ ++ qpic_bam: dma@7984000 { ++ status = "ok"; ++ }; ++ ++ nand: qpic-nand@79b0000 { ++ status = "ok"; ++ }; + }; + }; +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi +index 52a64e7..740808b 100644 +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi +@@ -593,5 +593,43 @@ + "legacy"; + status = "disabled"; + }; ++ ++ qpic_bam: dma@7984000 { ++ compatible = "qcom,bam-v1.7.0"; ++ reg = <0x7984000 0x1a000>; ++ interrupts = <0 101 0>; ++ clocks = <&gcc GCC_QPIC_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <0>; ++ status = "disabled"; ++ }; ++ ++ nand: qpic-nand@79b0000 { ++ compatible = "qcom,ebi2-nandc-bam", "qcom,msm-nand"; ++ reg = <0x79b0000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ clocks = <&gcc GCC_QPIC_CLK>, ++ <&gcc GCC_QPIC_AHB_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&qpic_bam 0>, ++ <&qpic_bam 1>, ++ <&qpic_bam 2>; ++ dma-names = "tx", "rx", "cmd"; ++ status = "disabled"; ++ ++ nandcs@0 { ++ compatible = "qcom,nandcs"; ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ nand-ecc-strength = <4>; ++ nand-ecc-step-size = <512>; ++ nand-bus-width = <8>; ++ }; ++ }; + }; + }; +-- +2.7.2 -- 2.7.2 _______________________________________________ Lede-dev mailing list Lede-dev@lists.infradead.org http://lists.infradead.org/mailman/listinfo/lede-dev