ipq806x: backport mtd adm and smem driver
authorAnsuel Smith <ansuelsmth@gmail.com>
Mon, 1 Mar 2021 00:06:22 +0000 (01:06 +0100)
committerPetr Štetiar <ynezz@true.cz>
Fri, 7 May 2021 05:05:16 +0000 (07:05 +0200)
- Backport mtd adm driver from kernel 5.12
- Backport mtd parser smem from kernel 5.11
- Fix mtd rootfs patch
- Update qcom,smem compatible to qcom,smem-parts

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
18 files changed:
target/linux/ipq806x/config-5.4
target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap161.dts
target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-g10.dts
target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts
target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8068-ecw5410.dts
target/linux/ipq806x/patches-5.10/0002-dmaengine-Add-ADM-driver.patch [deleted file]
target/linux/ipq806x/patches-5.10/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch [deleted file]
target/linux/ipq806x/patches-5.10/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch [deleted file]
target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch [deleted file]
target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch [deleted file]
target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch [deleted file]
target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch [new file with mode: 0644]

index 78a9ad7077c3b2dff84a0d3b1dac91d88c8a0bc7..c582b7a52be264df4218de812025cfbbe0e39bbb 100644 (file)
@@ -313,7 +313,7 @@ CONFIG_MTD_CMDLINE_PARTS=y
 CONFIG_MTD_NAND_CORE=y
 CONFIG_MTD_NAND_ECC_SW_HAMMING=y
 CONFIG_MTD_NAND_QCOM=y
-CONFIG_MTD_QCOM_SMEM_PARTS=y
+CONFIG_MTD_QCOMSMEM_PARTS=y
 CONFIG_MTD_RAW_NAND=y
 CONFIG_MTD_SPI_NOR=y
 CONFIG_MTD_SPLIT_FIRMWARE=y
index b2fdc427e28869ae93491b6b0328e591c6bd502f..29476e3877135b31105e0b6c53dd75849c0cebdb 100644 (file)
@@ -30,7 +30,7 @@
 
 &flash {
        partitions {
-               compatible = "qcom,smem";
+               compatible = "qcom,smem-part";
        };
 };
 
@@ -66,7 +66,7 @@
                nand-ecc-step-size = <512>;
 
                partitions {
-                       compatible = "qcom,smem";
+                       compatible = "qcom,smem-part";
                };
        };
 };
index 6ba3d70808f7b79a2a15f0b4d6cca4bb3a317da8..914c370552fc58395317b0e17005bfe226332e3c 100644 (file)
@@ -38,7 +38,7 @@
 
 &flash {
        partitions {
-               compatible = "qcom,smem";
+               compatible = "qcom,smem-part";
        };
 };
 
@@ -78,7 +78,7 @@
                nand-ecc-step-size = <512>;
 
                partitions {
-                       compatible = "qcom,smem";
+                       compatible = "qcom,smem-part";
                };
        };
 };
index 15ed5b4773e9b5267d0a3d63e82f8ed0cb4d804d..ccefc2301142bee632e55786869cc4cfaf6eb999 100644 (file)
                nand-ecc-step-size = <512>;
 
                partitions {
-                       compatible = "qcom,smem";
+                       compatible = "qcom,smem-part";
                };
        };
 };
index 119598d074d69b2015435de0cc498d71add59b72..34b9c5fc873217f59a98afc42b00f73fe429e4ac 100644 (file)
                        reg = <0>;
 
                        partitions {
-                               compatible = "qcom,smem";
+                               compatible = "qcom,smem-part";
                        };
                };
        };
index 712910c2610f6d69b99a19e6dce40d05acaf6ed1..9b14bb2c29f092e574b9365f278afccc667a33e1 100644 (file)
                        reg = <0>;
 
                        partitions {
-                               compatible = "qcom,smem";
+                               compatible = "qcom,smem-part";
                        };
                };
        };
diff --git a/target/linux/ipq806x/patches-5.10/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-5.10/0002-dmaengine-Add-ADM-driver.patch
deleted file mode 100644 (file)
index aa7d2e7..0000000
+++ /dev/null
@@ -1,966 +0,0 @@
-From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001
-From: Thomas Pedersen <twp@codeaurora.org>
-Date: Mon, 16 May 2016 17:58:51 -0700
-Subject: [PATCH 02/69] dmaengine: Add ADM driver
-
-Original patch by Andy Gross.
-
-Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
-controller found in the MSM8x60 and IPQ/APQ8064 platforms.
-
-The ADM supports both memory to memory transactions and memory
-to/from peripheral device transactions.  The controller also provides flow
-control capabilities for transactions to/from peripheral devices.
-
-The initial release of this driver supports slave transfers to/from peripherals
-and also incorporates CRCI (client rate control interface) flow control.
-
-Signed-off-by: Andy Gross <agross@codeaurora.org>
-Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
----
- drivers/dma/qcom/Kconfig    |  10 +
- drivers/dma/qcom/Makefile   |   1 +
- drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 911 insertions(+)
- create mode 100644 drivers/dma/qcom/qcom_adm.c
-
---- a/drivers/dma/qcom/Kconfig
-+++ b/drivers/dma/qcom/Kconfig
-@@ -28,3 +28,13 @@ config QCOM_HIDMA
-         (user to kernel, kernel to kernel, etc.).  It only supports
-         memcpy interface. The core is not intended for general
-         purpose slave DMA.
-+
-+config QCOM_ADM
-+      tristate "Qualcomm ADM support"
-+      depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
-+      select DMA_ENGINE
-+      select DMA_VIRTUAL_CHANNELS
-+      ---help---
-+        Enable support for the Qualcomm ADM DMA controller.  This controller
-+        provides DMA capabilities for both general purpose and on-chip
-+        peripheral devices.
---- a/drivers/dma/qcom/Makefile
-+++ b/drivers/dma/qcom/Makefile
-@@ -4,3 +4,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg
- hdma_mgmt-objs         := hidma_mgmt.o hidma_mgmt_sys.o
- obj-$(CONFIG_QCOM_HIDMA) +=  hdma.o
- hdma-objs        := hidma_ll.o hidma.o hidma_dbg.o
-+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
---- /dev/null
-+++ b/drivers/dma/qcom/qcom_adm.c
-@@ -0,0 +1,914 @@
-+/*
-+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 and
-+ * only version 2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/io.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/module.h>
-+#include <linux/interrupt.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/scatterlist.h>
-+#include <linux/device.h>
-+#include <linux/platform_device.h>
-+#include <linux/of.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_dma.h>
-+#include <linux/reset.h>
-+#include <linux/clk.h>
-+#include <linux/dmaengine.h>
-+
-+#include "../dmaengine.h"
-+#include "../virt-dma.h"
-+
-+/* ADM registers - calculated from channel number and security domain */
-+#define ADM_CHAN_MULTI                        0x4
-+#define ADM_CI_MULTI                  0x4
-+#define ADM_CRCI_MULTI                        0x4
-+#define ADM_EE_MULTI                  0x800
-+#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
-+#define ADM_EE_OFFS(ee)                       (ADM_EE_MULTI * ee)
-+#define ADM_CHAN_EE_OFFS(chan, ee)    (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
-+#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
-+#define ADM_CI_OFFS(ci)                       (ADM_CHAN_OFF(ci))
-+#define ADM_CH_CMD_PTR(chan, ee)      (ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_RSLT(chan, ee)         (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_STATUS_SD(chan, ee)    (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_CONF(chan)             (0x240 + ADM_CHAN_OFFS(chan))
-+#define ADM_CH_RSLT_CONF(chan, ee)    (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
-+#define ADM_CI_CONF(ci)                       (0x390 + ci * ADM_CI_MULTI)
-+#define ADM_GP_CTL                    0x3d8
-+#define ADM_CRCI_CTL(crci, ee)                (0x400 + crci * ADM_CRCI_MULTI + \
-+                                              ADM_EE_OFFS(ee))
-+
-+/* channel status */
-+#define ADM_CH_STATUS_VALID   BIT(1)
-+
-+/* channel result */
-+#define ADM_CH_RSLT_VALID     BIT(31)
-+#define ADM_CH_RSLT_ERR               BIT(3)
-+#define ADM_CH_RSLT_FLUSH     BIT(2)
-+#define ADM_CH_RSLT_TPD               BIT(1)
-+
-+/* channel conf */
-+#define ADM_CH_CONF_SHADOW_EN         BIT(12)
-+#define ADM_CH_CONF_MPU_DISABLE               BIT(11)
-+#define ADM_CH_CONF_PERM_MPU_CONF     BIT(9)
-+#define ADM_CH_CONF_FORCE_RSLT_EN     BIT(7)
-+#define ADM_CH_CONF_SEC_DOMAIN(ee)    (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
-+
-+/* channel result conf */
-+#define ADM_CH_RSLT_CONF_FLUSH_EN     BIT(1)
-+#define ADM_CH_RSLT_CONF_IRQ_EN               BIT(0)
-+
-+/* CRCI CTL */
-+#define ADM_CRCI_CTL_MUX_SEL  BIT(18)
-+#define ADM_CRCI_CTL_RST      BIT(17)
-+
-+/* CI configuration */
-+#define ADM_CI_RANGE_END(x)   (x << 24)
-+#define ADM_CI_RANGE_START(x) (x << 16)
-+#define ADM_CI_BURST_4_WORDS  BIT(2)
-+#define ADM_CI_BURST_8_WORDS  BIT(3)
-+
-+/* GP CTL */
-+#define ADM_GP_CTL_LP_EN      BIT(12)
-+#define ADM_GP_CTL_LP_CNT(x)  (x << 8)
-+
-+/* Command pointer list entry */
-+#define ADM_CPLE_LP           BIT(31)
-+#define ADM_CPLE_CMD_PTR_LIST BIT(29)
-+
-+/* Command list entry */
-+#define ADM_CMD_LC            BIT(31)
-+#define ADM_CMD_DST_CRCI(n)   (((n) & 0xf) << 7)
-+#define ADM_CMD_SRC_CRCI(n)   (((n) & 0xf) << 3)
-+
-+#define ADM_CMD_TYPE_SINGLE   0x0
-+#define ADM_CMD_TYPE_BOX      0x3
-+
-+#define ADM_CRCI_MUX_SEL      BIT(4)
-+#define ADM_DESC_ALIGN                8
-+#define ADM_MAX_XFER          (SZ_64K-1)
-+#define ADM_MAX_ROWS          (SZ_64K-1)
-+#define ADM_MAX_CHANNELS      16
-+
-+struct adm_desc_hw_box {
-+      u32 cmd;
-+      u32 src_addr;
-+      u32 dst_addr;
-+      u32 row_len;
-+      u32 num_rows;
-+      u32 row_offset;
-+};
-+
-+struct adm_desc_hw_single {
-+      u32 cmd;
-+      u32 src_addr;
-+      u32 dst_addr;
-+      u32 len;
-+};
-+
-+struct adm_async_desc {
-+      struct virt_dma_desc vd;
-+      struct adm_device *adev;
-+
-+      size_t length;
-+      enum dma_transfer_direction dir;
-+      dma_addr_t dma_addr;
-+      size_t dma_len;
-+
-+      void *cpl;
-+      dma_addr_t cp_addr;
-+      u32 crci;
-+      u32 mux;
-+      u32 blk_size;
-+};
-+
-+struct adm_chan {
-+      struct virt_dma_chan vc;
-+      struct adm_device *adev;
-+
-+      /* parsed from DT */
-+      u32 id;                 /* channel id */
-+
-+      struct adm_async_desc *curr_txd;
-+      struct dma_slave_config slave;
-+      struct list_head node;
-+
-+      int error;
-+      int initialized;
-+};
-+
-+static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
-+{
-+      return container_of(common, struct adm_chan, vc.chan);
-+}
-+
-+struct adm_device {
-+      void __iomem *regs;
-+      struct device *dev;
-+      struct dma_device common;
-+      struct device_dma_parameters dma_parms;
-+      struct adm_chan *channels;
-+
-+      u32 ee;
-+
-+      struct clk *core_clk;
-+      struct clk *iface_clk;
-+
-+      struct reset_control *clk_reset;
-+      struct reset_control *c0_reset;
-+      struct reset_control *c1_reset;
-+      struct reset_control *c2_reset;
-+      int irq;
-+};
-+
-+/**
-+ * adm_free_chan - Frees dma resources associated with the specific channel
-+ *
-+ * Free all allocated descriptors associated with this channel
-+ *
-+ */
-+static void adm_free_chan(struct dma_chan *chan)
-+{
-+      /* free all queued descriptors */
-+      vchan_free_chan_resources(to_virt_chan(chan));
-+}
-+
-+/**
-+ * adm_get_blksize - Get block size from burst value
-+ *
-+ */
-+static int adm_get_blksize(unsigned int burst)
-+{
-+      int ret;
-+
-+      switch (burst) {
-+      case 16:
-+      case 32:
-+      case 64:
-+      case 128:
-+              ret = ffs(burst>>4) - 1;
-+              break;
-+      case 192:
-+              ret = 4;
-+              break;
-+      case 256:
-+              ret = 5;
-+              break;
-+      default:
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+/**
-+ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
-+ *
-+ * @achan: ADM channel
-+ * @desc: Descriptor memory pointer
-+ * @sg: Scatterlist entry
-+ * @crci: CRCI value
-+ * @burst: Burst size of transaction
-+ * @direction: DMA transfer direction
-+ */
-+static void *adm_process_fc_descriptors(struct adm_chan *achan,
-+      void *desc, struct scatterlist *sg, u32 crci, u32 burst,
-+      enum dma_transfer_direction direction)
-+{
-+      struct adm_desc_hw_box *box_desc = NULL;
-+      struct adm_desc_hw_single *single_desc;
-+      u32 remainder = sg_dma_len(sg);
-+      u32 rows, row_offset, crci_cmd;
-+      u32 mem_addr = sg_dma_address(sg);
-+      u32 *incr_addr = &mem_addr;
-+      u32 *src, *dst;
-+
-+      if (direction == DMA_DEV_TO_MEM) {
-+              crci_cmd = ADM_CMD_SRC_CRCI(crci);
-+              row_offset = burst;
-+              src = &achan->slave.src_addr;
-+              dst = &mem_addr;
-+      } else {
-+              crci_cmd = ADM_CMD_DST_CRCI(crci);
-+              row_offset = burst << 16;
-+              src = &mem_addr;
-+              dst = &achan->slave.dst_addr;
-+      }
-+
-+      while (remainder >= burst) {
-+              box_desc = desc;
-+              box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
-+              box_desc->row_offset = row_offset;
-+              box_desc->src_addr = *src;
-+              box_desc->dst_addr = *dst;
-+
-+              rows = remainder / burst;
-+              rows = min_t(u32, rows, ADM_MAX_ROWS);
-+              box_desc->num_rows = rows << 16 | rows;
-+              box_desc->row_len = burst << 16 | burst;
-+
-+              *incr_addr += burst * rows;
-+              remainder -= burst * rows;
-+              desc += sizeof(*box_desc);
-+      }
-+
-+      /* if leftover bytes, do one single descriptor */
-+      if (remainder) {
-+              single_desc = desc;
-+              single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
-+              single_desc->len = remainder;
-+              single_desc->src_addr = *src;
-+              single_desc->dst_addr = *dst;
-+              desc += sizeof(*single_desc);
-+
-+              if (sg_is_last(sg))
-+                      single_desc->cmd |= ADM_CMD_LC;
-+      } else {
-+              if (box_desc && sg_is_last(sg))
-+                      box_desc->cmd |= ADM_CMD_LC;
-+      }
-+
-+      return desc;
-+}
-+
-+/**
-+ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
-+ *
-+ * @achan: ADM channel
-+ * @desc: Descriptor memory pointer
-+ * @sg: Scatterlist entry
-+ * @direction: DMA transfer direction
-+ */
-+static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
-+      void *desc, struct scatterlist *sg,
-+      enum dma_transfer_direction direction)
-+{
-+      struct adm_desc_hw_single *single_desc;
-+      u32 remainder = sg_dma_len(sg);
-+      u32 mem_addr = sg_dma_address(sg);
-+      u32 *incr_addr = &mem_addr;
-+      u32 *src, *dst;
-+
-+      if (direction == DMA_DEV_TO_MEM) {
-+              src = &achan->slave.src_addr;
-+              dst = &mem_addr;
-+      } else {
-+              src = &mem_addr;
-+              dst = &achan->slave.dst_addr;
-+      }
-+
-+      do {
-+              single_desc = desc;
-+              single_desc->cmd = ADM_CMD_TYPE_SINGLE;
-+              single_desc->src_addr = *src;
-+              single_desc->dst_addr = *dst;
-+              single_desc->len = (remainder > ADM_MAX_XFER) ?
-+                              ADM_MAX_XFER : remainder;
-+
-+              remainder -= single_desc->len;
-+              *incr_addr += single_desc->len;
-+              desc += sizeof(*single_desc);
-+      } while (remainder);
-+
-+      /* set last command if this is the end of the whole transaction */
-+      if (sg_is_last(sg))
-+              single_desc->cmd |= ADM_CMD_LC;
-+
-+      return desc;
-+}
-+
-+/**
-+ * adm_prep_slave_sg - Prep slave sg transaction
-+ *
-+ * @chan: dma channel
-+ * @sgl: scatter gather list
-+ * @sg_len: length of sg
-+ * @direction: DMA transfer direction
-+ * @flags: DMA flags
-+ * @context: transfer context (unused)
-+ */
-+static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
-+      struct scatterlist *sgl, unsigned int sg_len,
-+      enum dma_transfer_direction direction, unsigned long flags,
-+      void *context)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct adm_device *adev = achan->adev;
-+      struct adm_async_desc *async_desc;
-+      struct scatterlist *sg;
-+      dma_addr_t cple_addr;
-+      u32 i, burst;
-+      u32 single_count = 0, box_count = 0, crci = 0;
-+      void *desc;
-+      u32 *cple;
-+      int blk_size = 0;
-+
-+      if (!is_slave_direction(direction)) {
-+              dev_err(adev->dev, "invalid dma direction\n");
-+              return NULL;
-+      }
-+
-+      /*
-+       * get burst value from slave configuration
-+       */
-+      burst = (direction == DMA_MEM_TO_DEV) ?
-+              achan->slave.dst_maxburst :
-+              achan->slave.src_maxburst;
-+
-+      /* if using flow control, validate burst and crci values */
-+      if (achan->slave.device_fc) {
-+
-+              blk_size = adm_get_blksize(burst);
-+              if (blk_size < 0) {
-+                      dev_err(adev->dev, "invalid burst value: %d\n",
-+                              burst);
-+                      return ERR_PTR(-EINVAL);
-+              }
-+
-+              crci = achan->slave.slave_id & 0xf;
-+              if (!crci || achan->slave.slave_id > 0x1f) {
-+                      dev_err(adev->dev, "invalid crci value\n");
-+                      return ERR_PTR(-EINVAL);
-+              }
-+      }
-+
-+      /* iterate through sgs and compute allocation size of structures */
-+      for_each_sg(sgl, sg, sg_len, i) {
-+              if (achan->slave.device_fc) {
-+                      box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
-+                                                ADM_MAX_ROWS);
-+                      if (sg_dma_len(sg) % burst)
-+                              single_count++;
-+              } else {
-+                      single_count += DIV_ROUND_UP(sg_dma_len(sg),
-+                                                   ADM_MAX_XFER);
-+              }
-+      }
-+
-+      async_desc = kzalloc(sizeof(*async_desc), GFP_ATOMIC);
-+      if (!async_desc)
-+              return ERR_PTR(-ENOMEM);
-+
-+      if (crci)
-+              async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
-+                                      ADM_CRCI_CTL_MUX_SEL : 0;
-+      async_desc->crci = crci;
-+      async_desc->blk_size = blk_size;
-+      async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
-+                              box_count * sizeof(struct adm_desc_hw_box) +
-+                              sizeof(*cple) + 2 * ADM_DESC_ALIGN;
-+
-+      async_desc->cpl = kzalloc(async_desc->dma_len, GFP_ATOMIC);
-+      if (!async_desc->cpl)
-+              goto free;
-+
-+      async_desc->adev = adev;
-+
-+      /* both command list entry and descriptors must be 8 byte aligned */
-+      cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
-+      desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
-+
-+      for_each_sg(sgl, sg, sg_len, i) {
-+              async_desc->length += sg_dma_len(sg);
-+
-+              if (achan->slave.device_fc)
-+                      desc = adm_process_fc_descriptors(achan, desc, sg, crci,
-+                                                      burst, direction);
-+              else
-+                      desc = adm_process_non_fc_descriptors(achan, desc, sg,
-+                                                         direction);
-+      }
-+
-+      async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl,
-+                                            async_desc->dma_len,
-+                                            DMA_TO_DEVICE);
-+      if (dma_mapping_error(adev->dev, async_desc->dma_addr))
-+              goto free;
-+
-+      cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl);
-+
-+      /* init cmd list */
-+      dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple),
-+                              DMA_TO_DEVICE);
-+      *cple = ADM_CPLE_LP;
-+      *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3;
-+      dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple),
-+                                 DMA_TO_DEVICE);
-+
-+      return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
-+
-+free:
-+      kfree(async_desc);
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+/**
-+ * adm_terminate_all - terminate all transactions on a channel
-+ * @achan: adm dma channel
-+ *
-+ * Dequeues and frees all transactions, aborts current transaction
-+ * No callbacks are done
-+ *
-+ */
-+static int adm_terminate_all(struct dma_chan *chan)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct adm_device *adev = achan->adev;
-+      unsigned long flags;
-+      LIST_HEAD(head);
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+      vchan_get_all_descriptors(&achan->vc, &head);
-+
-+      /* send flush command to terminate current transaction */
-+      writel_relaxed(0x0,
-+              adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
-+
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+
-+      vchan_dma_desc_free_list(&achan->vc, &head);
-+
-+      return 0;
-+}
-+
-+static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      unsigned long flag;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flag);
-+      memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
-+      spin_unlock_irqrestore(&achan->vc.lock, flag);
-+
-+      return 0;
-+}
-+
-+/**
-+ * adm_start_dma - start next transaction
-+ * @achan - ADM dma channel
-+ */
-+static void adm_start_dma(struct adm_chan *achan)
-+{
-+      struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
-+      struct adm_device *adev = achan->adev;
-+      struct adm_async_desc *async_desc;
-+
-+      lockdep_assert_held(&achan->vc.lock);
-+
-+      if (!vd)
-+              return;
-+
-+      list_del(&vd->node);
-+
-+      /* write next command list out to the CMD FIFO */
-+      async_desc = container_of(vd, struct adm_async_desc, vd);
-+      achan->curr_txd = async_desc;
-+
-+      /* reset channel error */
-+      achan->error = 0;
-+
-+      if (!achan->initialized) {
-+              /* enable interrupts */
-+              writel(ADM_CH_CONF_SHADOW_EN |
-+                     ADM_CH_CONF_PERM_MPU_CONF |
-+                     ADM_CH_CONF_MPU_DISABLE |
-+                     ADM_CH_CONF_SEC_DOMAIN(adev->ee),
-+                     adev->regs + ADM_CH_CONF(achan->id));
-+
-+              writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
-+                      adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
-+
-+              achan->initialized = 1;
-+      }
-+
-+      /* set the crci block size if this transaction requires CRCI */
-+      if (async_desc->crci) {
-+              writel(async_desc->mux | async_desc->blk_size,
-+                      adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
-+      }
-+
-+      /* make sure IRQ enable doesn't get reordered */
-+      wmb();
-+
-+      /* write next command list out to the CMD FIFO */
-+      writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
-+              adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
-+}
-+
-+/**
-+ * adm_dma_irq - irq handler for ADM controller
-+ * @irq: IRQ of interrupt
-+ * @data: callback data
-+ *
-+ * IRQ handler for the bam controller
-+ */
-+static irqreturn_t adm_dma_irq(int irq, void *data)
-+{
-+      struct adm_device *adev = data;
-+      u32 srcs, i;
-+      struct adm_async_desc *async_desc;
-+      unsigned long flags;
-+
-+      srcs = readl_relaxed(adev->regs +
-+                      ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
-+              struct adm_chan *achan = &adev->channels[i];
-+              u32 status, result;
-+
-+              if (srcs & BIT(i)) {
-+                      status = readl_relaxed(adev->regs +
-+                              ADM_CH_STATUS_SD(i, adev->ee));
-+
-+                      /* if no result present, skip */
-+                      if (!(status & ADM_CH_STATUS_VALID))
-+                              continue;
-+
-+                      result = readl_relaxed(adev->regs +
-+                              ADM_CH_RSLT(i, adev->ee));
-+
-+                      /* no valid results, skip */
-+                      if (!(result & ADM_CH_RSLT_VALID))
-+                              continue;
-+
-+                      /* flag error if transaction was flushed or failed */
-+                      if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
-+                              achan->error = 1;
-+
-+                      spin_lock_irqsave(&achan->vc.lock, flags);
-+                      async_desc = achan->curr_txd;
-+
-+                      achan->curr_txd = NULL;
-+
-+                      if (async_desc) {
-+                              vchan_cookie_complete(&async_desc->vd);
-+
-+                              /* kick off next DMA */
-+                              adm_start_dma(achan);
-+                      }
-+
-+                      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+              }
-+      }
-+
-+      return IRQ_HANDLED;
-+}
-+
-+/**
-+ * adm_tx_status - returns status of transaction
-+ * @chan: dma channel
-+ * @cookie: transaction cookie
-+ * @txstate: DMA transaction state
-+ *
-+ * Return status of dma transaction
-+ */
-+static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
-+      struct dma_tx_state *txstate)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct virt_dma_desc *vd;
-+      enum dma_status ret;
-+      unsigned long flags;
-+      size_t residue = 0;
-+
-+      ret = dma_cookie_status(chan, cookie, txstate);
-+      if (ret == DMA_COMPLETE || !txstate)
-+              return ret;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+
-+      vd = vchan_find_desc(&achan->vc, cookie);
-+      if (vd)
-+              residue = container_of(vd, struct adm_async_desc, vd)->length;
-+
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+
-+      /*
-+       * residue is either the full length if it is in the issued list, or 0
-+       * if it is in progress.  We have no reliable way of determining
-+       * anything inbetween
-+      */
-+      dma_set_residue(txstate, residue);
-+
-+      if (achan->error)
-+              return DMA_ERROR;
-+
-+      return ret;
-+}
-+
-+/**
-+ * adm_issue_pending - starts pending transactions
-+ * @chan: dma channel
-+ *
-+ * Issues all pending transactions and starts DMA
-+ */
-+static void adm_issue_pending(struct dma_chan *chan)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      unsigned long flags;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+
-+      if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
-+              adm_start_dma(achan);
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+}
-+
-+/**
-+ * adm_dma_free_desc - free descriptor memory
-+ * @vd: virtual descriptor
-+ *
-+ */
-+static void adm_dma_free_desc(struct virt_dma_desc *vd)
-+{
-+      struct adm_async_desc *async_desc = container_of(vd,
-+                      struct adm_async_desc, vd);
-+
-+      dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr,
-+                       async_desc->dma_len, DMA_TO_DEVICE);
-+      kfree(async_desc->cpl);
-+      kfree(async_desc);
-+}
-+
-+static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
-+      u32 index)
-+{
-+      achan->id = index;
-+      achan->adev = adev;
-+
-+      vchan_init(&achan->vc, &adev->common);
-+      achan->vc.desc_free = adm_dma_free_desc;
-+}
-+
-+static int adm_dma_probe(struct platform_device *pdev)
-+{
-+      struct adm_device *adev;
-+      struct resource *iores;
-+      int ret;
-+      u32 i;
-+
-+      adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
-+      if (!adev)
-+              return -ENOMEM;
-+
-+      adev->dev = &pdev->dev;
-+
-+      iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      adev->regs = devm_ioremap_resource(&pdev->dev, iores);
-+      if (IS_ERR(adev->regs))
-+              return PTR_ERR(adev->regs);
-+
-+      adev->irq = platform_get_irq(pdev, 0);
-+      if (adev->irq < 0)
-+              return adev->irq;
-+
-+      ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
-+      if (ret) {
-+              dev_err(adev->dev, "Execution environment unspecified\n");
-+              return ret;
-+      }
-+
-+      adev->core_clk = devm_clk_get(adev->dev, "core");
-+      if (IS_ERR(adev->core_clk))
-+              return PTR_ERR(adev->core_clk);
-+
-+      ret = clk_prepare_enable(adev->core_clk);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to prepare/enable core clock\n");
-+              return ret;
-+      }
-+
-+      adev->iface_clk = devm_clk_get(adev->dev, "iface");
-+      if (IS_ERR(adev->iface_clk)) {
-+              ret = PTR_ERR(adev->iface_clk);
-+              goto err_disable_core_clk;
-+      }
-+
-+      ret = clk_prepare_enable(adev->iface_clk);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to prepare/enable iface clock\n");
-+              goto err_disable_core_clk;
-+      }
-+
-+      adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
-+      if (IS_ERR(adev->clk_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 reset\n");
-+              ret = PTR_ERR(adev->clk_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
-+      if (IS_ERR(adev->c0_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
-+              ret = PTR_ERR(adev->c0_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
-+      if (IS_ERR(adev->c1_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
-+              ret = PTR_ERR(adev->c1_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
-+      if (IS_ERR(adev->c2_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
-+              ret = PTR_ERR(adev->c2_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      reset_control_assert(adev->clk_reset);
-+      reset_control_assert(adev->c0_reset);
-+      reset_control_assert(adev->c1_reset);
-+      reset_control_assert(adev->c2_reset);
-+
-+      reset_control_deassert(adev->clk_reset);
-+      reset_control_deassert(adev->c0_reset);
-+      reset_control_deassert(adev->c1_reset);
-+      reset_control_deassert(adev->c2_reset);
-+
-+      adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
-+                              sizeof(*adev->channels), GFP_KERNEL);
-+
-+      if (!adev->channels) {
-+              ret = -ENOMEM;
-+              goto err_disable_clks;
-+      }
-+
-+      /* allocate and initialize channels */
-+      INIT_LIST_HEAD(&adev->common.channels);
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++)
-+              adm_channel_init(adev, &adev->channels[i], i);
-+
-+      /* reset CRCIs */
-+      for (i = 0; i < 16; i++)
-+              writel(ADM_CRCI_CTL_RST, adev->regs +
-+                      ADM_CRCI_CTL(i, adev->ee));
-+
-+      /* configure client interfaces */
-+      writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
-+      writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
-+      writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
-+      writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
-+              adev->regs + ADM_GP_CTL);
-+
-+      ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
-+                      0, "adm_dma", adev);
-+      if (ret)
-+              goto err_disable_clks;
-+
-+      platform_set_drvdata(pdev, adev);
-+
-+      adev->common.dev = adev->dev;
-+      adev->common.dev->dma_parms = &adev->dma_parms;
-+
-+      /* set capabilities */
-+      dma_cap_zero(adev->common.cap_mask);
-+      dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
-+      dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
-+
-+      /* initialize dmaengine apis */
-+      adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
-+      adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
-+      adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
-+      adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
-+      adev->common.device_free_chan_resources = adm_free_chan;
-+      adev->common.device_prep_slave_sg = adm_prep_slave_sg;
-+      adev->common.device_issue_pending = adm_issue_pending;
-+      adev->common.device_tx_status = adm_tx_status;
-+      adev->common.device_terminate_all = adm_terminate_all;
-+      adev->common.device_config = adm_slave_config;
-+
-+      ret = dma_async_device_register(&adev->common);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to register dma async device\n");
-+              goto err_disable_clks;
-+      }
-+
-+      ret = of_dma_controller_register(pdev->dev.of_node,
-+                                       of_dma_xlate_by_chan_id,
-+                                       &adev->common);
-+      if (ret)
-+              goto err_unregister_dma;
-+
-+      return 0;
-+
-+err_unregister_dma:
-+      dma_async_device_unregister(&adev->common);
-+err_disable_clks:
-+      clk_disable_unprepare(adev->iface_clk);
-+err_disable_core_clk:
-+      clk_disable_unprepare(adev->core_clk);
-+
-+      return ret;
-+}
-+
-+static int adm_dma_remove(struct platform_device *pdev)
-+{
-+      struct adm_device *adev = platform_get_drvdata(pdev);
-+      struct adm_chan *achan;
-+      u32 i;
-+
-+      of_dma_controller_free(pdev->dev.of_node);
-+      dma_async_device_unregister(&adev->common);
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
-+              achan = &adev->channels[i];
-+
-+              /* mask IRQs for this channel/EE pair */
-+              writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
-+
-+              adm_terminate_all(&adev->channels[i].vc.chan);
-+      }
-+
-+      devm_free_irq(adev->dev, adev->irq, adev);
-+
-+      clk_disable_unprepare(adev->core_clk);
-+      clk_disable_unprepare(adev->iface_clk);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id adm_of_match[] = {
-+      { .compatible = "qcom,adm", },
-+      {}
-+};
-+MODULE_DEVICE_TABLE(of, adm_of_match);
-+
-+static struct platform_driver adm_dma_driver = {
-+      .probe = adm_dma_probe,
-+      .remove = adm_dma_remove,
-+      .driver = {
-+              .name = "adm-dma-engine",
-+              .of_match_table = adm_of_match,
-+      },
-+};
-+
-+module_platform_driver(adm_dma_driver);
-+
-+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
-+MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
-+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-5.10/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-5.10/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch
deleted file mode 100644 (file)
index 6aa404c..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001
-From: Mathieu Olivari <mathieu@codeaurora.org>
-Date: Thu, 9 Mar 2017 08:18:08 +0100
-Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms
-
-On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
-used to store partition layout. This new parser can now be used to read
-SMEM and use it to register an MTD layout according to its content.
-
-Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
-Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
----
- drivers/mtd/parsers/Kconfig          |   7 ++
- drivers/mtd/parsers/Makefile         |   1 +
- drivers/mtd/parsers/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 236 insertions(+)
- create mode 100644 drivers/mtd/parsers/qcom_smem_part.c
-
---- a/drivers/mtd/parsers/Kconfig
-+++ b/drivers/mtd/parsers/Kconfig
-@@ -119,6 +119,13 @@ config MTD_PARSER_TRX
-         This driver will parse TRX header and report at least two partitions:
-         kernel and rootfs.
-+config MTD_QCOM_SMEM_PARTS
-+      tristate "QCOM SMEM partitioning support"
-+      depends on QCOM_SMEM
-+      help
-+        This provides partitions parser for QCOM devices using SMEM
-+        such as IPQ806x.
-+
- config MTD_SHARPSL_PARTS
-       tristate "Sharp SL Series NAND flash partition parser"
-       depends on MTD_NAND_SHARPSL || MTD_NAND_TMIO || COMPILE_TEST
---- a/drivers/mtd/parsers/Makefile
-+++ b/drivers/mtd/parsers/Makefile
-@@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_OF_PARTS)             += ofpart.o
- obj-$(CONFIG_MTD_PARSER_IMAGETAG)     += parser_imagetag.o
- obj-$(CONFIG_MTD_AFS_PARTS)           += afs.o
- obj-$(CONFIG_MTD_PARSER_TRX)          += parser_trx.o
-+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS)             += qcom_smem_part.o
- obj-$(CONFIG_MTD_SHARPSL_PARTS)               += sharpslpart.o
- obj-$(CONFIG_MTD_REDBOOT_PARTS)               += redboot.o
- obj-$(CONFIG_MTD_ROUTERBOOT_PARTS)            += routerbootpart.o
---- /dev/null
-+++ b/drivers/mtd/parsers/qcom_smem_part.c
-@@ -0,0 +1,235 @@
-+/*
-+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 and
-+ * only version 2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/device.h>
-+#include <linux/slab.h>
-+
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/spi/spi.h>
-+#include <linux/module.h>
-+
-+#include <linux/soc/qcom/smem.h>
-+
-+/* Processor/host identifier for the application processor */
-+#define SMEM_HOST_APPS                        0
-+
-+/* SMEM items index */
-+#define SMEM_AARM_PARTITION_TABLE     9
-+#define SMEM_BOOT_FLASH_TYPE          421
-+#define SMEM_BOOT_FLASH_BLOCK_SIZE    424
-+
-+/* SMEM Flash types */
-+#define SMEM_FLASH_NAND                       2
-+#define SMEM_FLASH_SPI                        6
-+
-+#define SMEM_PART_NAME_SZ             16
-+#define SMEM_PARTS_MAX                        32
-+
-+struct smem_partition {
-+      char name[SMEM_PART_NAME_SZ];
-+      __le32 start;
-+      __le32 size;
-+      __le32 attr;
-+};
-+
-+struct smem_partition_table {
-+      u8 magic[8];
-+      __le32 version;
-+      __le32 len;
-+      struct smem_partition parts[SMEM_PARTS_MAX];
-+};
-+
-+/* SMEM Magic values in partition table */
-+static const u8 SMEM_PTABLE_MAGIC[] = {
-+      0xaa, 0x73, 0xee, 0x55,
-+      0xdb, 0xbd, 0x5e, 0xe3,
-+};
-+
-+static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
-+{
-+      size_t size;
-+
-+      *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
-+                                  &size);
-+
-+      if (IS_ERR(*smem_blksz)) {
-+              pr_err("Unable to read flash blksz from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      if (size != sizeof(**smem_blksz)) {
-+              pr_err("Invalid flash blksz size in SMEM\n");
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int qcom_smem_get_flash_type(u64 **smem_flash_type)
-+{
-+      size_t size;
-+
-+      *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
-+                                      &size);
-+
-+      if (IS_ERR(*smem_flash_type)) {
-+              pr_err("Unable to read flash type from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      if (size != sizeof(**smem_flash_type)) {
-+              pr_err("Invalid flash type size in SMEM\n");
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
-+{
-+      size_t size;
-+
-+      *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
-+                              &size);
-+
-+      if (IS_ERR(*pparts)) {
-+              pr_err("Unable to read partition table from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      return 0;
-+}
-+
-+static int of_dev_node_match(struct device *dev, const void *data)
-+{
-+      return dev->of_node == data;
-+}
-+
-+static bool is_spi_device(struct device_node *np)
-+{
-+      struct device *dev;
-+
-+      dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
-+      if (!dev)
-+              return false;
-+
-+      put_device(dev);
-+      return true;
-+}
-+
-+static int parse_qcom_smem_partitions(struct mtd_info *master,
-+                                    const struct mtd_partition **pparts,
-+                                    struct mtd_part_parser_data *data)
-+{
-+      struct smem_partition_table *smem_parts;
-+      u64 *smem_flash_type, *smem_blksz;
-+      struct mtd_partition *mtd_parts;
-+      struct device_node *of_node = master->dev.of_node;
-+      int i, ret;
-+
-+      /*
-+       * SMEM will only store the partition table of the boot device.
-+       * If this is not the boot device, do not return any partition.
-+       */
-+      ret = qcom_smem_get_flash_type(&smem_flash_type);
-+      if (ret < 0)
-+              return ret;
-+
-+      if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
-+          || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
-+              return 0;
-+
-+      /*
-+       * Just for sanity purpose, make sure the block size in SMEM matches the
-+       * block size of the MTD device
-+       */
-+      ret = qcom_smem_get_flash_blksz(&smem_blksz);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (*smem_blksz != master->erasesize) {
-+              pr_err("SMEM block size differs from MTD block size\n");
-+              return -EINVAL;
-+      }
-+
-+      /* Get partition pointer from SMEM */
-+      ret = qcom_smem_get_flash_partitions(&smem_parts);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
-+                 sizeof(SMEM_PTABLE_MAGIC))) {
-+              pr_err("SMEM partition magic invalid\n");
-+              return -EINVAL;
-+      }
-+
-+      /* Allocate and populate the mtd structures */
-+      mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
-+                          GFP_KERNEL);
-+      if (!mtd_parts)
-+              return -ENOMEM;
-+
-+      for (i = 0; i < smem_parts->len; i++) {
-+              struct smem_partition *s_part = &smem_parts->parts[i];
-+              struct mtd_partition *m_part = &mtd_parts[i];
-+
-+              m_part->name = s_part->name;
-+              m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
-+              m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
-+
-+              /*
-+               * The last SMEM partition may have its size marked as
-+               * something like 0xffffffff, which means "until the end of the
-+               * flash device". In this case, truncate it.
-+               */
-+              if (m_part->offset + m_part->size > master->size)
-+                      m_part->size = master->size - m_part->offset;
-+      }
-+
-+      *pparts = mtd_parts;
-+
-+      return smem_parts->len;
-+}
-+
-+static const struct of_device_id qcom_smem_of_match_table[] = {
-+      { .compatible = "qcom,smem" },
-+      {},
-+};
-+MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table);
-+
-+static struct mtd_part_parser qcom_smem_parser = {
-+      .owner = THIS_MODULE,
-+      .parse_fn = parse_qcom_smem_partitions,
-+      .name = "qcom-smem",
-+      .of_match_table = qcom_smem_of_match_table,
-+};
-+
-+static int __init qcom_smem_parser_init(void)
-+{
-+      register_mtd_parser(&qcom_smem_parser);
-+      return 0;
-+}
-+
-+static void __exit qcom_smem_parser_exit(void)
-+{
-+      deregister_mtd_parser(&qcom_smem_parser);
-+}
-+
-+module_init(qcom_smem_parser_init);
-+module_exit(qcom_smem_parser_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
-+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
diff --git a/target/linux/ipq806x/patches-5.10/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.10/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch
deleted file mode 100644 (file)
index ff6f6ed..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
-From: John Crispin <john@phrozen.org>
-Date: Thu, 9 Mar 2017 09:31:44 +0100
-Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
-
-Signed-off-by: John Crispin <john@phrozen.org>
----
- drivers/mtd/parsers/qcom_smem_part.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/drivers/mtd/parsers/qcom_smem_part.c
-+++ b/drivers/mtd/parsers/qcom_smem_part.c
-@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st
-               m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
-               m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
-+              /* "rootfs" conflicts with OpenWrt auto mounting */
-+              if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
-+                      m_part->name = "ubi";
-+
-               /*
-                * The last SMEM partition may have its size marked as
-                * something like 0xffffffff, which means "until the end of the
diff --git a/target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch b/target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch
new file mode 100644 (file)
index 0000000..45062d5
--- /dev/null
@@ -0,0 +1,975 @@
+From 5c9f8c2dbdbe53818bcde6aa6695e1331e5f841f Mon Sep 17 00:00:00 2001
+From: Jonathan McDowell <noodles@earth.li>
+Date: Sat, 14 Nov 2020 14:02:33 +0000
+Subject: dmaengine: qcom: Add ADM driver
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8x60 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions.  The controller also provides
+flow control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from
+peripherals and also incorporates CRCI (client rate control interface)
+flow control.
+
+The hardware only supports a 32 bit physical address, so specifying
+!PHYS_ADDR_T_64BIT gives maximum COMPILE_TEST coverage without having to
+spend effort on kludging things in the code that will never actually be
+needed on real hardware.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+Signed-off-by: Jonathan McDowell <noodles@earth.li>
+Link: https://lore.kernel.org/r/20201114140233.GM32650@earth.li
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ drivers/dma/qcom/Kconfig    |  11 +
+ drivers/dma/qcom/Makefile   |   1 +
+ drivers/dma/qcom/qcom_adm.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 915 insertions(+)
+ create mode 100644 drivers/dma/qcom/qcom_adm.c
+
+diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig
+index 3bcb689162c67..0389d60d2604a 100644
+--- a/drivers/dma/qcom/Kconfig
++++ b/drivers/dma/qcom/Kconfig
+@@ -1,4 +1,15 @@
+ # SPDX-License-Identifier: GPL-2.0-only
++config QCOM_ADM
++      tristate "Qualcomm ADM support"
++      depends on (ARCH_QCOM || COMPILE_TEST) && !PHYS_ADDR_T_64BIT
++      select DMA_ENGINE
++      select DMA_VIRTUAL_CHANNELS
++      help
++        Enable support for the Qualcomm Application Data Mover (ADM) DMA
++        controller, as present on MSM8x60, APQ8064, and IPQ8064 devices.
++        This controller provides DMA capabilities for both general purpose
++        and on-chip peripheral devices.
++
+ config QCOM_BAM_DMA
+       tristate "QCOM BAM DMA support"
+       depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
+diff --git a/drivers/dma/qcom/Makefile b/drivers/dma/qcom/Makefile
+index 1ae92da88b0c9..346e643fbb6db 100644
+--- a/drivers/dma/qcom/Makefile
++++ b/drivers/dma/qcom/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+ obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o
+ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o
+ hdma_mgmt-objs         := hidma_mgmt.o hidma_mgmt_sys.o
+diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c
+new file mode 100644
+index 0000000000000..9b6f8e050ecce
+--- /dev/null
++++ b/drivers/dma/qcom/qcom_adm.c
+@@ -0,0 +1,903 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_dma.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++
++#include "../dmaengine.h"
++#include "../virt-dma.h"
++
++/* ADM registers - calculated from channel number and security domain */
++#define ADM_CHAN_MULTI                        0x4
++#define ADM_CI_MULTI                  0x4
++#define ADM_CRCI_MULTI                        0x4
++#define ADM_EE_MULTI                  0x800
++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * (chan))
++#define ADM_EE_OFFS(ee)                       (ADM_EE_MULTI * (ee))
++#define ADM_CHAN_EE_OFFS(chan, ee)    (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * (chan))
++#define ADM_CI_OFFS(ci)                       (ADM_CHAN_OFF(ci))
++#define ADM_CH_CMD_PTR(chan, ee)      (ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_RSLT(chan, ee)         (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_STATUS_SD(chan, ee)    (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_CONF(chan)             (0x240 + ADM_CHAN_OFFS(chan))
++#define ADM_CH_RSLT_CONF(chan, ee)    (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
++#define ADM_CI_CONF(ci)                       (0x390 + (ci) * ADM_CI_MULTI)
++#define ADM_GP_CTL                    0x3d8
++#define ADM_CRCI_CTL(crci, ee)                (0x400 + (crci) * ADM_CRCI_MULTI + \
++                                              ADM_EE_OFFS(ee))
++
++/* channel status */
++#define ADM_CH_STATUS_VALID           BIT(1)
++
++/* channel result */
++#define ADM_CH_RSLT_VALID             BIT(31)
++#define ADM_CH_RSLT_ERR                       BIT(3)
++#define ADM_CH_RSLT_FLUSH             BIT(2)
++#define ADM_CH_RSLT_TPD                       BIT(1)
++
++/* channel conf */
++#define ADM_CH_CONF_SHADOW_EN         BIT(12)
++#define ADM_CH_CONF_MPU_DISABLE               BIT(11)
++#define ADM_CH_CONF_PERM_MPU_CONF     BIT(9)
++#define ADM_CH_CONF_FORCE_RSLT_EN     BIT(7)
++#define ADM_CH_CONF_SEC_DOMAIN(ee)    ((((ee) & 0x3) << 4) | (((ee) & 0x4) << 11))
++
++/* channel result conf */
++#define ADM_CH_RSLT_CONF_FLUSH_EN     BIT(1)
++#define ADM_CH_RSLT_CONF_IRQ_EN               BIT(0)
++
++/* CRCI CTL */
++#define ADM_CRCI_CTL_MUX_SEL          BIT(18)
++#define ADM_CRCI_CTL_RST              BIT(17)
++
++/* CI configuration */
++#define ADM_CI_RANGE_END(x)           ((x) << 24)
++#define ADM_CI_RANGE_START(x)         ((x) << 16)
++#define ADM_CI_BURST_4_WORDS          BIT(2)
++#define ADM_CI_BURST_8_WORDS          BIT(3)
++
++/* GP CTL */
++#define ADM_GP_CTL_LP_EN              BIT(12)
++#define ADM_GP_CTL_LP_CNT(x)          ((x) << 8)
++
++/* Command pointer list entry */
++#define ADM_CPLE_LP                   BIT(31)
++#define ADM_CPLE_CMD_PTR_LIST         BIT(29)
++
++/* Command list entry */
++#define ADM_CMD_LC                    BIT(31)
++#define ADM_CMD_DST_CRCI(n)           (((n) & 0xf) << 7)
++#define ADM_CMD_SRC_CRCI(n)           (((n) & 0xf) << 3)
++
++#define ADM_CMD_TYPE_SINGLE           0x0
++#define ADM_CMD_TYPE_BOX              0x3
++
++#define ADM_CRCI_MUX_SEL              BIT(4)
++#define ADM_DESC_ALIGN                        8
++#define ADM_MAX_XFER                  (SZ_64K - 1)
++#define ADM_MAX_ROWS                  (SZ_64K - 1)
++#define ADM_MAX_CHANNELS              16
++
++struct adm_desc_hw_box {
++      u32 cmd;
++      u32 src_addr;
++      u32 dst_addr;
++      u32 row_len;
++      u32 num_rows;
++      u32 row_offset;
++};
++
++struct adm_desc_hw_single {
++      u32 cmd;
++      u32 src_addr;
++      u32 dst_addr;
++      u32 len;
++};
++
++struct adm_async_desc {
++      struct virt_dma_desc vd;
++      struct adm_device *adev;
++
++      size_t length;
++      enum dma_transfer_direction dir;
++      dma_addr_t dma_addr;
++      size_t dma_len;
++
++      void *cpl;
++      dma_addr_t cp_addr;
++      u32 crci;
++      u32 mux;
++      u32 blk_size;
++};
++
++struct adm_chan {
++      struct virt_dma_chan vc;
++      struct adm_device *adev;
++
++      /* parsed from DT */
++      u32 id;                 /* channel id */
++
++      struct adm_async_desc *curr_txd;
++      struct dma_slave_config slave;
++      struct list_head node;
++
++      int error;
++      int initialized;
++};
++
++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
++{
++      return container_of(common, struct adm_chan, vc.chan);
++}
++
++struct adm_device {
++      void __iomem *regs;
++      struct device *dev;
++      struct dma_device common;
++      struct device_dma_parameters dma_parms;
++      struct adm_chan *channels;
++
++      u32 ee;
++
++      struct clk *core_clk;
++      struct clk *iface_clk;
++
++      struct reset_control *clk_reset;
++      struct reset_control *c0_reset;
++      struct reset_control *c1_reset;
++      struct reset_control *c2_reset;
++      int irq;
++};
++
++/**
++ * adm_free_chan - Frees dma resources associated with the specific channel
++ *
++ * Free all allocated descriptors associated with this channel
++ *
++ */
++static void adm_free_chan(struct dma_chan *chan)
++{
++      /* free all queued descriptors */
++      vchan_free_chan_resources(to_virt_chan(chan));
++}
++
++/**
++ * adm_get_blksize - Get block size from burst value
++ *
++ */
++static int adm_get_blksize(unsigned int burst)
++{
++      int ret;
++
++      switch (burst) {
++      case 16:
++      case 32:
++      case 64:
++      case 128:
++              ret = ffs(burst >> 4) - 1;
++              break;
++      case 192:
++              ret = 4;
++              break;
++      case 256:
++              ret = 5;
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++/**
++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @crci: CRCI value
++ * @burst: Burst size of transaction
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_fc_descriptors(struct adm_chan *achan, void *desc,
++                                      struct scatterlist *sg, u32 crci,
++                                      u32 burst,
++                                      enum dma_transfer_direction direction)
++{
++      struct adm_desc_hw_box *box_desc = NULL;
++      struct adm_desc_hw_single *single_desc;
++      u32 remainder = sg_dma_len(sg);
++      u32 rows, row_offset, crci_cmd;
++      u32 mem_addr = sg_dma_address(sg);
++      u32 *incr_addr = &mem_addr;
++      u32 *src, *dst;
++
++      if (direction == DMA_DEV_TO_MEM) {
++              crci_cmd = ADM_CMD_SRC_CRCI(crci);
++              row_offset = burst;
++              src = &achan->slave.src_addr;
++              dst = &mem_addr;
++      } else {
++              crci_cmd = ADM_CMD_DST_CRCI(crci);
++              row_offset = burst << 16;
++              src = &mem_addr;
++              dst = &achan->slave.dst_addr;
++      }
++
++      while (remainder >= burst) {
++              box_desc = desc;
++              box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
++              box_desc->row_offset = row_offset;
++              box_desc->src_addr = *src;
++              box_desc->dst_addr = *dst;
++
++              rows = remainder / burst;
++              rows = min_t(u32, rows, ADM_MAX_ROWS);
++              box_desc->num_rows = rows << 16 | rows;
++              box_desc->row_len = burst << 16 | burst;
++
++              *incr_addr += burst * rows;
++              remainder -= burst * rows;
++              desc += sizeof(*box_desc);
++      }
++
++      /* if leftover bytes, do one single descriptor */
++      if (remainder) {
++              single_desc = desc;
++              single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
++              single_desc->len = remainder;
++              single_desc->src_addr = *src;
++              single_desc->dst_addr = *dst;
++              desc += sizeof(*single_desc);
++
++              if (sg_is_last(sg))
++                      single_desc->cmd |= ADM_CMD_LC;
++      } else {
++              if (box_desc && sg_is_last(sg))
++                      box_desc->cmd |= ADM_CMD_LC;
++      }
++
++      return desc;
++}
++
++/**
++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, void *desc,
++                                          struct scatterlist *sg,
++                                          enum dma_transfer_direction direction)
++{
++      struct adm_desc_hw_single *single_desc;
++      u32 remainder = sg_dma_len(sg);
++      u32 mem_addr = sg_dma_address(sg);
++      u32 *incr_addr = &mem_addr;
++      u32 *src, *dst;
++
++      if (direction == DMA_DEV_TO_MEM) {
++              src = &achan->slave.src_addr;
++              dst = &mem_addr;
++      } else {
++              src = &mem_addr;
++              dst = &achan->slave.dst_addr;
++      }
++
++      do {
++              single_desc = desc;
++              single_desc->cmd = ADM_CMD_TYPE_SINGLE;
++              single_desc->src_addr = *src;
++              single_desc->dst_addr = *dst;
++              single_desc->len = (remainder > ADM_MAX_XFER) ?
++                              ADM_MAX_XFER : remainder;
++
++              remainder -= single_desc->len;
++              *incr_addr += single_desc->len;
++              desc += sizeof(*single_desc);
++      } while (remainder);
++
++      /* set last command if this is the end of the whole transaction */
++      if (sg_is_last(sg))
++              single_desc->cmd |= ADM_CMD_LC;
++
++      return desc;
++}
++
++/**
++ * adm_prep_slave_sg - Prep slave sg transaction
++ *
++ * @chan: dma channel
++ * @sgl: scatter gather list
++ * @sg_len: length of sg
++ * @direction: DMA transfer direction
++ * @flags: DMA flags
++ * @context: transfer context (unused)
++ */
++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
++                                                       struct scatterlist *sgl,
++                                                       unsigned int sg_len,
++                                                       enum dma_transfer_direction direction,
++                                                       unsigned long flags,
++                                                       void *context)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct adm_device *adev = achan->adev;
++      struct adm_async_desc *async_desc;
++      struct scatterlist *sg;
++      dma_addr_t cple_addr;
++      u32 i, burst;
++      u32 single_count = 0, box_count = 0, crci = 0;
++      void *desc;
++      u32 *cple;
++      int blk_size = 0;
++
++      if (!is_slave_direction(direction)) {
++              dev_err(adev->dev, "invalid dma direction\n");
++              return NULL;
++      }
++
++      /*
++       * get burst value from slave configuration
++       */
++      burst = (direction == DMA_MEM_TO_DEV) ?
++              achan->slave.dst_maxburst :
++              achan->slave.src_maxburst;
++
++      /* if using flow control, validate burst and crci values */
++      if (achan->slave.device_fc) {
++              blk_size = adm_get_blksize(burst);
++              if (blk_size < 0) {
++                      dev_err(adev->dev, "invalid burst value: %d\n",
++                              burst);
++                      return ERR_PTR(-EINVAL);
++              }
++
++              crci = achan->slave.slave_id & 0xf;
++              if (!crci || achan->slave.slave_id > 0x1f) {
++                      dev_err(adev->dev, "invalid crci value\n");
++                      return ERR_PTR(-EINVAL);
++              }
++      }
++
++      /* iterate through sgs and compute allocation size of structures */
++      for_each_sg(sgl, sg, sg_len, i) {
++              if (achan->slave.device_fc) {
++                      box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
++                                                ADM_MAX_ROWS);
++                      if (sg_dma_len(sg) % burst)
++                              single_count++;
++              } else {
++                      single_count += DIV_ROUND_UP(sg_dma_len(sg),
++                                                   ADM_MAX_XFER);
++              }
++      }
++
++      async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++      if (!async_desc)
++              return ERR_PTR(-ENOMEM);
++
++      if (crci)
++              async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
++                                      ADM_CRCI_CTL_MUX_SEL : 0;
++      async_desc->crci = crci;
++      async_desc->blk_size = blk_size;
++      async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
++                              box_count * sizeof(struct adm_desc_hw_box) +
++                              sizeof(*cple) + 2 * ADM_DESC_ALIGN;
++
++      async_desc->cpl = kzalloc(async_desc->dma_len, GFP_NOWAIT);
++      if (!async_desc->cpl)
++              goto free;
++
++      async_desc->adev = adev;
++
++      /* both command list entry and descriptors must be 8 byte aligned */
++      cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++      desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
++
++      for_each_sg(sgl, sg, sg_len, i) {
++              async_desc->length += sg_dma_len(sg);
++
++              if (achan->slave.device_fc)
++                      desc = adm_process_fc_descriptors(achan, desc, sg, crci,
++                                                        burst, direction);
++              else
++                      desc = adm_process_non_fc_descriptors(achan, desc, sg,
++                                                            direction);
++      }
++
++      async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl,
++                                            async_desc->dma_len,
++                                            DMA_TO_DEVICE);
++      if (dma_mapping_error(adev->dev, async_desc->dma_addr))
++              goto free;
++
++      cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl);
++
++      /* init cmd list */
++      dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple),
++                              DMA_TO_DEVICE);
++      *cple = ADM_CPLE_LP;
++      *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3;
++      dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple),
++                                 DMA_TO_DEVICE);
++
++      return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++
++free:
++      kfree(async_desc);
++      return ERR_PTR(-ENOMEM);
++}
++
++/**
++ * adm_terminate_all - terminate all transactions on a channel
++ * @achan: adm dma channel
++ *
++ * Dequeues and frees all transactions, aborts current transaction
++ * No callbacks are done
++ *
++ */
++static int adm_terminate_all(struct dma_chan *chan)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct adm_device *adev = achan->adev;
++      unsigned long flags;
++      LIST_HEAD(head);
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++      vchan_get_all_descriptors(&achan->vc, &head);
++
++      /* send flush command to terminate current transaction */
++      writel_relaxed(0x0,
++                     adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++      vchan_dma_desc_free_list(&achan->vc, &head);
++
++      return 0;
++}
++
++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      unsigned long flag;
++
++      spin_lock_irqsave(&achan->vc.lock, flag);
++      memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
++      spin_unlock_irqrestore(&achan->vc.lock, flag);
++
++      return 0;
++}
++
++/**
++ * adm_start_dma - start next transaction
++ * @achan - ADM dma channel
++ */
++static void adm_start_dma(struct adm_chan *achan)
++{
++      struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
++      struct adm_device *adev = achan->adev;
++      struct adm_async_desc *async_desc;
++
++      lockdep_assert_held(&achan->vc.lock);
++
++      if (!vd)
++              return;
++
++      list_del(&vd->node);
++
++      /* write next command list out to the CMD FIFO */
++      async_desc = container_of(vd, struct adm_async_desc, vd);
++      achan->curr_txd = async_desc;
++
++      /* reset channel error */
++      achan->error = 0;
++
++      if (!achan->initialized) {
++              /* enable interrupts */
++              writel(ADM_CH_CONF_SHADOW_EN |
++                     ADM_CH_CONF_PERM_MPU_CONF |
++                     ADM_CH_CONF_MPU_DISABLE |
++                     ADM_CH_CONF_SEC_DOMAIN(adev->ee),
++                     adev->regs + ADM_CH_CONF(achan->id));
++
++              writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
++                     adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++              achan->initialized = 1;
++      }
++
++      /* set the crci block size if this transaction requires CRCI */
++      if (async_desc->crci) {
++              writel(async_desc->mux | async_desc->blk_size,
++                     adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
++      }
++
++      /* make sure IRQ enable doesn't get reordered */
++      wmb();
++
++      /* write next command list out to the CMD FIFO */
++      writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++             adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
++}
++
++/**
++ * adm_dma_irq - irq handler for ADM controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t adm_dma_irq(int irq, void *data)
++{
++      struct adm_device *adev = data;
++      u32 srcs, i;
++      struct adm_async_desc *async_desc;
++      unsigned long flags;
++
++      srcs = readl_relaxed(adev->regs +
++                      ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++              struct adm_chan *achan = &adev->channels[i];
++              u32 status, result;
++
++              if (srcs & BIT(i)) {
++                      status = readl_relaxed(adev->regs +
++                                             ADM_CH_STATUS_SD(i, adev->ee));
++
++                      /* if no result present, skip */
++                      if (!(status & ADM_CH_STATUS_VALID))
++                              continue;
++
++                      result = readl_relaxed(adev->regs +
++                              ADM_CH_RSLT(i, adev->ee));
++
++                      /* no valid results, skip */
++                      if (!(result & ADM_CH_RSLT_VALID))
++                              continue;
++
++                      /* flag error if transaction was flushed or failed */
++                      if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
++                              achan->error = 1;
++
++                      spin_lock_irqsave(&achan->vc.lock, flags);
++                      async_desc = achan->curr_txd;
++
++                      achan->curr_txd = NULL;
++
++                      if (async_desc) {
++                              vchan_cookie_complete(&async_desc->vd);
++
++                              /* kick off next DMA */
++                              adm_start_dma(achan);
++                      }
++
++                      spin_unlock_irqrestore(&achan->vc.lock, flags);
++              }
++      }
++
++      return IRQ_HANDLED;
++}
++
++/**
++ * adm_tx_status - returns status of transaction
++ * @chan: dma channel
++ * @cookie: transaction cookie
++ * @txstate: DMA transaction state
++ *
++ * Return status of dma transaction
++ */
++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++                                   struct dma_tx_state *txstate)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct virt_dma_desc *vd;
++      enum dma_status ret;
++      unsigned long flags;
++      size_t residue = 0;
++
++      ret = dma_cookie_status(chan, cookie, txstate);
++      if (ret == DMA_COMPLETE || !txstate)
++              return ret;
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++
++      vd = vchan_find_desc(&achan->vc, cookie);
++      if (vd)
++              residue = container_of(vd, struct adm_async_desc, vd)->length;
++
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++      /*
++       * residue is either the full length if it is in the issued list, or 0
++       * if it is in progress.  We have no reliable way of determining
++       * anything inbetween
++       */
++      dma_set_residue(txstate, residue);
++
++      if (achan->error)
++              return DMA_ERROR;
++
++      return ret;
++}
++
++/**
++ * adm_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Issues all pending transactions and starts DMA
++ */
++static void adm_issue_pending(struct dma_chan *chan)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      unsigned long flags;
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++
++      if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
++              adm_start_dma(achan);
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++}
++
++/**
++ * adm_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void adm_dma_free_desc(struct virt_dma_desc *vd)
++{
++      struct adm_async_desc *async_desc = container_of(vd,
++                      struct adm_async_desc, vd);
++
++      dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr,
++                       async_desc->dma_len, DMA_TO_DEVICE);
++      kfree(async_desc->cpl);
++      kfree(async_desc);
++}
++
++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
++                           u32 index)
++{
++      achan->id = index;
++      achan->adev = adev;
++
++      vchan_init(&achan->vc, &adev->common);
++      achan->vc.desc_free = adm_dma_free_desc;
++}
++
++static int adm_dma_probe(struct platform_device *pdev)
++{
++      struct adm_device *adev;
++      int ret;
++      u32 i;
++
++      adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
++      if (!adev)
++              return -ENOMEM;
++
++      adev->dev = &pdev->dev;
++
++      adev->regs = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(adev->regs))
++              return PTR_ERR(adev->regs);
++
++      adev->irq = platform_get_irq(pdev, 0);
++      if (adev->irq < 0)
++              return adev->irq;
++
++      ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
++      if (ret) {
++              dev_err(adev->dev, "Execution environment unspecified\n");
++              return ret;
++      }
++
++      adev->core_clk = devm_clk_get(adev->dev, "core");
++      if (IS_ERR(adev->core_clk))
++              return PTR_ERR(adev->core_clk);
++
++      adev->iface_clk = devm_clk_get(adev->dev, "iface");
++      if (IS_ERR(adev->iface_clk))
++              return PTR_ERR(adev->iface_clk);
++
++      adev->clk_reset = devm_reset_control_get_exclusive(&pdev->dev, "clk");
++      if (IS_ERR(adev->clk_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 reset\n");
++              return PTR_ERR(adev->clk_reset);
++      }
++
++      adev->c0_reset = devm_reset_control_get_exclusive(&pdev->dev, "c0");
++      if (IS_ERR(adev->c0_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
++              return PTR_ERR(adev->c0_reset);
++      }
++
++      adev->c1_reset = devm_reset_control_get_exclusive(&pdev->dev, "c1");
++      if (IS_ERR(adev->c1_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
++              return PTR_ERR(adev->c1_reset);
++      }
++
++      adev->c2_reset = devm_reset_control_get_exclusive(&pdev->dev, "c2");
++      if (IS_ERR(adev->c2_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
++              return PTR_ERR(adev->c2_reset);
++      }
++
++      ret = clk_prepare_enable(adev->core_clk);
++      if (ret) {
++              dev_err(adev->dev, "failed to prepare/enable core clock\n");
++              return ret;
++      }
++
++      ret = clk_prepare_enable(adev->iface_clk);
++      if (ret) {
++              dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++              goto err_disable_core_clk;
++      }
++
++      reset_control_assert(adev->clk_reset);
++      reset_control_assert(adev->c0_reset);
++      reset_control_assert(adev->c1_reset);
++      reset_control_assert(adev->c2_reset);
++
++      udelay(2);
++
++      reset_control_deassert(adev->clk_reset);
++      reset_control_deassert(adev->c0_reset);
++      reset_control_deassert(adev->c1_reset);
++      reset_control_deassert(adev->c2_reset);
++
++      adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
++                                    sizeof(*adev->channels), GFP_KERNEL);
++
++      if (!adev->channels) {
++              ret = -ENOMEM;
++              goto err_disable_clks;
++      }
++
++      /* allocate and initialize channels */
++      INIT_LIST_HEAD(&adev->common.channels);
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++)
++              adm_channel_init(adev, &adev->channels[i], i);
++
++      /* reset CRCIs */
++      for (i = 0; i < 16; i++)
++              writel(ADM_CRCI_CTL_RST, adev->regs +
++                      ADM_CRCI_CTL(i, adev->ee));
++
++      /* configure client interfaces */
++      writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
++      writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
++      writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
++      writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
++             adev->regs + ADM_GP_CTL);
++
++      ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++                             0, "adm_dma", adev);
++      if (ret)
++              goto err_disable_clks;
++
++      platform_set_drvdata(pdev, adev);
++
++      adev->common.dev = adev->dev;
++      adev->common.dev->dma_parms = &adev->dma_parms;
++
++      /* set capabilities */
++      dma_cap_zero(adev->common.cap_mask);
++      dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++      dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++      /* initialize dmaengine apis */
++      adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
++      adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
++      adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++      adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++      adev->common.device_free_chan_resources = adm_free_chan;
++      adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++      adev->common.device_issue_pending = adm_issue_pending;
++      adev->common.device_tx_status = adm_tx_status;
++      adev->common.device_terminate_all = adm_terminate_all;
++      adev->common.device_config = adm_slave_config;
++
++      ret = dma_async_device_register(&adev->common);
++      if (ret) {
++              dev_err(adev->dev, "failed to register dma async device\n");
++              goto err_disable_clks;
++      }
++
++      ret = of_dma_controller_register(pdev->dev.of_node,
++                                       of_dma_xlate_by_chan_id,
++                                       &adev->common);
++      if (ret)
++              goto err_unregister_dma;
++
++      return 0;
++
++err_unregister_dma:
++      dma_async_device_unregister(&adev->common);
++err_disable_clks:
++      clk_disable_unprepare(adev->iface_clk);
++err_disable_core_clk:
++      clk_disable_unprepare(adev->core_clk);
++
++      return ret;
++}
++
++static int adm_dma_remove(struct platform_device *pdev)
++{
++      struct adm_device *adev = platform_get_drvdata(pdev);
++      struct adm_chan *achan;
++      u32 i;
++
++      of_dma_controller_free(pdev->dev.of_node);
++      dma_async_device_unregister(&adev->common);
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++              achan = &adev->channels[i];
++
++              /* mask IRQs for this channel/EE pair */
++              writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++              tasklet_kill(&adev->channels[i].vc.task);
++              adm_terminate_all(&adev->channels[i].vc.chan);
++      }
++
++      devm_free_irq(adev->dev, adev->irq, adev);
++
++      clk_disable_unprepare(adev->core_clk);
++      clk_disable_unprepare(adev->iface_clk);
++
++      return 0;
++}
++
++static const struct of_device_id adm_of_match[] = {
++      { .compatible = "qcom,adm", },
++      {}
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++
++static struct platform_driver adm_dma_driver = {
++      .probe = adm_dma_probe,
++      .remove = adm_dma_remove,
++      .driver = {
++              .name = "adm-dma-engine",
++              .of_match_table = adm_of_match,
++      },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+-- 
+cgit 1.2.3-1.el7
+
diff --git a/target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch b/target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch
new file mode 100644 (file)
index 0000000..7aec4fc
--- /dev/null
@@ -0,0 +1,227 @@
+From 803eb124e1a64e42888542c3444bfe6dac412c7f Mon Sep 17 00:00:00 2001
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Date: Mon, 4 Jan 2021 09:41:35 +0530
+Subject: mtd: parsers: Add Qcom SMEM parser
+
+NAND based Qualcomm platforms have the partition table populated in the
+Shared Memory (SMEM). Hence, add a parser for parsing the partitions
+from it.
+
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadhasivam@linaro.org
+---
+ drivers/mtd/parsers/Kconfig        |   8 ++
+ drivers/mtd/parsers/Makefile       |   1 +
+ drivers/mtd/parsers/qcomsmempart.c | 170 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 179 insertions(+)
+ create mode 100644 drivers/mtd/parsers/qcomsmempart.c
+
+diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
+index e72354322f628..d90c302290522 100644
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -160,6 +160,14 @@ config MTD_REDBOOT_PARTS_READONLY
+         'FIS directory' images, enable this option.
+ endif # MTD_REDBOOT_PARTS
++
++config MTD_QCOMSMEM_PARTS
++      tristate "Qualcomm SMEM NAND flash partition parser"
++      depends on MTD_NAND_QCOM || COMPILE_TEST
++      depends on QCOM_SMEM
++      help
++        This provides support for parsing partitions from Shared Memory (SMEM)
++        for NAND flash on Qualcomm platforms.
+ config MTD_ROUTERBOOT_PARTS
+       tristate "RouterBoot flash partition parser"
+diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile
+index b0c5f62f9e858..50eb0b0a22105 100644
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -9,4 +9,5 @@ obj-$(CONFIG_MTD_AFS_PARTS)            += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX)          += parser_trx.o
+ obj-$(CONFIG_MTD_SHARPSL_PARTS)               += sharpslpart.o
+ obj-$(CONFIG_MTD_REDBOOT_PARTS)               += redboot.o
++obj-$(CONFIG_MTD_QCOMSMEM_PARTS)      += qcomsmempart.o
+ obj-$(CONFIG_MTD_ROUTERBOOT_PARTS)            += routerbootpart.o
+diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c
+new file mode 100644
+index 0000000000000..808cb33d71f8e
+--- /dev/null
++++ b/drivers/mtd/parsers/qcomsmempart.c
+@@ -0,0 +1,170 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Qualcomm SMEM NAND flash partition parser
++ *
++ * Copyright (C) 2020, Linaro Ltd.
++ */
++
++#include <linux/ctype.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/slab.h>
++#include <linux/soc/qcom/smem.h>
++
++#define SMEM_AARM_PARTITION_TABLE     9
++#define SMEM_APPS                     0
++
++#define SMEM_FLASH_PART_MAGIC1                0x55ee73aa
++#define SMEM_FLASH_PART_MAGIC2                0xe35ebddb
++#define SMEM_FLASH_PTABLE_V3          3
++#define SMEM_FLASH_PTABLE_V4          4
++#define SMEM_FLASH_PTABLE_MAX_PARTS_V3        16
++#define SMEM_FLASH_PTABLE_MAX_PARTS_V4        48
++#define SMEM_FLASH_PTABLE_HDR_LEN     (4 * sizeof(u32))
++#define SMEM_FLASH_PTABLE_NAME_SIZE   16
++
++/**
++ * struct smem_flash_pentry - SMEM Flash partition entry
++ * @name: Name of the partition
++ * @offset: Offset in blocks
++ * @length: Length of the partition in blocks
++ * @attr: Flags for this partition
++ */
++struct smem_flash_pentry {
++      char name[SMEM_FLASH_PTABLE_NAME_SIZE];
++      __le32 offset;
++      __le32 length;
++      u8 attr;
++} __packed __aligned(4);
++
++/**
++ * struct smem_flash_ptable - SMEM Flash partition table
++ * @magic1: Partition table Magic 1
++ * @magic2: Partition table Magic 2
++ * @version: Partition table version
++ * @numparts: Number of partitions in this ptable
++ * @pentry: Flash partition entries belonging to this ptable
++ */
++struct smem_flash_ptable {
++      __le32 magic1;
++      __le32 magic2;
++      __le32 version;
++      __le32 numparts;
++      struct smem_flash_pentry pentry[SMEM_FLASH_PTABLE_MAX_PARTS_V4];
++} __packed __aligned(4);
++
++static int parse_qcomsmem_part(struct mtd_info *mtd,
++                             const struct mtd_partition **pparts,
++                             struct mtd_part_parser_data *data)
++{
++      struct smem_flash_pentry *pentry;
++      struct smem_flash_ptable *ptable;
++      size_t len = SMEM_FLASH_PTABLE_HDR_LEN;
++      struct mtd_partition *parts;
++      int ret, i, numparts;
++      char *name, *c;
++
++      pr_debug("Parsing partition table info from SMEM\n");
++      ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
++      if (IS_ERR(ptable)) {
++              pr_err("Error reading partition table header\n");
++              return PTR_ERR(ptable);
++      }
++
++      /* Verify ptable magic */
++      if (le32_to_cpu(ptable->magic1) != SMEM_FLASH_PART_MAGIC1 ||
++          le32_to_cpu(ptable->magic2) != SMEM_FLASH_PART_MAGIC2) {
++              pr_err("Partition table magic verification failed\n");
++              return -EINVAL;
++      }
++
++      /* Ensure that # of partitions is less than the max we have allocated */
++      numparts = le32_to_cpu(ptable->numparts);
++      if (numparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4) {
++              pr_err("Partition numbers exceed the max limit\n");
++              return -EINVAL;
++      }
++
++      /* Find out length of partition data based on table version */
++      if (le32_to_cpu(ptable->version) <= SMEM_FLASH_PTABLE_V3) {
++              len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 *
++                      sizeof(struct smem_flash_pentry);
++      } else if (le32_to_cpu(ptable->version) == SMEM_FLASH_PTABLE_V4) {
++              len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 *
++                      sizeof(struct smem_flash_pentry);
++      } else {
++              pr_err("Unknown ptable version (%d)", le32_to_cpu(ptable->version));
++              return -EINVAL;
++      }
++
++      /*
++       * Now that the partition table header has been parsed, verified
++       * and the length of the partition table calculated, read the
++       * complete partition table
++       */
++      ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
++      if (IS_ERR_OR_NULL(ptable)) {
++              pr_err("Error reading partition table\n");
++              return PTR_ERR(ptable);
++      }
++
++      parts = kcalloc(numparts, sizeof(*parts), GFP_KERNEL);
++      if (!parts)
++              return -ENOMEM;
++
++      for (i = 0; i < numparts; i++) {
++              pentry = &ptable->pentry[i];
++              if (pentry->name[0] == '\0')
++                      continue;
++
++              name = kstrdup(pentry->name, GFP_KERNEL);
++              if (!name) {
++                      ret = -ENOMEM;
++                      goto out_free_parts;
++              }
++
++              /* Convert name to lower case */
++              for (c = name; *c != '\0'; c++)
++                      *c = tolower(*c);
++
++              parts[i].name = name;
++              parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
++              parts[i].mask_flags = pentry->attr;
++              parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize;
++              pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
++                       i, pentry->name, le32_to_cpu(pentry->offset),
++                       le32_to_cpu(pentry->length), pentry->attr);
++      }
++
++      pr_debug("SMEM partition table found: ver: %d len: %d\n",
++               le32_to_cpu(ptable->version), numparts);
++      *pparts = parts;
++
++      return numparts;
++
++out_free_parts:
++      while (--i >= 0)
++              kfree(parts[i].name);
++      kfree(parts);
++      *pparts = NULL;
++
++      return ret;
++}
++
++static const struct of_device_id qcomsmem_of_match_table[] = {
++      { .compatible = "qcom,smem-part" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, qcomsmem_of_match_table);
++
++static struct mtd_part_parser mtd_parser_qcomsmem = {
++      .parse_fn = parse_qcomsmem_part,
++      .name = "qcomsmem",
++      .of_match_table = qcomsmem_of_match_table,
++};
++module_mtd_part_parser(mtd_parser_qcomsmem);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
++MODULE_DESCRIPTION("Qualcomm SMEM NAND flash partition parser");
+-- 
+cgit 1.2.3-1.el7
+
diff --git a/target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch
new file mode 100644 (file)
index 0000000..47f3bfb
--- /dev/null
@@ -0,0 +1,24 @@
+From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 09:31:44 +0100
+Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/mtd/parsers/qcomsmempart.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/parsers/qcomsmempart.c
++++ b/drivers/mtd/parsers/qcomsmempart.c
+@@ -189,6 +189,11 @@ static int parse_qcomsmem_part(st
+               parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
+               parts[i].mask_flags = pentry->attr;
+               parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize;
++
++              /* "rootfs" conflicts with OpenWrt auto mounting */
++              if (mtd_type_is_nand(mtd) && !strcmp(name, "rootfs"))
++                      parts[i].name = "ubi";
++
+               pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
+                        i, pentry->name, le32_to_cpu(pentry->offset),
+                        le32_to_cpu(pentry->length), pentry->attr);
diff --git a/target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch
deleted file mode 100644 (file)
index aa7d2e7..0000000
+++ /dev/null
@@ -1,966 +0,0 @@
-From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001
-From: Thomas Pedersen <twp@codeaurora.org>
-Date: Mon, 16 May 2016 17:58:51 -0700
-Subject: [PATCH 02/69] dmaengine: Add ADM driver
-
-Original patch by Andy Gross.
-
-Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
-controller found in the MSM8x60 and IPQ/APQ8064 platforms.
-
-The ADM supports both memory to memory transactions and memory
-to/from peripheral device transactions.  The controller also provides flow
-control capabilities for transactions to/from peripheral devices.
-
-The initial release of this driver supports slave transfers to/from peripherals
-and also incorporates CRCI (client rate control interface) flow control.
-
-Signed-off-by: Andy Gross <agross@codeaurora.org>
-Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
----
- drivers/dma/qcom/Kconfig    |  10 +
- drivers/dma/qcom/Makefile   |   1 +
- drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 911 insertions(+)
- create mode 100644 drivers/dma/qcom/qcom_adm.c
-
---- a/drivers/dma/qcom/Kconfig
-+++ b/drivers/dma/qcom/Kconfig
-@@ -28,3 +28,13 @@ config QCOM_HIDMA
-         (user to kernel, kernel to kernel, etc.).  It only supports
-         memcpy interface. The core is not intended for general
-         purpose slave DMA.
-+
-+config QCOM_ADM
-+      tristate "Qualcomm ADM support"
-+      depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
-+      select DMA_ENGINE
-+      select DMA_VIRTUAL_CHANNELS
-+      ---help---
-+        Enable support for the Qualcomm ADM DMA controller.  This controller
-+        provides DMA capabilities for both general purpose and on-chip
-+        peripheral devices.
---- a/drivers/dma/qcom/Makefile
-+++ b/drivers/dma/qcom/Makefile
-@@ -4,3 +4,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg
- hdma_mgmt-objs         := hidma_mgmt.o hidma_mgmt_sys.o
- obj-$(CONFIG_QCOM_HIDMA) +=  hdma.o
- hdma-objs        := hidma_ll.o hidma.o hidma_dbg.o
-+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
---- /dev/null
-+++ b/drivers/dma/qcom/qcom_adm.c
-@@ -0,0 +1,914 @@
-+/*
-+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 and
-+ * only version 2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/io.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/module.h>
-+#include <linux/interrupt.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/scatterlist.h>
-+#include <linux/device.h>
-+#include <linux/platform_device.h>
-+#include <linux/of.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_dma.h>
-+#include <linux/reset.h>
-+#include <linux/clk.h>
-+#include <linux/dmaengine.h>
-+
-+#include "../dmaengine.h"
-+#include "../virt-dma.h"
-+
-+/* ADM registers - calculated from channel number and security domain */
-+#define ADM_CHAN_MULTI                        0x4
-+#define ADM_CI_MULTI                  0x4
-+#define ADM_CRCI_MULTI                        0x4
-+#define ADM_EE_MULTI                  0x800
-+#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
-+#define ADM_EE_OFFS(ee)                       (ADM_EE_MULTI * ee)
-+#define ADM_CHAN_EE_OFFS(chan, ee)    (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
-+#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * chan)
-+#define ADM_CI_OFFS(ci)                       (ADM_CHAN_OFF(ci))
-+#define ADM_CH_CMD_PTR(chan, ee)      (ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_RSLT(chan, ee)         (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_STATUS_SD(chan, ee)    (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_CH_CONF(chan)             (0x240 + ADM_CHAN_OFFS(chan))
-+#define ADM_CH_RSLT_CONF(chan, ee)    (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
-+#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
-+#define ADM_CI_CONF(ci)                       (0x390 + ci * ADM_CI_MULTI)
-+#define ADM_GP_CTL                    0x3d8
-+#define ADM_CRCI_CTL(crci, ee)                (0x400 + crci * ADM_CRCI_MULTI + \
-+                                              ADM_EE_OFFS(ee))
-+
-+/* channel status */
-+#define ADM_CH_STATUS_VALID   BIT(1)
-+
-+/* channel result */
-+#define ADM_CH_RSLT_VALID     BIT(31)
-+#define ADM_CH_RSLT_ERR               BIT(3)
-+#define ADM_CH_RSLT_FLUSH     BIT(2)
-+#define ADM_CH_RSLT_TPD               BIT(1)
-+
-+/* channel conf */
-+#define ADM_CH_CONF_SHADOW_EN         BIT(12)
-+#define ADM_CH_CONF_MPU_DISABLE               BIT(11)
-+#define ADM_CH_CONF_PERM_MPU_CONF     BIT(9)
-+#define ADM_CH_CONF_FORCE_RSLT_EN     BIT(7)
-+#define ADM_CH_CONF_SEC_DOMAIN(ee)    (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
-+
-+/* channel result conf */
-+#define ADM_CH_RSLT_CONF_FLUSH_EN     BIT(1)
-+#define ADM_CH_RSLT_CONF_IRQ_EN               BIT(0)
-+
-+/* CRCI CTL */
-+#define ADM_CRCI_CTL_MUX_SEL  BIT(18)
-+#define ADM_CRCI_CTL_RST      BIT(17)
-+
-+/* CI configuration */
-+#define ADM_CI_RANGE_END(x)   (x << 24)
-+#define ADM_CI_RANGE_START(x) (x << 16)
-+#define ADM_CI_BURST_4_WORDS  BIT(2)
-+#define ADM_CI_BURST_8_WORDS  BIT(3)
-+
-+/* GP CTL */
-+#define ADM_GP_CTL_LP_EN      BIT(12)
-+#define ADM_GP_CTL_LP_CNT(x)  (x << 8)
-+
-+/* Command pointer list entry */
-+#define ADM_CPLE_LP           BIT(31)
-+#define ADM_CPLE_CMD_PTR_LIST BIT(29)
-+
-+/* Command list entry */
-+#define ADM_CMD_LC            BIT(31)
-+#define ADM_CMD_DST_CRCI(n)   (((n) & 0xf) << 7)
-+#define ADM_CMD_SRC_CRCI(n)   (((n) & 0xf) << 3)
-+
-+#define ADM_CMD_TYPE_SINGLE   0x0
-+#define ADM_CMD_TYPE_BOX      0x3
-+
-+#define ADM_CRCI_MUX_SEL      BIT(4)
-+#define ADM_DESC_ALIGN                8
-+#define ADM_MAX_XFER          (SZ_64K-1)
-+#define ADM_MAX_ROWS          (SZ_64K-1)
-+#define ADM_MAX_CHANNELS      16
-+
-+struct adm_desc_hw_box {
-+      u32 cmd;
-+      u32 src_addr;
-+      u32 dst_addr;
-+      u32 row_len;
-+      u32 num_rows;
-+      u32 row_offset;
-+};
-+
-+struct adm_desc_hw_single {
-+      u32 cmd;
-+      u32 src_addr;
-+      u32 dst_addr;
-+      u32 len;
-+};
-+
-+struct adm_async_desc {
-+      struct virt_dma_desc vd;
-+      struct adm_device *adev;
-+
-+      size_t length;
-+      enum dma_transfer_direction dir;
-+      dma_addr_t dma_addr;
-+      size_t dma_len;
-+
-+      void *cpl;
-+      dma_addr_t cp_addr;
-+      u32 crci;
-+      u32 mux;
-+      u32 blk_size;
-+};
-+
-+struct adm_chan {
-+      struct virt_dma_chan vc;
-+      struct adm_device *adev;
-+
-+      /* parsed from DT */
-+      u32 id;                 /* channel id */
-+
-+      struct adm_async_desc *curr_txd;
-+      struct dma_slave_config slave;
-+      struct list_head node;
-+
-+      int error;
-+      int initialized;
-+};
-+
-+static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
-+{
-+      return container_of(common, struct adm_chan, vc.chan);
-+}
-+
-+struct adm_device {
-+      void __iomem *regs;
-+      struct device *dev;
-+      struct dma_device common;
-+      struct device_dma_parameters dma_parms;
-+      struct adm_chan *channels;
-+
-+      u32 ee;
-+
-+      struct clk *core_clk;
-+      struct clk *iface_clk;
-+
-+      struct reset_control *clk_reset;
-+      struct reset_control *c0_reset;
-+      struct reset_control *c1_reset;
-+      struct reset_control *c2_reset;
-+      int irq;
-+};
-+
-+/**
-+ * adm_free_chan - Frees dma resources associated with the specific channel
-+ *
-+ * Free all allocated descriptors associated with this channel
-+ *
-+ */
-+static void adm_free_chan(struct dma_chan *chan)
-+{
-+      /* free all queued descriptors */
-+      vchan_free_chan_resources(to_virt_chan(chan));
-+}
-+
-+/**
-+ * adm_get_blksize - Get block size from burst value
-+ *
-+ */
-+static int adm_get_blksize(unsigned int burst)
-+{
-+      int ret;
-+
-+      switch (burst) {
-+      case 16:
-+      case 32:
-+      case 64:
-+      case 128:
-+              ret = ffs(burst>>4) - 1;
-+              break;
-+      case 192:
-+              ret = 4;
-+              break;
-+      case 256:
-+              ret = 5;
-+              break;
-+      default:
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+/**
-+ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
-+ *
-+ * @achan: ADM channel
-+ * @desc: Descriptor memory pointer
-+ * @sg: Scatterlist entry
-+ * @crci: CRCI value
-+ * @burst: Burst size of transaction
-+ * @direction: DMA transfer direction
-+ */
-+static void *adm_process_fc_descriptors(struct adm_chan *achan,
-+      void *desc, struct scatterlist *sg, u32 crci, u32 burst,
-+      enum dma_transfer_direction direction)
-+{
-+      struct adm_desc_hw_box *box_desc = NULL;
-+      struct adm_desc_hw_single *single_desc;
-+      u32 remainder = sg_dma_len(sg);
-+      u32 rows, row_offset, crci_cmd;
-+      u32 mem_addr = sg_dma_address(sg);
-+      u32 *incr_addr = &mem_addr;
-+      u32 *src, *dst;
-+
-+      if (direction == DMA_DEV_TO_MEM) {
-+              crci_cmd = ADM_CMD_SRC_CRCI(crci);
-+              row_offset = burst;
-+              src = &achan->slave.src_addr;
-+              dst = &mem_addr;
-+      } else {
-+              crci_cmd = ADM_CMD_DST_CRCI(crci);
-+              row_offset = burst << 16;
-+              src = &mem_addr;
-+              dst = &achan->slave.dst_addr;
-+      }
-+
-+      while (remainder >= burst) {
-+              box_desc = desc;
-+              box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
-+              box_desc->row_offset = row_offset;
-+              box_desc->src_addr = *src;
-+              box_desc->dst_addr = *dst;
-+
-+              rows = remainder / burst;
-+              rows = min_t(u32, rows, ADM_MAX_ROWS);
-+              box_desc->num_rows = rows << 16 | rows;
-+              box_desc->row_len = burst << 16 | burst;
-+
-+              *incr_addr += burst * rows;
-+              remainder -= burst * rows;
-+              desc += sizeof(*box_desc);
-+      }
-+
-+      /* if leftover bytes, do one single descriptor */
-+      if (remainder) {
-+              single_desc = desc;
-+              single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
-+              single_desc->len = remainder;
-+              single_desc->src_addr = *src;
-+              single_desc->dst_addr = *dst;
-+              desc += sizeof(*single_desc);
-+
-+              if (sg_is_last(sg))
-+                      single_desc->cmd |= ADM_CMD_LC;
-+      } else {
-+              if (box_desc && sg_is_last(sg))
-+                      box_desc->cmd |= ADM_CMD_LC;
-+      }
-+
-+      return desc;
-+}
-+
-+/**
-+ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
-+ *
-+ * @achan: ADM channel
-+ * @desc: Descriptor memory pointer
-+ * @sg: Scatterlist entry
-+ * @direction: DMA transfer direction
-+ */
-+static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
-+      void *desc, struct scatterlist *sg,
-+      enum dma_transfer_direction direction)
-+{
-+      struct adm_desc_hw_single *single_desc;
-+      u32 remainder = sg_dma_len(sg);
-+      u32 mem_addr = sg_dma_address(sg);
-+      u32 *incr_addr = &mem_addr;
-+      u32 *src, *dst;
-+
-+      if (direction == DMA_DEV_TO_MEM) {
-+              src = &achan->slave.src_addr;
-+              dst = &mem_addr;
-+      } else {
-+              src = &mem_addr;
-+              dst = &achan->slave.dst_addr;
-+      }
-+
-+      do {
-+              single_desc = desc;
-+              single_desc->cmd = ADM_CMD_TYPE_SINGLE;
-+              single_desc->src_addr = *src;
-+              single_desc->dst_addr = *dst;
-+              single_desc->len = (remainder > ADM_MAX_XFER) ?
-+                              ADM_MAX_XFER : remainder;
-+
-+              remainder -= single_desc->len;
-+              *incr_addr += single_desc->len;
-+              desc += sizeof(*single_desc);
-+      } while (remainder);
-+
-+      /* set last command if this is the end of the whole transaction */
-+      if (sg_is_last(sg))
-+              single_desc->cmd |= ADM_CMD_LC;
-+
-+      return desc;
-+}
-+
-+/**
-+ * adm_prep_slave_sg - Prep slave sg transaction
-+ *
-+ * @chan: dma channel
-+ * @sgl: scatter gather list
-+ * @sg_len: length of sg
-+ * @direction: DMA transfer direction
-+ * @flags: DMA flags
-+ * @context: transfer context (unused)
-+ */
-+static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
-+      struct scatterlist *sgl, unsigned int sg_len,
-+      enum dma_transfer_direction direction, unsigned long flags,
-+      void *context)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct adm_device *adev = achan->adev;
-+      struct adm_async_desc *async_desc;
-+      struct scatterlist *sg;
-+      dma_addr_t cple_addr;
-+      u32 i, burst;
-+      u32 single_count = 0, box_count = 0, crci = 0;
-+      void *desc;
-+      u32 *cple;
-+      int blk_size = 0;
-+
-+      if (!is_slave_direction(direction)) {
-+              dev_err(adev->dev, "invalid dma direction\n");
-+              return NULL;
-+      }
-+
-+      /*
-+       * get burst value from slave configuration
-+       */
-+      burst = (direction == DMA_MEM_TO_DEV) ?
-+              achan->slave.dst_maxburst :
-+              achan->slave.src_maxburst;
-+
-+      /* if using flow control, validate burst and crci values */
-+      if (achan->slave.device_fc) {
-+
-+              blk_size = adm_get_blksize(burst);
-+              if (blk_size < 0) {
-+                      dev_err(adev->dev, "invalid burst value: %d\n",
-+                              burst);
-+                      return ERR_PTR(-EINVAL);
-+              }
-+
-+              crci = achan->slave.slave_id & 0xf;
-+              if (!crci || achan->slave.slave_id > 0x1f) {
-+                      dev_err(adev->dev, "invalid crci value\n");
-+                      return ERR_PTR(-EINVAL);
-+              }
-+      }
-+
-+      /* iterate through sgs and compute allocation size of structures */
-+      for_each_sg(sgl, sg, sg_len, i) {
-+              if (achan->slave.device_fc) {
-+                      box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
-+                                                ADM_MAX_ROWS);
-+                      if (sg_dma_len(sg) % burst)
-+                              single_count++;
-+              } else {
-+                      single_count += DIV_ROUND_UP(sg_dma_len(sg),
-+                                                   ADM_MAX_XFER);
-+              }
-+      }
-+
-+      async_desc = kzalloc(sizeof(*async_desc), GFP_ATOMIC);
-+      if (!async_desc)
-+              return ERR_PTR(-ENOMEM);
-+
-+      if (crci)
-+              async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
-+                                      ADM_CRCI_CTL_MUX_SEL : 0;
-+      async_desc->crci = crci;
-+      async_desc->blk_size = blk_size;
-+      async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
-+                              box_count * sizeof(struct adm_desc_hw_box) +
-+                              sizeof(*cple) + 2 * ADM_DESC_ALIGN;
-+
-+      async_desc->cpl = kzalloc(async_desc->dma_len, GFP_ATOMIC);
-+      if (!async_desc->cpl)
-+              goto free;
-+
-+      async_desc->adev = adev;
-+
-+      /* both command list entry and descriptors must be 8 byte aligned */
-+      cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
-+      desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
-+
-+      for_each_sg(sgl, sg, sg_len, i) {
-+              async_desc->length += sg_dma_len(sg);
-+
-+              if (achan->slave.device_fc)
-+                      desc = adm_process_fc_descriptors(achan, desc, sg, crci,
-+                                                      burst, direction);
-+              else
-+                      desc = adm_process_non_fc_descriptors(achan, desc, sg,
-+                                                         direction);
-+      }
-+
-+      async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl,
-+                                            async_desc->dma_len,
-+                                            DMA_TO_DEVICE);
-+      if (dma_mapping_error(adev->dev, async_desc->dma_addr))
-+              goto free;
-+
-+      cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl);
-+
-+      /* init cmd list */
-+      dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple),
-+                              DMA_TO_DEVICE);
-+      *cple = ADM_CPLE_LP;
-+      *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3;
-+      dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple),
-+                                 DMA_TO_DEVICE);
-+
-+      return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
-+
-+free:
-+      kfree(async_desc);
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+/**
-+ * adm_terminate_all - terminate all transactions on a channel
-+ * @achan: adm dma channel
-+ *
-+ * Dequeues and frees all transactions, aborts current transaction
-+ * No callbacks are done
-+ *
-+ */
-+static int adm_terminate_all(struct dma_chan *chan)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct adm_device *adev = achan->adev;
-+      unsigned long flags;
-+      LIST_HEAD(head);
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+      vchan_get_all_descriptors(&achan->vc, &head);
-+
-+      /* send flush command to terminate current transaction */
-+      writel_relaxed(0x0,
-+              adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
-+
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+
-+      vchan_dma_desc_free_list(&achan->vc, &head);
-+
-+      return 0;
-+}
-+
-+static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      unsigned long flag;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flag);
-+      memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
-+      spin_unlock_irqrestore(&achan->vc.lock, flag);
-+
-+      return 0;
-+}
-+
-+/**
-+ * adm_start_dma - start next transaction
-+ * @achan - ADM dma channel
-+ */
-+static void adm_start_dma(struct adm_chan *achan)
-+{
-+      struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
-+      struct adm_device *adev = achan->adev;
-+      struct adm_async_desc *async_desc;
-+
-+      lockdep_assert_held(&achan->vc.lock);
-+
-+      if (!vd)
-+              return;
-+
-+      list_del(&vd->node);
-+
-+      /* write next command list out to the CMD FIFO */
-+      async_desc = container_of(vd, struct adm_async_desc, vd);
-+      achan->curr_txd = async_desc;
-+
-+      /* reset channel error */
-+      achan->error = 0;
-+
-+      if (!achan->initialized) {
-+              /* enable interrupts */
-+              writel(ADM_CH_CONF_SHADOW_EN |
-+                     ADM_CH_CONF_PERM_MPU_CONF |
-+                     ADM_CH_CONF_MPU_DISABLE |
-+                     ADM_CH_CONF_SEC_DOMAIN(adev->ee),
-+                     adev->regs + ADM_CH_CONF(achan->id));
-+
-+              writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
-+                      adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
-+
-+              achan->initialized = 1;
-+      }
-+
-+      /* set the crci block size if this transaction requires CRCI */
-+      if (async_desc->crci) {
-+              writel(async_desc->mux | async_desc->blk_size,
-+                      adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
-+      }
-+
-+      /* make sure IRQ enable doesn't get reordered */
-+      wmb();
-+
-+      /* write next command list out to the CMD FIFO */
-+      writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
-+              adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
-+}
-+
-+/**
-+ * adm_dma_irq - irq handler for ADM controller
-+ * @irq: IRQ of interrupt
-+ * @data: callback data
-+ *
-+ * IRQ handler for the bam controller
-+ */
-+static irqreturn_t adm_dma_irq(int irq, void *data)
-+{
-+      struct adm_device *adev = data;
-+      u32 srcs, i;
-+      struct adm_async_desc *async_desc;
-+      unsigned long flags;
-+
-+      srcs = readl_relaxed(adev->regs +
-+                      ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
-+              struct adm_chan *achan = &adev->channels[i];
-+              u32 status, result;
-+
-+              if (srcs & BIT(i)) {
-+                      status = readl_relaxed(adev->regs +
-+                              ADM_CH_STATUS_SD(i, adev->ee));
-+
-+                      /* if no result present, skip */
-+                      if (!(status & ADM_CH_STATUS_VALID))
-+                              continue;
-+
-+                      result = readl_relaxed(adev->regs +
-+                              ADM_CH_RSLT(i, adev->ee));
-+
-+                      /* no valid results, skip */
-+                      if (!(result & ADM_CH_RSLT_VALID))
-+                              continue;
-+
-+                      /* flag error if transaction was flushed or failed */
-+                      if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
-+                              achan->error = 1;
-+
-+                      spin_lock_irqsave(&achan->vc.lock, flags);
-+                      async_desc = achan->curr_txd;
-+
-+                      achan->curr_txd = NULL;
-+
-+                      if (async_desc) {
-+                              vchan_cookie_complete(&async_desc->vd);
-+
-+                              /* kick off next DMA */
-+                              adm_start_dma(achan);
-+                      }
-+
-+                      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+              }
-+      }
-+
-+      return IRQ_HANDLED;
-+}
-+
-+/**
-+ * adm_tx_status - returns status of transaction
-+ * @chan: dma channel
-+ * @cookie: transaction cookie
-+ * @txstate: DMA transaction state
-+ *
-+ * Return status of dma transaction
-+ */
-+static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
-+      struct dma_tx_state *txstate)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      struct virt_dma_desc *vd;
-+      enum dma_status ret;
-+      unsigned long flags;
-+      size_t residue = 0;
-+
-+      ret = dma_cookie_status(chan, cookie, txstate);
-+      if (ret == DMA_COMPLETE || !txstate)
-+              return ret;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+
-+      vd = vchan_find_desc(&achan->vc, cookie);
-+      if (vd)
-+              residue = container_of(vd, struct adm_async_desc, vd)->length;
-+
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+
-+      /*
-+       * residue is either the full length if it is in the issued list, or 0
-+       * if it is in progress.  We have no reliable way of determining
-+       * anything inbetween
-+      */
-+      dma_set_residue(txstate, residue);
-+
-+      if (achan->error)
-+              return DMA_ERROR;
-+
-+      return ret;
-+}
-+
-+/**
-+ * adm_issue_pending - starts pending transactions
-+ * @chan: dma channel
-+ *
-+ * Issues all pending transactions and starts DMA
-+ */
-+static void adm_issue_pending(struct dma_chan *chan)
-+{
-+      struct adm_chan *achan = to_adm_chan(chan);
-+      unsigned long flags;
-+
-+      spin_lock_irqsave(&achan->vc.lock, flags);
-+
-+      if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
-+              adm_start_dma(achan);
-+      spin_unlock_irqrestore(&achan->vc.lock, flags);
-+}
-+
-+/**
-+ * adm_dma_free_desc - free descriptor memory
-+ * @vd: virtual descriptor
-+ *
-+ */
-+static void adm_dma_free_desc(struct virt_dma_desc *vd)
-+{
-+      struct adm_async_desc *async_desc = container_of(vd,
-+                      struct adm_async_desc, vd);
-+
-+      dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr,
-+                       async_desc->dma_len, DMA_TO_DEVICE);
-+      kfree(async_desc->cpl);
-+      kfree(async_desc);
-+}
-+
-+static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
-+      u32 index)
-+{
-+      achan->id = index;
-+      achan->adev = adev;
-+
-+      vchan_init(&achan->vc, &adev->common);
-+      achan->vc.desc_free = adm_dma_free_desc;
-+}
-+
-+static int adm_dma_probe(struct platform_device *pdev)
-+{
-+      struct adm_device *adev;
-+      struct resource *iores;
-+      int ret;
-+      u32 i;
-+
-+      adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
-+      if (!adev)
-+              return -ENOMEM;
-+
-+      adev->dev = &pdev->dev;
-+
-+      iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      adev->regs = devm_ioremap_resource(&pdev->dev, iores);
-+      if (IS_ERR(adev->regs))
-+              return PTR_ERR(adev->regs);
-+
-+      adev->irq = platform_get_irq(pdev, 0);
-+      if (adev->irq < 0)
-+              return adev->irq;
-+
-+      ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
-+      if (ret) {
-+              dev_err(adev->dev, "Execution environment unspecified\n");
-+              return ret;
-+      }
-+
-+      adev->core_clk = devm_clk_get(adev->dev, "core");
-+      if (IS_ERR(adev->core_clk))
-+              return PTR_ERR(adev->core_clk);
-+
-+      ret = clk_prepare_enable(adev->core_clk);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to prepare/enable core clock\n");
-+              return ret;
-+      }
-+
-+      adev->iface_clk = devm_clk_get(adev->dev, "iface");
-+      if (IS_ERR(adev->iface_clk)) {
-+              ret = PTR_ERR(adev->iface_clk);
-+              goto err_disable_core_clk;
-+      }
-+
-+      ret = clk_prepare_enable(adev->iface_clk);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to prepare/enable iface clock\n");
-+              goto err_disable_core_clk;
-+      }
-+
-+      adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk");
-+      if (IS_ERR(adev->clk_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 reset\n");
-+              ret = PTR_ERR(adev->clk_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0");
-+      if (IS_ERR(adev->c0_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
-+              ret = PTR_ERR(adev->c0_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1");
-+      if (IS_ERR(adev->c1_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
-+              ret = PTR_ERR(adev->c1_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2");
-+      if (IS_ERR(adev->c2_reset)) {
-+              dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
-+              ret = PTR_ERR(adev->c2_reset);
-+              goto err_disable_clks;
-+      }
-+
-+      reset_control_assert(adev->clk_reset);
-+      reset_control_assert(adev->c0_reset);
-+      reset_control_assert(adev->c1_reset);
-+      reset_control_assert(adev->c2_reset);
-+
-+      reset_control_deassert(adev->clk_reset);
-+      reset_control_deassert(adev->c0_reset);
-+      reset_control_deassert(adev->c1_reset);
-+      reset_control_deassert(adev->c2_reset);
-+
-+      adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
-+                              sizeof(*adev->channels), GFP_KERNEL);
-+
-+      if (!adev->channels) {
-+              ret = -ENOMEM;
-+              goto err_disable_clks;
-+      }
-+
-+      /* allocate and initialize channels */
-+      INIT_LIST_HEAD(&adev->common.channels);
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++)
-+              adm_channel_init(adev, &adev->channels[i], i);
-+
-+      /* reset CRCIs */
-+      for (i = 0; i < 16; i++)
-+              writel(ADM_CRCI_CTL_RST, adev->regs +
-+                      ADM_CRCI_CTL(i, adev->ee));
-+
-+      /* configure client interfaces */
-+      writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
-+      writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
-+      writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
-+              ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
-+      writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
-+              adev->regs + ADM_GP_CTL);
-+
-+      ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
-+                      0, "adm_dma", adev);
-+      if (ret)
-+              goto err_disable_clks;
-+
-+      platform_set_drvdata(pdev, adev);
-+
-+      adev->common.dev = adev->dev;
-+      adev->common.dev->dma_parms = &adev->dma_parms;
-+
-+      /* set capabilities */
-+      dma_cap_zero(adev->common.cap_mask);
-+      dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
-+      dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
-+
-+      /* initialize dmaengine apis */
-+      adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
-+      adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
-+      adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
-+      adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
-+      adev->common.device_free_chan_resources = adm_free_chan;
-+      adev->common.device_prep_slave_sg = adm_prep_slave_sg;
-+      adev->common.device_issue_pending = adm_issue_pending;
-+      adev->common.device_tx_status = adm_tx_status;
-+      adev->common.device_terminate_all = adm_terminate_all;
-+      adev->common.device_config = adm_slave_config;
-+
-+      ret = dma_async_device_register(&adev->common);
-+      if (ret) {
-+              dev_err(adev->dev, "failed to register dma async device\n");
-+              goto err_disable_clks;
-+      }
-+
-+      ret = of_dma_controller_register(pdev->dev.of_node,
-+                                       of_dma_xlate_by_chan_id,
-+                                       &adev->common);
-+      if (ret)
-+              goto err_unregister_dma;
-+
-+      return 0;
-+
-+err_unregister_dma:
-+      dma_async_device_unregister(&adev->common);
-+err_disable_clks:
-+      clk_disable_unprepare(adev->iface_clk);
-+err_disable_core_clk:
-+      clk_disable_unprepare(adev->core_clk);
-+
-+      return ret;
-+}
-+
-+static int adm_dma_remove(struct platform_device *pdev)
-+{
-+      struct adm_device *adev = platform_get_drvdata(pdev);
-+      struct adm_chan *achan;
-+      u32 i;
-+
-+      of_dma_controller_free(pdev->dev.of_node);
-+      dma_async_device_unregister(&adev->common);
-+
-+      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
-+              achan = &adev->channels[i];
-+
-+              /* mask IRQs for this channel/EE pair */
-+              writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
-+
-+              adm_terminate_all(&adev->channels[i].vc.chan);
-+      }
-+
-+      devm_free_irq(adev->dev, adev->irq, adev);
-+
-+      clk_disable_unprepare(adev->core_clk);
-+      clk_disable_unprepare(adev->iface_clk);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id adm_of_match[] = {
-+      { .compatible = "qcom,adm", },
-+      {}
-+};
-+MODULE_DEVICE_TABLE(of, adm_of_match);
-+
-+static struct platform_driver adm_dma_driver = {
-+      .probe = adm_dma_probe,
-+      .remove = adm_dma_remove,
-+      .driver = {
-+              .name = "adm-dma-engine",
-+              .of_match_table = adm_of_match,
-+      },
-+};
-+
-+module_platform_driver(adm_dma_driver);
-+
-+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
-+MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
-+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch
deleted file mode 100644 (file)
index 6b7119b..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001
-From: Mathieu Olivari <mathieu@codeaurora.org>
-Date: Thu, 9 Mar 2017 08:18:08 +0100
-Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms
-
-On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
-used to store partition layout. This new parser can now be used to read
-SMEM and use it to register an MTD layout according to its content.
-
-Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
-Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
----
- drivers/mtd/parsers/Kconfig          |   7 ++
- drivers/mtd/parsers/Makefile         |   1 +
- drivers/mtd/parsers/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 236 insertions(+)
- create mode 100644 drivers/mtd/parsers/qcom_smem_part.c
-
---- a/drivers/mtd/parsers/Kconfig
-+++ b/drivers/mtd/parsers/Kconfig
-@@ -138,6 +138,13 @@ config MTD_PARSER_TRX
-         This driver will parse TRX header and report at least two partitions:
-         kernel and rootfs.
-+config MTD_QCOM_SMEM_PARTS
-+      tristate "QCOM SMEM partitioning support"
-+      depends on QCOM_SMEM
-+      help
-+        This provides partitions parser for QCOM devices using SMEM
-+        such as IPQ806x.
-+
- config MTD_SHARPSL_PARTS
-       tristate "Sharp SL Series NAND flash partition parser"
-       depends on MTD_NAND_SHARPSL || MTD_NAND_TMIO || COMPILE_TEST
---- a/drivers/mtd/parsers/Makefile
-+++ b/drivers/mtd/parsers/Makefile
-@@ -11,6 +11,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)
- obj-$(CONFIG_MTD_PARSER_IMAGETAG)     += parser_imagetag.o
- obj-$(CONFIG_MTD_AFS_PARTS)           += afs.o
- obj-$(CONFIG_MTD_PARSER_TRX)          += parser_trx.o
-+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS)             += qcom_smem_part.o
- obj-$(CONFIG_MTD_SHARPSL_PARTS)               += sharpslpart.o
- obj-$(CONFIG_MTD_REDBOOT_PARTS)               += redboot.o
- obj-$(CONFIG_MTD_ROUTERBOOT_PARTS)            += routerbootpart.o
---- /dev/null
-+++ b/drivers/mtd/parsers/qcom_smem_part.c
-@@ -0,0 +1,235 @@
-+/*
-+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 and
-+ * only version 2 as published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/device.h>
-+#include <linux/slab.h>
-+
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/spi/spi.h>
-+#include <linux/module.h>
-+
-+#include <linux/soc/qcom/smem.h>
-+
-+/* Processor/host identifier for the application processor */
-+#define SMEM_HOST_APPS                        0
-+
-+/* SMEM items index */
-+#define SMEM_AARM_PARTITION_TABLE     9
-+#define SMEM_BOOT_FLASH_TYPE          421
-+#define SMEM_BOOT_FLASH_BLOCK_SIZE    424
-+
-+/* SMEM Flash types */
-+#define SMEM_FLASH_NAND                       2
-+#define SMEM_FLASH_SPI                        6
-+
-+#define SMEM_PART_NAME_SZ             16
-+#define SMEM_PARTS_MAX                        32
-+
-+struct smem_partition {
-+      char name[SMEM_PART_NAME_SZ];
-+      __le32 start;
-+      __le32 size;
-+      __le32 attr;
-+};
-+
-+struct smem_partition_table {
-+      u8 magic[8];
-+      __le32 version;
-+      __le32 len;
-+      struct smem_partition parts[SMEM_PARTS_MAX];
-+};
-+
-+/* SMEM Magic values in partition table */
-+static const u8 SMEM_PTABLE_MAGIC[] = {
-+      0xaa, 0x73, 0xee, 0x55,
-+      0xdb, 0xbd, 0x5e, 0xe3,
-+};
-+
-+static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
-+{
-+      size_t size;
-+
-+      *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
-+                                  &size);
-+
-+      if (IS_ERR(*smem_blksz)) {
-+              pr_err("Unable to read flash blksz from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      if (size != sizeof(**smem_blksz)) {
-+              pr_err("Invalid flash blksz size in SMEM\n");
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int qcom_smem_get_flash_type(u64 **smem_flash_type)
-+{
-+      size_t size;
-+
-+      *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
-+                                      &size);
-+
-+      if (IS_ERR(*smem_flash_type)) {
-+              pr_err("Unable to read flash type from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      if (size != sizeof(**smem_flash_type)) {
-+              pr_err("Invalid flash type size in SMEM\n");
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
-+{
-+      size_t size;
-+
-+      *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
-+                              &size);
-+
-+      if (IS_ERR(*pparts)) {
-+              pr_err("Unable to read partition table from SMEM\n");
-+              return -ENOENT;
-+      }
-+
-+      return 0;
-+}
-+
-+static int of_dev_node_match(struct device *dev, const void *data)
-+{
-+      return dev->of_node == data;
-+}
-+
-+static bool is_spi_device(struct device_node *np)
-+{
-+      struct device *dev;
-+
-+      dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
-+      if (!dev)
-+              return false;
-+
-+      put_device(dev);
-+      return true;
-+}
-+
-+static int parse_qcom_smem_partitions(struct mtd_info *master,
-+                                    const struct mtd_partition **pparts,
-+                                    struct mtd_part_parser_data *data)
-+{
-+      struct smem_partition_table *smem_parts;
-+      u64 *smem_flash_type, *smem_blksz;
-+      struct mtd_partition *mtd_parts;
-+      struct device_node *of_node = master->dev.of_node;
-+      int i, ret;
-+
-+      /*
-+       * SMEM will only store the partition table of the boot device.
-+       * If this is not the boot device, do not return any partition.
-+       */
-+      ret = qcom_smem_get_flash_type(&smem_flash_type);
-+      if (ret < 0)
-+              return ret;
-+
-+      if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
-+          || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
-+              return 0;
-+
-+      /*
-+       * Just for sanity purpose, make sure the block size in SMEM matches the
-+       * block size of the MTD device
-+       */
-+      ret = qcom_smem_get_flash_blksz(&smem_blksz);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (*smem_blksz != master->erasesize) {
-+              pr_err("SMEM block size differs from MTD block size\n");
-+              return -EINVAL;
-+      }
-+
-+      /* Get partition pointer from SMEM */
-+      ret = qcom_smem_get_flash_partitions(&smem_parts);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
-+                 sizeof(SMEM_PTABLE_MAGIC))) {
-+              pr_err("SMEM partition magic invalid\n");
-+              return -EINVAL;
-+      }
-+
-+      /* Allocate and populate the mtd structures */
-+      mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
-+                          GFP_KERNEL);
-+      if (!mtd_parts)
-+              return -ENOMEM;
-+
-+      for (i = 0; i < smem_parts->len; i++) {
-+              struct smem_partition *s_part = &smem_parts->parts[i];
-+              struct mtd_partition *m_part = &mtd_parts[i];
-+
-+              m_part->name = s_part->name;
-+              m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
-+              m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
-+
-+              /*
-+               * The last SMEM partition may have its size marked as
-+               * something like 0xffffffff, which means "until the end of the
-+               * flash device". In this case, truncate it.
-+               */
-+              if (m_part->offset + m_part->size > master->size)
-+                      m_part->size = master->size - m_part->offset;
-+      }
-+
-+      *pparts = mtd_parts;
-+
-+      return smem_parts->len;
-+}
-+
-+static const struct of_device_id qcom_smem_of_match_table[] = {
-+      { .compatible = "qcom,smem" },
-+      {},
-+};
-+MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table);
-+
-+static struct mtd_part_parser qcom_smem_parser = {
-+      .owner = THIS_MODULE,
-+      .parse_fn = parse_qcom_smem_partitions,
-+      .name = "qcom-smem",
-+      .of_match_table = qcom_smem_of_match_table,
-+};
-+
-+static int __init qcom_smem_parser_init(void)
-+{
-+      register_mtd_parser(&qcom_smem_parser);
-+      return 0;
-+}
-+
-+static void __exit qcom_smem_parser_exit(void)
-+{
-+      deregister_mtd_parser(&qcom_smem_parser);
-+}
-+
-+module_init(qcom_smem_parser_init);
-+module_exit(qcom_smem_parser_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
-+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
diff --git a/target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch
deleted file mode 100644 (file)
index ff6f6ed..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
-From: John Crispin <john@phrozen.org>
-Date: Thu, 9 Mar 2017 09:31:44 +0100
-Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
-
-Signed-off-by: John Crispin <john@phrozen.org>
----
- drivers/mtd/parsers/qcom_smem_part.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/drivers/mtd/parsers/qcom_smem_part.c
-+++ b/drivers/mtd/parsers/qcom_smem_part.c
-@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st
-               m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
-               m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
-+              /* "rootfs" conflicts with OpenWrt auto mounting */
-+              if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
-+                      m_part->name = "ubi";
-+
-               /*
-                * The last SMEM partition may have its size marked as
-                * something like 0xffffffff, which means "until the end of the
diff --git a/target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch b/target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch
new file mode 100644 (file)
index 0000000..45062d5
--- /dev/null
@@ -0,0 +1,975 @@
+From 5c9f8c2dbdbe53818bcde6aa6695e1331e5f841f Mon Sep 17 00:00:00 2001
+From: Jonathan McDowell <noodles@earth.li>
+Date: Sat, 14 Nov 2020 14:02:33 +0000
+Subject: dmaengine: qcom: Add ADM driver
+
+Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
+controller found in the MSM8x60 and IPQ/APQ8064 platforms.
+
+The ADM supports both memory to memory transactions and memory
+to/from peripheral device transactions.  The controller also provides
+flow control capabilities for transactions to/from peripheral devices.
+
+The initial release of this driver supports slave transfers to/from
+peripherals and also incorporates CRCI (client rate control interface)
+flow control.
+
+The hardware only supports a 32 bit physical address, so specifying
+!PHYS_ADDR_T_64BIT gives maximum COMPILE_TEST coverage without having to
+spend effort on kludging things in the code that will never actually be
+needed on real hardware.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
+Signed-off-by: Jonathan McDowell <noodles@earth.li>
+Link: https://lore.kernel.org/r/20201114140233.GM32650@earth.li
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ drivers/dma/qcom/Kconfig    |  11 +
+ drivers/dma/qcom/Makefile   |   1 +
+ drivers/dma/qcom/qcom_adm.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 915 insertions(+)
+ create mode 100644 drivers/dma/qcom/qcom_adm.c
+
+diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig
+index 3bcb689162c67..0389d60d2604a 100644
+--- a/drivers/dma/qcom/Kconfig
++++ b/drivers/dma/qcom/Kconfig
+@@ -1,4 +1,15 @@
+ # SPDX-License-Identifier: GPL-2.0-only
++config QCOM_ADM
++      tristate "Qualcomm ADM support"
++      depends on (ARCH_QCOM || COMPILE_TEST) && !PHYS_ADDR_T_64BIT
++      select DMA_ENGINE
++      select DMA_VIRTUAL_CHANNELS
++      help
++        Enable support for the Qualcomm Application Data Mover (ADM) DMA
++        controller, as present on MSM8x60, APQ8064, and IPQ8064 devices.
++        This controller provides DMA capabilities for both general purpose
++        and on-chip peripheral devices.
++
+ config QCOM_BAM_DMA
+       tristate "QCOM BAM DMA support"
+       depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
+diff --git a/drivers/dma/qcom/Makefile b/drivers/dma/qcom/Makefile
+index 1ae92da88b0c9..346e643fbb6db 100644
+--- a/drivers/dma/qcom/Makefile
++++ b/drivers/dma/qcom/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
+ obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o
+ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o
+ hdma_mgmt-objs         := hidma_mgmt.o hidma_mgmt_sys.o
+diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c
+new file mode 100644
+index 0000000000000..9b6f8e050ecce
+--- /dev/null
++++ b/drivers/dma/qcom/qcom_adm.c
+@@ -0,0 +1,903 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_dma.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++
++#include "../dmaengine.h"
++#include "../virt-dma.h"
++
++/* ADM registers - calculated from channel number and security domain */
++#define ADM_CHAN_MULTI                        0x4
++#define ADM_CI_MULTI                  0x4
++#define ADM_CRCI_MULTI                        0x4
++#define ADM_EE_MULTI                  0x800
++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * (chan))
++#define ADM_EE_OFFS(ee)                       (ADM_EE_MULTI * (ee))
++#define ADM_CHAN_EE_OFFS(chan, ee)    (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
++#define ADM_CHAN_OFFS(chan)           (ADM_CHAN_MULTI * (chan))
++#define ADM_CI_OFFS(ci)                       (ADM_CHAN_OFF(ci))
++#define ADM_CH_CMD_PTR(chan, ee)      (ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_RSLT(chan, ee)         (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_STATUS_SD(chan, ee)    (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_CH_CONF(chan)             (0x240 + ADM_CHAN_OFFS(chan))
++#define ADM_CH_RSLT_CONF(chan, ee)    (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
++#define ADM_CI_CONF(ci)                       (0x390 + (ci) * ADM_CI_MULTI)
++#define ADM_GP_CTL                    0x3d8
++#define ADM_CRCI_CTL(crci, ee)                (0x400 + (crci) * ADM_CRCI_MULTI + \
++                                              ADM_EE_OFFS(ee))
++
++/* channel status */
++#define ADM_CH_STATUS_VALID           BIT(1)
++
++/* channel result */
++#define ADM_CH_RSLT_VALID             BIT(31)
++#define ADM_CH_RSLT_ERR                       BIT(3)
++#define ADM_CH_RSLT_FLUSH             BIT(2)
++#define ADM_CH_RSLT_TPD                       BIT(1)
++
++/* channel conf */
++#define ADM_CH_CONF_SHADOW_EN         BIT(12)
++#define ADM_CH_CONF_MPU_DISABLE               BIT(11)
++#define ADM_CH_CONF_PERM_MPU_CONF     BIT(9)
++#define ADM_CH_CONF_FORCE_RSLT_EN     BIT(7)
++#define ADM_CH_CONF_SEC_DOMAIN(ee)    ((((ee) & 0x3) << 4) | (((ee) & 0x4) << 11))
++
++/* channel result conf */
++#define ADM_CH_RSLT_CONF_FLUSH_EN     BIT(1)
++#define ADM_CH_RSLT_CONF_IRQ_EN               BIT(0)
++
++/* CRCI CTL */
++#define ADM_CRCI_CTL_MUX_SEL          BIT(18)
++#define ADM_CRCI_CTL_RST              BIT(17)
++
++/* CI configuration */
++#define ADM_CI_RANGE_END(x)           ((x) << 24)
++#define ADM_CI_RANGE_START(x)         ((x) << 16)
++#define ADM_CI_BURST_4_WORDS          BIT(2)
++#define ADM_CI_BURST_8_WORDS          BIT(3)
++
++/* GP CTL */
++#define ADM_GP_CTL_LP_EN              BIT(12)
++#define ADM_GP_CTL_LP_CNT(x)          ((x) << 8)
++
++/* Command pointer list entry */
++#define ADM_CPLE_LP                   BIT(31)
++#define ADM_CPLE_CMD_PTR_LIST         BIT(29)
++
++/* Command list entry */
++#define ADM_CMD_LC                    BIT(31)
++#define ADM_CMD_DST_CRCI(n)           (((n) & 0xf) << 7)
++#define ADM_CMD_SRC_CRCI(n)           (((n) & 0xf) << 3)
++
++#define ADM_CMD_TYPE_SINGLE           0x0
++#define ADM_CMD_TYPE_BOX              0x3
++
++#define ADM_CRCI_MUX_SEL              BIT(4)
++#define ADM_DESC_ALIGN                        8
++#define ADM_MAX_XFER                  (SZ_64K - 1)
++#define ADM_MAX_ROWS                  (SZ_64K - 1)
++#define ADM_MAX_CHANNELS              16
++
++struct adm_desc_hw_box {
++      u32 cmd;
++      u32 src_addr;
++      u32 dst_addr;
++      u32 row_len;
++      u32 num_rows;
++      u32 row_offset;
++};
++
++struct adm_desc_hw_single {
++      u32 cmd;
++      u32 src_addr;
++      u32 dst_addr;
++      u32 len;
++};
++
++struct adm_async_desc {
++      struct virt_dma_desc vd;
++      struct adm_device *adev;
++
++      size_t length;
++      enum dma_transfer_direction dir;
++      dma_addr_t dma_addr;
++      size_t dma_len;
++
++      void *cpl;
++      dma_addr_t cp_addr;
++      u32 crci;
++      u32 mux;
++      u32 blk_size;
++};
++
++struct adm_chan {
++      struct virt_dma_chan vc;
++      struct adm_device *adev;
++
++      /* parsed from DT */
++      u32 id;                 /* channel id */
++
++      struct adm_async_desc *curr_txd;
++      struct dma_slave_config slave;
++      struct list_head node;
++
++      int error;
++      int initialized;
++};
++
++static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
++{
++      return container_of(common, struct adm_chan, vc.chan);
++}
++
++struct adm_device {
++      void __iomem *regs;
++      struct device *dev;
++      struct dma_device common;
++      struct device_dma_parameters dma_parms;
++      struct adm_chan *channels;
++
++      u32 ee;
++
++      struct clk *core_clk;
++      struct clk *iface_clk;
++
++      struct reset_control *clk_reset;
++      struct reset_control *c0_reset;
++      struct reset_control *c1_reset;
++      struct reset_control *c2_reset;
++      int irq;
++};
++
++/**
++ * adm_free_chan - Frees dma resources associated with the specific channel
++ *
++ * Free all allocated descriptors associated with this channel
++ *
++ */
++static void adm_free_chan(struct dma_chan *chan)
++{
++      /* free all queued descriptors */
++      vchan_free_chan_resources(to_virt_chan(chan));
++}
++
++/**
++ * adm_get_blksize - Get block size from burst value
++ *
++ */
++static int adm_get_blksize(unsigned int burst)
++{
++      int ret;
++
++      switch (burst) {
++      case 16:
++      case 32:
++      case 64:
++      case 128:
++              ret = ffs(burst >> 4) - 1;
++              break;
++      case 192:
++              ret = 4;
++              break;
++      case 256:
++              ret = 5;
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++/**
++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @crci: CRCI value
++ * @burst: Burst size of transaction
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_fc_descriptors(struct adm_chan *achan, void *desc,
++                                      struct scatterlist *sg, u32 crci,
++                                      u32 burst,
++                                      enum dma_transfer_direction direction)
++{
++      struct adm_desc_hw_box *box_desc = NULL;
++      struct adm_desc_hw_single *single_desc;
++      u32 remainder = sg_dma_len(sg);
++      u32 rows, row_offset, crci_cmd;
++      u32 mem_addr = sg_dma_address(sg);
++      u32 *incr_addr = &mem_addr;
++      u32 *src, *dst;
++
++      if (direction == DMA_DEV_TO_MEM) {
++              crci_cmd = ADM_CMD_SRC_CRCI(crci);
++              row_offset = burst;
++              src = &achan->slave.src_addr;
++              dst = &mem_addr;
++      } else {
++              crci_cmd = ADM_CMD_DST_CRCI(crci);
++              row_offset = burst << 16;
++              src = &mem_addr;
++              dst = &achan->slave.dst_addr;
++      }
++
++      while (remainder >= burst) {
++              box_desc = desc;
++              box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
++              box_desc->row_offset = row_offset;
++              box_desc->src_addr = *src;
++              box_desc->dst_addr = *dst;
++
++              rows = remainder / burst;
++              rows = min_t(u32, rows, ADM_MAX_ROWS);
++              box_desc->num_rows = rows << 16 | rows;
++              box_desc->row_len = burst << 16 | burst;
++
++              *incr_addr += burst * rows;
++              remainder -= burst * rows;
++              desc += sizeof(*box_desc);
++      }
++
++      /* if leftover bytes, do one single descriptor */
++      if (remainder) {
++              single_desc = desc;
++              single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
++              single_desc->len = remainder;
++              single_desc->src_addr = *src;
++              single_desc->dst_addr = *dst;
++              desc += sizeof(*single_desc);
++
++              if (sg_is_last(sg))
++                      single_desc->cmd |= ADM_CMD_LC;
++      } else {
++              if (box_desc && sg_is_last(sg))
++                      box_desc->cmd |= ADM_CMD_LC;
++      }
++
++      return desc;
++}
++
++/**
++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
++ *
++ * @achan: ADM channel
++ * @desc: Descriptor memory pointer
++ * @sg: Scatterlist entry
++ * @direction: DMA transfer direction
++ */
++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, void *desc,
++                                          struct scatterlist *sg,
++                                          enum dma_transfer_direction direction)
++{
++      struct adm_desc_hw_single *single_desc;
++      u32 remainder = sg_dma_len(sg);
++      u32 mem_addr = sg_dma_address(sg);
++      u32 *incr_addr = &mem_addr;
++      u32 *src, *dst;
++
++      if (direction == DMA_DEV_TO_MEM) {
++              src = &achan->slave.src_addr;
++              dst = &mem_addr;
++      } else {
++              src = &mem_addr;
++              dst = &achan->slave.dst_addr;
++      }
++
++      do {
++              single_desc = desc;
++              single_desc->cmd = ADM_CMD_TYPE_SINGLE;
++              single_desc->src_addr = *src;
++              single_desc->dst_addr = *dst;
++              single_desc->len = (remainder > ADM_MAX_XFER) ?
++                              ADM_MAX_XFER : remainder;
++
++              remainder -= single_desc->len;
++              *incr_addr += single_desc->len;
++              desc += sizeof(*single_desc);
++      } while (remainder);
++
++      /* set last command if this is the end of the whole transaction */
++      if (sg_is_last(sg))
++              single_desc->cmd |= ADM_CMD_LC;
++
++      return desc;
++}
++
++/**
++ * adm_prep_slave_sg - Prep slave sg transaction
++ *
++ * @chan: dma channel
++ * @sgl: scatter gather list
++ * @sg_len: length of sg
++ * @direction: DMA transfer direction
++ * @flags: DMA flags
++ * @context: transfer context (unused)
++ */
++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
++                                                       struct scatterlist *sgl,
++                                                       unsigned int sg_len,
++                                                       enum dma_transfer_direction direction,
++                                                       unsigned long flags,
++                                                       void *context)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct adm_device *adev = achan->adev;
++      struct adm_async_desc *async_desc;
++      struct scatterlist *sg;
++      dma_addr_t cple_addr;
++      u32 i, burst;
++      u32 single_count = 0, box_count = 0, crci = 0;
++      void *desc;
++      u32 *cple;
++      int blk_size = 0;
++
++      if (!is_slave_direction(direction)) {
++              dev_err(adev->dev, "invalid dma direction\n");
++              return NULL;
++      }
++
++      /*
++       * get burst value from slave configuration
++       */
++      burst = (direction == DMA_MEM_TO_DEV) ?
++              achan->slave.dst_maxburst :
++              achan->slave.src_maxburst;
++
++      /* if using flow control, validate burst and crci values */
++      if (achan->slave.device_fc) {
++              blk_size = adm_get_blksize(burst);
++              if (blk_size < 0) {
++                      dev_err(adev->dev, "invalid burst value: %d\n",
++                              burst);
++                      return ERR_PTR(-EINVAL);
++              }
++
++              crci = achan->slave.slave_id & 0xf;
++              if (!crci || achan->slave.slave_id > 0x1f) {
++                      dev_err(adev->dev, "invalid crci value\n");
++                      return ERR_PTR(-EINVAL);
++              }
++      }
++
++      /* iterate through sgs and compute allocation size of structures */
++      for_each_sg(sgl, sg, sg_len, i) {
++              if (achan->slave.device_fc) {
++                      box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
++                                                ADM_MAX_ROWS);
++                      if (sg_dma_len(sg) % burst)
++                              single_count++;
++              } else {
++                      single_count += DIV_ROUND_UP(sg_dma_len(sg),
++                                                   ADM_MAX_XFER);
++              }
++      }
++
++      async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);
++      if (!async_desc)
++              return ERR_PTR(-ENOMEM);
++
++      if (crci)
++              async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ?
++                                      ADM_CRCI_CTL_MUX_SEL : 0;
++      async_desc->crci = crci;
++      async_desc->blk_size = blk_size;
++      async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) +
++                              box_count * sizeof(struct adm_desc_hw_box) +
++                              sizeof(*cple) + 2 * ADM_DESC_ALIGN;
++
++      async_desc->cpl = kzalloc(async_desc->dma_len, GFP_NOWAIT);
++      if (!async_desc->cpl)
++              goto free;
++
++      async_desc->adev = adev;
++
++      /* both command list entry and descriptors must be 8 byte aligned */
++      cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN);
++      desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN);
++
++      for_each_sg(sgl, sg, sg_len, i) {
++              async_desc->length += sg_dma_len(sg);
++
++              if (achan->slave.device_fc)
++                      desc = adm_process_fc_descriptors(achan, desc, sg, crci,
++                                                        burst, direction);
++              else
++                      desc = adm_process_non_fc_descriptors(achan, desc, sg,
++                                                            direction);
++      }
++
++      async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl,
++                                            async_desc->dma_len,
++                                            DMA_TO_DEVICE);
++      if (dma_mapping_error(adev->dev, async_desc->dma_addr))
++              goto free;
++
++      cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl);
++
++      /* init cmd list */
++      dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple),
++                              DMA_TO_DEVICE);
++      *cple = ADM_CPLE_LP;
++      *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3;
++      dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple),
++                                 DMA_TO_DEVICE);
++
++      return vchan_tx_prep(&achan->vc, &async_desc->vd, flags);
++
++free:
++      kfree(async_desc);
++      return ERR_PTR(-ENOMEM);
++}
++
++/**
++ * adm_terminate_all - terminate all transactions on a channel
++ * @achan: adm dma channel
++ *
++ * Dequeues and frees all transactions, aborts current transaction
++ * No callbacks are done
++ *
++ */
++static int adm_terminate_all(struct dma_chan *chan)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct adm_device *adev = achan->adev;
++      unsigned long flags;
++      LIST_HEAD(head);
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++      vchan_get_all_descriptors(&achan->vc, &head);
++
++      /* send flush command to terminate current transaction */
++      writel_relaxed(0x0,
++                     adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee));
++
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++      vchan_dma_desc_free_list(&achan->vc, &head);
++
++      return 0;
++}
++
++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      unsigned long flag;
++
++      spin_lock_irqsave(&achan->vc.lock, flag);
++      memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config));
++      spin_unlock_irqrestore(&achan->vc.lock, flag);
++
++      return 0;
++}
++
++/**
++ * adm_start_dma - start next transaction
++ * @achan - ADM dma channel
++ */
++static void adm_start_dma(struct adm_chan *achan)
++{
++      struct virt_dma_desc *vd = vchan_next_desc(&achan->vc);
++      struct adm_device *adev = achan->adev;
++      struct adm_async_desc *async_desc;
++
++      lockdep_assert_held(&achan->vc.lock);
++
++      if (!vd)
++              return;
++
++      list_del(&vd->node);
++
++      /* write next command list out to the CMD FIFO */
++      async_desc = container_of(vd, struct adm_async_desc, vd);
++      achan->curr_txd = async_desc;
++
++      /* reset channel error */
++      achan->error = 0;
++
++      if (!achan->initialized) {
++              /* enable interrupts */
++              writel(ADM_CH_CONF_SHADOW_EN |
++                     ADM_CH_CONF_PERM_MPU_CONF |
++                     ADM_CH_CONF_MPU_DISABLE |
++                     ADM_CH_CONF_SEC_DOMAIN(adev->ee),
++                     adev->regs + ADM_CH_CONF(achan->id));
++
++              writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN,
++                     adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++              achan->initialized = 1;
++      }
++
++      /* set the crci block size if this transaction requires CRCI */
++      if (async_desc->crci) {
++              writel(async_desc->mux | async_desc->blk_size,
++                     adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee));
++      }
++
++      /* make sure IRQ enable doesn't get reordered */
++      wmb();
++
++      /* write next command list out to the CMD FIFO */
++      writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3,
++             adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee));
++}
++
++/**
++ * adm_dma_irq - irq handler for ADM controller
++ * @irq: IRQ of interrupt
++ * @data: callback data
++ *
++ * IRQ handler for the bam controller
++ */
++static irqreturn_t adm_dma_irq(int irq, void *data)
++{
++      struct adm_device *adev = data;
++      u32 srcs, i;
++      struct adm_async_desc *async_desc;
++      unsigned long flags;
++
++      srcs = readl_relaxed(adev->regs +
++                      ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee));
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++              struct adm_chan *achan = &adev->channels[i];
++              u32 status, result;
++
++              if (srcs & BIT(i)) {
++                      status = readl_relaxed(adev->regs +
++                                             ADM_CH_STATUS_SD(i, adev->ee));
++
++                      /* if no result present, skip */
++                      if (!(status & ADM_CH_STATUS_VALID))
++                              continue;
++
++                      result = readl_relaxed(adev->regs +
++                              ADM_CH_RSLT(i, adev->ee));
++
++                      /* no valid results, skip */
++                      if (!(result & ADM_CH_RSLT_VALID))
++                              continue;
++
++                      /* flag error if transaction was flushed or failed */
++                      if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH))
++                              achan->error = 1;
++
++                      spin_lock_irqsave(&achan->vc.lock, flags);
++                      async_desc = achan->curr_txd;
++
++                      achan->curr_txd = NULL;
++
++                      if (async_desc) {
++                              vchan_cookie_complete(&async_desc->vd);
++
++                              /* kick off next DMA */
++                              adm_start_dma(achan);
++                      }
++
++                      spin_unlock_irqrestore(&achan->vc.lock, flags);
++              }
++      }
++
++      return IRQ_HANDLED;
++}
++
++/**
++ * adm_tx_status - returns status of transaction
++ * @chan: dma channel
++ * @cookie: transaction cookie
++ * @txstate: DMA transaction state
++ *
++ * Return status of dma transaction
++ */
++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
++                                   struct dma_tx_state *txstate)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      struct virt_dma_desc *vd;
++      enum dma_status ret;
++      unsigned long flags;
++      size_t residue = 0;
++
++      ret = dma_cookie_status(chan, cookie, txstate);
++      if (ret == DMA_COMPLETE || !txstate)
++              return ret;
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++
++      vd = vchan_find_desc(&achan->vc, cookie);
++      if (vd)
++              residue = container_of(vd, struct adm_async_desc, vd)->length;
++
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++
++      /*
++       * residue is either the full length if it is in the issued list, or 0
++       * if it is in progress.  We have no reliable way of determining
++       * anything inbetween
++       */
++      dma_set_residue(txstate, residue);
++
++      if (achan->error)
++              return DMA_ERROR;
++
++      return ret;
++}
++
++/**
++ * adm_issue_pending - starts pending transactions
++ * @chan: dma channel
++ *
++ * Issues all pending transactions and starts DMA
++ */
++static void adm_issue_pending(struct dma_chan *chan)
++{
++      struct adm_chan *achan = to_adm_chan(chan);
++      unsigned long flags;
++
++      spin_lock_irqsave(&achan->vc.lock, flags);
++
++      if (vchan_issue_pending(&achan->vc) && !achan->curr_txd)
++              adm_start_dma(achan);
++      spin_unlock_irqrestore(&achan->vc.lock, flags);
++}
++
++/**
++ * adm_dma_free_desc - free descriptor memory
++ * @vd: virtual descriptor
++ *
++ */
++static void adm_dma_free_desc(struct virt_dma_desc *vd)
++{
++      struct adm_async_desc *async_desc = container_of(vd,
++                      struct adm_async_desc, vd);
++
++      dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr,
++                       async_desc->dma_len, DMA_TO_DEVICE);
++      kfree(async_desc->cpl);
++      kfree(async_desc);
++}
++
++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan,
++                           u32 index)
++{
++      achan->id = index;
++      achan->adev = adev;
++
++      vchan_init(&achan->vc, &adev->common);
++      achan->vc.desc_free = adm_dma_free_desc;
++}
++
++static int adm_dma_probe(struct platform_device *pdev)
++{
++      struct adm_device *adev;
++      int ret;
++      u32 i;
++
++      adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
++      if (!adev)
++              return -ENOMEM;
++
++      adev->dev = &pdev->dev;
++
++      adev->regs = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(adev->regs))
++              return PTR_ERR(adev->regs);
++
++      adev->irq = platform_get_irq(pdev, 0);
++      if (adev->irq < 0)
++              return adev->irq;
++
++      ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee);
++      if (ret) {
++              dev_err(adev->dev, "Execution environment unspecified\n");
++              return ret;
++      }
++
++      adev->core_clk = devm_clk_get(adev->dev, "core");
++      if (IS_ERR(adev->core_clk))
++              return PTR_ERR(adev->core_clk);
++
++      adev->iface_clk = devm_clk_get(adev->dev, "iface");
++      if (IS_ERR(adev->iface_clk))
++              return PTR_ERR(adev->iface_clk);
++
++      adev->clk_reset = devm_reset_control_get_exclusive(&pdev->dev, "clk");
++      if (IS_ERR(adev->clk_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 reset\n");
++              return PTR_ERR(adev->clk_reset);
++      }
++
++      adev->c0_reset = devm_reset_control_get_exclusive(&pdev->dev, "c0");
++      if (IS_ERR(adev->c0_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C0 reset\n");
++              return PTR_ERR(adev->c0_reset);
++      }
++
++      adev->c1_reset = devm_reset_control_get_exclusive(&pdev->dev, "c1");
++      if (IS_ERR(adev->c1_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C1 reset\n");
++              return PTR_ERR(adev->c1_reset);
++      }
++
++      adev->c2_reset = devm_reset_control_get_exclusive(&pdev->dev, "c2");
++      if (IS_ERR(adev->c2_reset)) {
++              dev_err(adev->dev, "failed to get ADM0 C2 reset\n");
++              return PTR_ERR(adev->c2_reset);
++      }
++
++      ret = clk_prepare_enable(adev->core_clk);
++      if (ret) {
++              dev_err(adev->dev, "failed to prepare/enable core clock\n");
++              return ret;
++      }
++
++      ret = clk_prepare_enable(adev->iface_clk);
++      if (ret) {
++              dev_err(adev->dev, "failed to prepare/enable iface clock\n");
++              goto err_disable_core_clk;
++      }
++
++      reset_control_assert(adev->clk_reset);
++      reset_control_assert(adev->c0_reset);
++      reset_control_assert(adev->c1_reset);
++      reset_control_assert(adev->c2_reset);
++
++      udelay(2);
++
++      reset_control_deassert(adev->clk_reset);
++      reset_control_deassert(adev->c0_reset);
++      reset_control_deassert(adev->c1_reset);
++      reset_control_deassert(adev->c2_reset);
++
++      adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS,
++                                    sizeof(*adev->channels), GFP_KERNEL);
++
++      if (!adev->channels) {
++              ret = -ENOMEM;
++              goto err_disable_clks;
++      }
++
++      /* allocate and initialize channels */
++      INIT_LIST_HEAD(&adev->common.channels);
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++)
++              adm_channel_init(adev, &adev->channels[i], i);
++
++      /* reset CRCIs */
++      for (i = 0; i < 16; i++)
++              writel(ADM_CRCI_CTL_RST, adev->regs +
++                      ADM_CRCI_CTL(i, adev->ee));
++
++      /* configure client interfaces */
++      writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0));
++      writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1));
++      writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) |
++             ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2));
++      writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf),
++             adev->regs + ADM_GP_CTL);
++
++      ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq,
++                             0, "adm_dma", adev);
++      if (ret)
++              goto err_disable_clks;
++
++      platform_set_drvdata(pdev, adev);
++
++      adev->common.dev = adev->dev;
++      adev->common.dev->dma_parms = &adev->dma_parms;
++
++      /* set capabilities */
++      dma_cap_zero(adev->common.cap_mask);
++      dma_cap_set(DMA_SLAVE, adev->common.cap_mask);
++      dma_cap_set(DMA_PRIVATE, adev->common.cap_mask);
++
++      /* initialize dmaengine apis */
++      adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV);
++      adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
++      adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++      adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
++      adev->common.device_free_chan_resources = adm_free_chan;
++      adev->common.device_prep_slave_sg = adm_prep_slave_sg;
++      adev->common.device_issue_pending = adm_issue_pending;
++      adev->common.device_tx_status = adm_tx_status;
++      adev->common.device_terminate_all = adm_terminate_all;
++      adev->common.device_config = adm_slave_config;
++
++      ret = dma_async_device_register(&adev->common);
++      if (ret) {
++              dev_err(adev->dev, "failed to register dma async device\n");
++              goto err_disable_clks;
++      }
++
++      ret = of_dma_controller_register(pdev->dev.of_node,
++                                       of_dma_xlate_by_chan_id,
++                                       &adev->common);
++      if (ret)
++              goto err_unregister_dma;
++
++      return 0;
++
++err_unregister_dma:
++      dma_async_device_unregister(&adev->common);
++err_disable_clks:
++      clk_disable_unprepare(adev->iface_clk);
++err_disable_core_clk:
++      clk_disable_unprepare(adev->core_clk);
++
++      return ret;
++}
++
++static int adm_dma_remove(struct platform_device *pdev)
++{
++      struct adm_device *adev = platform_get_drvdata(pdev);
++      struct adm_chan *achan;
++      u32 i;
++
++      of_dma_controller_free(pdev->dev.of_node);
++      dma_async_device_unregister(&adev->common);
++
++      for (i = 0; i < ADM_MAX_CHANNELS; i++) {
++              achan = &adev->channels[i];
++
++              /* mask IRQs for this channel/EE pair */
++              writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
++
++              tasklet_kill(&adev->channels[i].vc.task);
++              adm_terminate_all(&adev->channels[i].vc.chan);
++      }
++
++      devm_free_irq(adev->dev, adev->irq, adev);
++
++      clk_disable_unprepare(adev->core_clk);
++      clk_disable_unprepare(adev->iface_clk);
++
++      return 0;
++}
++
++static const struct of_device_id adm_of_match[] = {
++      { .compatible = "qcom,adm", },
++      {}
++};
++MODULE_DEVICE_TABLE(of, adm_of_match);
++
++static struct platform_driver adm_dma_driver = {
++      .probe = adm_dma_probe,
++      .remove = adm_dma_remove,
++      .driver = {
++              .name = "adm-dma-engine",
++              .of_match_table = adm_of_match,
++      },
++};
++
++module_platform_driver(adm_dma_driver);
++
++MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
++MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
++MODULE_LICENSE("GPL v2");
+-- 
+cgit 1.2.3-1.el7
+
diff --git a/target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch b/target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch
new file mode 100644 (file)
index 0000000..7aec4fc
--- /dev/null
@@ -0,0 +1,227 @@
+From 803eb124e1a64e42888542c3444bfe6dac412c7f Mon Sep 17 00:00:00 2001
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Date: Mon, 4 Jan 2021 09:41:35 +0530
+Subject: mtd: parsers: Add Qcom SMEM parser
+
+NAND based Qualcomm platforms have the partition table populated in the
+Shared Memory (SMEM). Hence, add a parser for parsing the partitions
+from it.
+
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadhasivam@linaro.org
+---
+ drivers/mtd/parsers/Kconfig        |   8 ++
+ drivers/mtd/parsers/Makefile       |   1 +
+ drivers/mtd/parsers/qcomsmempart.c | 170 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 179 insertions(+)
+ create mode 100644 drivers/mtd/parsers/qcomsmempart.c
+
+diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
+index e72354322f628..d90c302290522 100644
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -160,6 +160,14 @@ config MTD_REDBOOT_PARTS_READONLY
+         'FIS directory' images, enable this option.
+ endif # MTD_REDBOOT_PARTS
++
++config MTD_QCOMSMEM_PARTS
++      tristate "Qualcomm SMEM NAND flash partition parser"
++      depends on MTD_NAND_QCOM || COMPILE_TEST
++      depends on QCOM_SMEM
++      help
++        This provides support for parsing partitions from Shared Memory (SMEM)
++        for NAND flash on Qualcomm platforms.
+ config MTD_ROUTERBOOT_PARTS
+       tristate "RouterBoot flash partition parser"
+diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile
+index b0c5f62f9e858..50eb0b0a22105 100644
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -9,4 +9,5 @@ obj-$(CONFIG_MTD_AFS_PARTS)            += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX)          += parser_trx.o
+ obj-$(CONFIG_MTD_SHARPSL_PARTS)               += sharpslpart.o
+ obj-$(CONFIG_MTD_REDBOOT_PARTS)               += redboot.o
++obj-$(CONFIG_MTD_QCOMSMEM_PARTS)      += qcomsmempart.o
+ obj-$(CONFIG_MTD_ROUTERBOOT_PARTS)            += routerbootpart.o
+diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c
+new file mode 100644
+index 0000000000000..808cb33d71f8e
+--- /dev/null
++++ b/drivers/mtd/parsers/qcomsmempart.c
+@@ -0,0 +1,170 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Qualcomm SMEM NAND flash partition parser
++ *
++ * Copyright (C) 2020, Linaro Ltd.
++ */
++
++#include <linux/ctype.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/slab.h>
++#include <linux/soc/qcom/smem.h>
++
++#define SMEM_AARM_PARTITION_TABLE     9
++#define SMEM_APPS                     0
++
++#define SMEM_FLASH_PART_MAGIC1                0x55ee73aa
++#define SMEM_FLASH_PART_MAGIC2                0xe35ebddb
++#define SMEM_FLASH_PTABLE_V3          3
++#define SMEM_FLASH_PTABLE_V4          4
++#define SMEM_FLASH_PTABLE_MAX_PARTS_V3        16
++#define SMEM_FLASH_PTABLE_MAX_PARTS_V4        48
++#define SMEM_FLASH_PTABLE_HDR_LEN     (4 * sizeof(u32))
++#define SMEM_FLASH_PTABLE_NAME_SIZE   16
++
++/**
++ * struct smem_flash_pentry - SMEM Flash partition entry
++ * @name: Name of the partition
++ * @offset: Offset in blocks
++ * @length: Length of the partition in blocks
++ * @attr: Flags for this partition
++ */
++struct smem_flash_pentry {
++      char name[SMEM_FLASH_PTABLE_NAME_SIZE];
++      __le32 offset;
++      __le32 length;
++      u8 attr;
++} __packed __aligned(4);
++
++/**
++ * struct smem_flash_ptable - SMEM Flash partition table
++ * @magic1: Partition table Magic 1
++ * @magic2: Partition table Magic 2
++ * @version: Partition table version
++ * @numparts: Number of partitions in this ptable
++ * @pentry: Flash partition entries belonging to this ptable
++ */
++struct smem_flash_ptable {
++      __le32 magic1;
++      __le32 magic2;
++      __le32 version;
++      __le32 numparts;
++      struct smem_flash_pentry pentry[SMEM_FLASH_PTABLE_MAX_PARTS_V4];
++} __packed __aligned(4);
++
++static int parse_qcomsmem_part(struct mtd_info *mtd,
++                             const struct mtd_partition **pparts,
++                             struct mtd_part_parser_data *data)
++{
++      struct smem_flash_pentry *pentry;
++      struct smem_flash_ptable *ptable;
++      size_t len = SMEM_FLASH_PTABLE_HDR_LEN;
++      struct mtd_partition *parts;
++      int ret, i, numparts;
++      char *name, *c;
++
++      pr_debug("Parsing partition table info from SMEM\n");
++      ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
++      if (IS_ERR(ptable)) {
++              pr_err("Error reading partition table header\n");
++              return PTR_ERR(ptable);
++      }
++
++      /* Verify ptable magic */
++      if (le32_to_cpu(ptable->magic1) != SMEM_FLASH_PART_MAGIC1 ||
++          le32_to_cpu(ptable->magic2) != SMEM_FLASH_PART_MAGIC2) {
++              pr_err("Partition table magic verification failed\n");
++              return -EINVAL;
++      }
++
++      /* Ensure that # of partitions is less than the max we have allocated */
++      numparts = le32_to_cpu(ptable->numparts);
++      if (numparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4) {
++              pr_err("Partition numbers exceed the max limit\n");
++              return -EINVAL;
++      }
++
++      /* Find out length of partition data based on table version */
++      if (le32_to_cpu(ptable->version) <= SMEM_FLASH_PTABLE_V3) {
++              len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 *
++                      sizeof(struct smem_flash_pentry);
++      } else if (le32_to_cpu(ptable->version) == SMEM_FLASH_PTABLE_V4) {
++              len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 *
++                      sizeof(struct smem_flash_pentry);
++      } else {
++              pr_err("Unknown ptable version (%d)", le32_to_cpu(ptable->version));
++              return -EINVAL;
++      }
++
++      /*
++       * Now that the partition table header has been parsed, verified
++       * and the length of the partition table calculated, read the
++       * complete partition table
++       */
++      ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
++      if (IS_ERR_OR_NULL(ptable)) {
++              pr_err("Error reading partition table\n");
++              return PTR_ERR(ptable);
++      }
++
++      parts = kcalloc(numparts, sizeof(*parts), GFP_KERNEL);
++      if (!parts)
++              return -ENOMEM;
++
++      for (i = 0; i < numparts; i++) {
++              pentry = &ptable->pentry[i];
++              if (pentry->name[0] == '\0')
++                      continue;
++
++              name = kstrdup(pentry->name, GFP_KERNEL);
++              if (!name) {
++                      ret = -ENOMEM;
++                      goto out_free_parts;
++              }
++
++              /* Convert name to lower case */
++              for (c = name; *c != '\0'; c++)
++                      *c = tolower(*c);
++
++              parts[i].name = name;
++              parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
++              parts[i].mask_flags = pentry->attr;
++              parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize;
++              pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
++                       i, pentry->name, le32_to_cpu(pentry->offset),
++                       le32_to_cpu(pentry->length), pentry->attr);
++      }
++
++      pr_debug("SMEM partition table found: ver: %d len: %d\n",
++               le32_to_cpu(ptable->version), numparts);
++      *pparts = parts;
++
++      return numparts;
++
++out_free_parts:
++      while (--i >= 0)
++              kfree(parts[i].name);
++      kfree(parts);
++      *pparts = NULL;
++
++      return ret;
++}
++
++static const struct of_device_id qcomsmem_of_match_table[] = {
++      { .compatible = "qcom,smem-part" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, qcomsmem_of_match_table);
++
++static struct mtd_part_parser mtd_parser_qcomsmem = {
++      .parse_fn = parse_qcomsmem_part,
++      .name = "qcomsmem",
++      .of_match_table = qcomsmem_of_match_table,
++};
++module_mtd_part_parser(mtd_parser_qcomsmem);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
++MODULE_DESCRIPTION("Qualcomm SMEM NAND flash partition parser");
+-- 
+cgit 1.2.3-1.el7
+
diff --git a/target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch
new file mode 100644 (file)
index 0000000..e838729
--- /dev/null
@@ -0,0 +1,24 @@
+From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Thu, 9 Mar 2017 09:31:44 +0100
+Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ drivers/mtd/parsers/qcomsmempart.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/parsers/qcomsmempart.c
++++ b/drivers/mtd/parsers/qcomsmempart.c
+@@ -132,6 +132,11 @@ static int parse_qcomsmem_part(struct mt
+               parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
+               parts[i].mask_flags = pentry->attr;
+               parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize;
++
++              /* "rootfs" conflicts with OpenWrt auto mounting */
++              if (mtd_type_is_nand(mtd) && !strcmp(name, "rootfs"))
++                      parts[i].name = "ubi";
++
+               pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
+                        i, pentry->name, le32_to_cpu(pentry->offset),
+                        le32_to_cpu(pentry->length), pentry->attr);