Merge tag 'dmaengine-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Dec 2020 20:52:23 +0000 (12:52 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Dec 2020 20:52:23 +0000 (12:52 -0800)
Pull dmaengine updates from Vinod Koul:
 "The last dmaengine updates for this year :)

  This contains couple of new drivers, new device support and updates to
  bunch of drivers.

  New drivers/devices:
   - Qualcomm ADM driver
   - Qualcomm GPI driver
   - Allwinner A100 DMA support
   - Microchip Sama7g5 support
   - Mediatek MT8516 apdma

  Updates:
   - more updates to idxd driver and support for IAX config
   - runtime PM support for dw driver
   - TI drivers"

* tag 'dmaengine-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (75 commits)
  soc: ti: k3-ringacc: Use correct error casting in k3_ringacc_dmarings_init
  dmaengine: ti: k3-udma-glue: Add support for K3 PKTDMA
  dmaengine: ti: k3-udma: Initial support for K3 PKTDMA
  dmaengine: ti: k3-udma: Add support for BCDMA channel TPL handling
  dmaengine: ti: k3-udma: Initial support for K3 BCDMA
  soc: ti: k3-ringacc: add AM64 DMA rings support.
  dmaengine: ti: Add support for k3 event routers
  dmaengine: ti: k3-psil: Add initial map for AM64
  dmaengine: ti: k3-psil: Extend psil_endpoint_config for K3 PKTDMA
  dt-bindings: dma: ti: Add document for K3 PKTDMA
  dt-bindings: dma: ti: Add document for K3 BCDMA
  dmaengine: dmatest: Use dmaengine_get_dma_device
  dmaengine: doc: client: Update for dmaengine_get_dma_device() usage
  dmaengine: Add support for per channel coherency handling
  dmaengine: of-dma: Add support for optional router configuration callback
  dmaengine: ti: k3-udma-glue: Configure the dma_dev for rings
  dmaengine: ti: k3-udma-glue: Get the ringacc from udma_dev
  dmaengine: ti: k3-udma-glue: Add function to get device pointer for DMA API
  dmaengine: ti: k3-udma: Add support for second resource range from sysfw
  dmaengine: ti: k3-udma: Wait for peer teardown completion if supported
  ...

70 files changed:
Documentation/ABI/stable/sysfs-driver-dma-idxd
Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
Documentation/devicetree/bindings/dma/atmel-xdma.txt
Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt
Documentation/devicetree/bindings/dma/qcom,gpi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml [new file with mode: 0644]
Documentation/driver-api/dmaengine/client.rst
drivers/dma/Kconfig
drivers/dma/at_xdmac.c
drivers/dma/dma-jz4780.c
drivers/dma/dmatest.c
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
drivers/dma/dw/core.c
drivers/dma/hisi_dma.c
drivers/dma/idma64.c
drivers/dma/idxd/cdev.c
drivers/dma/idxd/device.c
drivers/dma/idxd/dma.c
drivers/dma/idxd/idxd.h
drivers/dma/idxd/init.c
drivers/dma/idxd/irq.c
drivers/dma/idxd/registers.h
drivers/dma/idxd/submit.c
drivers/dma/idxd/sysfs.c
drivers/dma/imx-dma.c
drivers/dma/imx-sdma.c
drivers/dma/ipu/ipu_idmac.c
drivers/dma/k3dma.c
drivers/dma/milbeaut-xdmac.c
drivers/dma/moxart-dma.c
drivers/dma/mv_xor.c
drivers/dma/mv_xor_v2.c
drivers/dma/mxs-dma.c
drivers/dma/of-dma.c
drivers/dma/pl330.c
drivers/dma/ppc4xx/adma.c
drivers/dma/pxa_dma.c
drivers/dma/qcom/Kconfig
drivers/dma/qcom/Makefile
drivers/dma/qcom/bam_dma.c
drivers/dma/qcom/gpi.c [new file with mode: 0644]
drivers/dma/qcom/qcom_adm.c [new file with mode: 0644]
drivers/dma/sf-pdma/sf-pdma.c
drivers/dma/ste_dma40.c
drivers/dma/stm32-dma.c
drivers/dma/stm32-dmamux.c
drivers/dma/stm32-mdma.c
drivers/dma/sun6i-dma.c
drivers/dma/tegra210-adma.c
drivers/dma/ti/Makefile
drivers/dma/ti/dma-crossbar.c
drivers/dma/ti/k3-psil-am64.c [new file with mode: 0644]
drivers/dma/ti/k3-psil-priv.h
drivers/dma/ti/k3-psil.c
drivers/dma/ti/k3-udma-glue.c
drivers/dma/ti/k3-udma-private.c
drivers/dma/ti/k3-udma.c
drivers/dma/ti/k3-udma.h
drivers/soc/ti/k3-ringacc.c
include/dt-bindings/dma/jz4775-dma.h [new file with mode: 0644]
include/dt-bindings/dma/qcom-gpi.h [new file with mode: 0644]
include/dt-bindings/dma/x2000-dma.h [new file with mode: 0644]
include/linux/dma/k3-event-router.h [new file with mode: 0644]
include/linux/dma/k3-psil.h
include/linux/dma/k3-udma-glue.h
include/linux/dma/qcom-gpi-dma.h [new file with mode: 0644]
include/linux/dmaengine.h
include/linux/soc/ti/k3-ringacc.h
include/uapi/linux/idxd.h

index b4418388093554d4933ea0265e4794ed6acc43e5..55285c136cf06a4fda159de104ef091561b3fed9 100644 (file)
@@ -77,6 +77,13 @@ Contact:        dmaengine@vger.kernel.org
 Description:    The operation capability bit mask specify the operation types
                supported by the this device.
 
+What:          /sys/bus/dsa/devices/dsa<m>/pasid_enabled
+Date:          Oct 27, 2020
+KernelVersion: 5.11.0
+Contact:       dmaengine@vger.kernel.org
+Description:   To indicate if PASID (process address space identifier) is
+               enabled or not for this device.
+
 What:           /sys/bus/dsa/devices/dsa<m>/state
 Date:           Oct 25, 2019
 KernelVersion:  5.6.0
@@ -122,6 +129,13 @@ KernelVersion:     5.10.0
 Contact:       dmaengine@vger.kernel.org
 Description:   The last executed device administrative command's status/error.
 
+What:          /sys/bus/dsa/devices/wq<m>.<n>/block_on_fault
+Date:          Oct 27, 2020
+KernelVersion: 5.11.0
+Contact:       dmaengine@vger.kernel.org
+Description:   To indicate block on fault is allowed or not for the work queue
+               to support on demand paging.
+
 What:           /sys/bus/dsa/devices/wq<m>.<n>/group_id
 Date:           Oct 25, 2019
 KernelVersion:  5.6.0
@@ -190,6 +204,13 @@ Contact:   dmaengine@vger.kernel.org
 Description:   The max batch size for this workqueue. Cannot exceed device
                max batch size. Configurable parameter.
 
+What:          /sys/bus/dsa/devices/wq<m>.<n>/ats_disable
+Date:          Nov 13, 2020
+KernelVersion: 5.11.0
+Contact:       dmaengine@vger.kernel.org
+Description:   Indicate whether ATS disable is turned on for the workqueue.
+               0 indicates ATS is on, and 1 indicates ATS is off for the workqueue.
+
 What:           /sys/bus/dsa/devices/engine<m>.<n>/group_id
 Date:           Oct 25, 2019
 KernelVersion:  5.6.0
index 372679dbd216f1ae1c6c4f42041b638c253b192f..b6e1ebfaf3666941646efeae2f9a5942668627f6 100644 (file)
@@ -21,6 +21,7 @@ properties:
   compatible:
     oneOf:
       - const: allwinner,sun50i-a64-dma
+      - const: allwinner,sun50i-a100-dma
       - const: allwinner,sun50i-h6-dma
       - items:
           - const: allwinner,sun8i-r40-dma
@@ -56,7 +57,9 @@ required:
 if:
   properties:
     compatible:
-      const: allwinner,sun50i-h6-dma
+      enum:
+        - allwinner,sun50i-a100-dma
+        - allwinner,sun50i-h6-dma
 
 then:
   properties:
index 4dc398e1a37103050754c6b1411463800ac078c7..510b7f25ba24236a7b792c04e3f2ecc66ddab231 100644 (file)
@@ -2,7 +2,8 @@
 
 * XDMA Controller
 Required properties:
-- compatible: Should be "atmel,sama5d4-dma" or "microchip,sam9x60-dma".
+- compatible: Should be "atmel,sama5d4-dma", "microchip,sam9x60-dma" or
+  "microchip,sama7g5-dma".
 - reg: Should contain DMA registers location and length.
 - interrupts: Should contain DMA interrupt.
 - #dma-cells: Must be <1>, used to represent the number of integer cells in
index 2117db0ce4f2b231a3df2be48ace0c17c03b7452..fef9c1eeb2644a1ae7077028bfb712432b19b72a 100644 (file)
@@ -4,6 +4,7 @@ Required properties:
 - compatible should contain:
   * "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
   * "mediatek,mt6577-uart-dma" for MT6577 and all of the above
+  * "mediatek,mt8516-uart-dma", "mediatek,mt6577" for MT8516 SoC
 
 - reg: The base address of the APDMA register bank.
 
diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml
new file mode 100644 (file)
index 0000000..f8142ad
--- /dev/null
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/qcom,gpi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies Inc GPI DMA controller
+
+maintainers:
+  - Vinod Koul <vkoul@kernel.org>
+
+description: |
+  QCOM GPI DMA controller provides DMA capabilities for
+  peripheral buses such as I2C, UART, and SPI.
+
+allOf:
+  - $ref: "dma-controller.yaml#"
+
+properties:
+  compatible:
+    enum:
+      - qcom,sdm845-gpi-dma
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    description:
+      Interrupt lines for each GPI instance
+    maxItems: 13
+
+  "#dma-cells":
+    const: 3
+    description: >
+      DMA clients must use the format described in dma.txt, giving a phandle
+      to the DMA controller plus the following 3 integer cells:
+      - channel: if set to 0xffffffff, any available channel will be allocated
+        for the client. Otherwise, the exact channel specified will be used.
+      - seid: serial id of the client as defined in the SoC documentation.
+      - client: type of the client as defined in dt-bindings/dma/qcom-gpi.h
+
+  iommus:
+    maxItems: 1
+
+  dma-channels:
+    maximum: 31
+
+  dma-channel-mask:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#dma-cells"
+  - iommus
+  - dma-channels
+  - dma-channel-mask
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/dma/qcom-gpi.h>
+    gpi_dma0: dma-controller@800000 {
+        compatible = "qcom,gpi-dma";
+        #dma-cells = <3>;
+        reg = <0x00800000 0x60000>;
+        iommus = <&apps_smmu 0x0016 0x0>;
+        dma-channels = <13>;
+        dma-channel-mask = <0xfa>;
+        interrupts = <GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 251 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 254 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 255 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>;
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml b/Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml
new file mode 100644 (file)
index 0000000..b15f68c
--- /dev/null
@@ -0,0 +1,164 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/ti/k3-bcdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments K3 DMSS BCDMA Device Tree Bindings
+
+maintainers:
+  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+
+description: |
+  The Block Copy DMA (BCDMA) is intended to perform similar functions as the TR
+  mode channels of K3 UDMA-P.
+  BCDMA includes block copy channels and Split channels.
+
+  Block copy channels mainly used for memory to memory transfers, but with
+  optional triggers a block copy channel can service peripherals by accessing
+  directly to memory mapped registers or area.
+
+  Split channels can be used to service PSI-L based peripherals.
+  The peripherals can be PSI-L native or legacy, non PSI-L native peripherals
+  with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the
+  legacy peripheral.
+
+  PDMAs can be configured via BCDMA split channel's peer registers to match with
+  the configuration of the legacy peripheral.
+
+allOf:
+  - $ref: /schemas/dma/dma-controller.yaml#
+
+properties:
+  compatible:
+    const: ti,am64-dmss-bcdma
+
+  "#dma-cells":
+    const: 3
+    description: |
+      cell 1: type of the BCDMA channel to be used to service the peripheral:
+        0 - split channel
+        1 - block copy channel using global trigger 1
+        2 - block copy channel using global trigger 2
+        3 - block copy channel using local trigger
+
+      cell 2: parameter for the channel:
+        if cell 1 is 0 (split channel):
+          PSI-L thread ID of the remote (to BCDMA) end.
+          Valid ranges for thread ID depends on the data movement direction:
+          for source thread IDs (rx): 0 - 0x7fff
+          for destination thread IDs (tx): 0x8000 - 0xffff
+
+          Please refer to the device documentation for the PSI-L thread map and
+          also the PSI-L peripheral chapter for the correct thread ID.
+        if cell 1 is 1 or 2 (block copy channel using global trigger):
+          Unused, ignored
+
+          The trigger must be configured for the channel externally to BCDMA,
+          channels using global triggers should not be requested directly, but
+          via DMA event router.
+        if cell 1 is 3 (block copy channel using local trigger):
+          bchan number of the locally triggered channel
+
+      cell 3: ASEL value for the channel
+
+  reg:
+    maxItems: 5
+
+  reg-names:
+    items:
+      - const: gcfg
+      - const: bchanrt
+      - const: rchanrt
+      - const: tchanrt
+      - const: ringrt
+
+  msi-parent: true
+
+  ti,asel:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: ASEL value for non slave channels
+
+  ti,sci-rm-range-bchan:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of BCDMA block-copy channel resource subtypes for resource
+      allocation for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+  ti,sci-rm-range-tchan:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of BCDMA split tx channel resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+  ti,sci-rm-range-rchan:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of BCDMA split rx channel resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+required:
+  - compatible
+  - "#dma-cells"
+  - reg
+  - reg-names
+  - msi-parent
+  - ti,sci
+  - ti,sci-dev-id
+  - ti,sci-rm-range-bchan
+  - ti,sci-rm-range-tchan
+  - ti,sci-rm-range-rchan
+
+unevaluatedProperties: false
+
+examples:
+  - |+
+    cbass_main {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        main_dmss {
+            compatible = "simple-mfd";
+            #address-cells = <2>;
+            #size-cells = <2>;
+            dma-ranges;
+            ranges;
+
+            ti,sci-dev-id = <25>;
+
+            main_bcdma: dma-controller@485c0100 {
+                compatible = "ti,am64-dmss-bcdma";
+
+                reg = <0x0 0x485c0100 0x0 0x100>,
+                      <0x0 0x4c000000 0x0 0x20000>,
+                      <0x0 0x4a820000 0x0 0x20000>,
+                      <0x0 0x4aa40000 0x0 0x20000>,
+                      <0x0 0x4bc00000 0x0 0x100000>;
+                reg-names = "gcfg", "bchanrt", "rchanrt", "tchanrt", "ringrt";
+                msi-parent = <&inta_main_dmss>;
+                #dma-cells = <3>;
+
+                ti,sci = <&dmsc>;
+                ti,sci-dev-id = <26>;
+
+                ti,sci-rm-range-bchan = <0x20>; /* BLOCK_COPY_CHAN */
+                ti,sci-rm-range-rchan = <0x21>; /* SPLIT_TR_RX_CHAN */
+                ti,sci-rm-range-tchan = <0x22>; /* SPLIT_TR_TX_CHAN */
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml b/Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml
new file mode 100644 (file)
index 0000000..b13ab60
--- /dev/null
@@ -0,0 +1,172 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/ti/k3-pktdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments K3 DMSS PKTDMA Device Tree Bindings
+
+maintainers:
+  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+
+description: |
+  The Packet DMA (PKTDMA) is intended to perform similar functions as the packet
+  mode channels of K3 UDMA-P.
+  PKTDMA only includes Split channels to service PSI-L based peripherals.
+
+  The peripherals can be PSI-L native or legacy, non PSI-L native peripherals
+  with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the
+  legacy peripheral.
+
+  PDMAs can be configured via PKTDMA split channel's peer registers to match
+  with the configuration of the legacy peripheral.
+
+allOf:
+  - $ref: /schemas/dma/dma-controller.yaml#
+
+properties:
+  compatible:
+    const: ti,am64-dmss-pktdma
+
+  "#dma-cells":
+    const: 2
+    description: |
+      The first cell is the PSI-L  thread ID of the remote (to PKTDMA) end.
+      Valid ranges for thread ID depends on the data movement direction:
+      for source thread IDs (rx): 0 - 0x7fff
+      for destination thread IDs (tx): 0x8000 - 0xffff
+
+      Please refer to the device documentation for the PSI-L thread map and also
+      the PSI-L peripheral chapter for the correct thread ID.
+
+      The second cell is the ASEL value for the channel
+
+  reg:
+    maxItems: 4
+
+  reg-names:
+    items:
+      - const: gcfg
+      - const: rchanrt
+      - const: tchanrt
+      - const: ringrt
+
+  msi-parent: true
+
+  ti,sci-rm-range-tchan:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of PKTDMA split tx channel resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+  ti,sci-rm-range-tflow:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of PKTDMA split tx flow resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+  ti,sci-rm-range-rchan:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of PKTDMA split rx channel resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+  ti,sci-rm-range-rflow:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: |
+      Array of PKTDMA split rx flow resource subtypes for resource allocation
+      for this host
+    minItems: 1
+    # Should be enough
+    maxItems: 255
+    items:
+      maximum: 0x3f
+
+required:
+  - compatible
+  - "#dma-cells"
+  - reg
+  - reg-names
+  - msi-parent
+  - ti,sci
+  - ti,sci-dev-id
+  - ti,sci-rm-range-tchan
+  - ti,sci-rm-range-tflow
+  - ti,sci-rm-range-rchan
+  - ti,sci-rm-range-rflow
+
+unevaluatedProperties: false
+
+examples:
+  - |+
+    cbass_main {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        main_dmss {
+            compatible = "simple-mfd";
+            #address-cells = <2>;
+            #size-cells = <2>;
+            dma-ranges;
+            ranges;
+
+            ti,sci-dev-id = <25>;
+
+            main_pktdma: dma-controller@485c0000 {
+                compatible = "ti,am64-dmss-pktdma";
+
+                reg = <0x0 0x485c0000 0x0 0x100>,
+                      <0x0 0x4a800000 0x0 0x20000>,
+                      <0x0 0x4aa00000 0x0 0x40000>,
+                      <0x0 0x4b800000 0x0 0x400000>;
+                reg-names = "gcfg", "rchanrt", "tchanrt", "ringrt";
+                msi-parent = <&inta_main_dmss>;
+                #dma-cells = <2>;
+
+                ti,sci = <&dmsc>;
+                ti,sci-dev-id = <30>;
+
+                ti,sci-rm-range-tchan = <0x23>, /* UNMAPPED_TX_CHAN */
+                                        <0x24>, /* CPSW_TX_CHAN */
+                                        <0x25>, /* SAUL_TX_0_CHAN */
+                                        <0x26>, /* SAUL_TX_1_CHAN */
+                                        <0x27>, /* ICSSG_0_TX_CHAN */
+                                        <0x28>; /* ICSSG_1_TX_CHAN */
+                ti,sci-rm-range-tflow = <0x10>, /* RING_UNMAPPED_TX_CHAN */
+                                        <0x11>, /* RING_CPSW_TX_CHAN */
+                                        <0x12>, /* RING_SAUL_TX_0_CHAN */
+                                        <0x13>, /* RING_SAUL_TX_1_CHAN */
+                                        <0x14>, /* RING_ICSSG_0_TX_CHAN */
+                                        <0x15>; /* RING_ICSSG_1_TX_CHAN */
+                ti,sci-rm-range-rchan = <0x29>, /* UNMAPPED_RX_CHAN */
+                                        <0x2b>, /* CPSW_RX_CHAN */
+                                        <0x2d>, /* SAUL_RX_0_CHAN */
+                                        <0x2f>, /* SAUL_RX_1_CHAN */
+                                        <0x31>, /* SAUL_RX_2_CHAN */
+                                        <0x33>, /* SAUL_RX_3_CHAN */
+                                        <0x35>, /* ICSSG_0_RX_CHAN */
+                                        <0x37>; /* ICSSG_1_RX_CHAN */
+                ti,sci-rm-range-rflow = <0x2a>, /* FLOW_UNMAPPED_RX_CHAN */
+                                        <0x2c>, /* FLOW_CPSW_RX_CHAN */
+                                        <0x2e>, /* FLOW_SAUL_RX_0/1_CHAN */
+                                        <0x32>, /* FLOW_SAUL_RX_2/3_CHAN */
+                                        <0x36>, /* FLOW_ICSSG_0_RX_CHAN */
+                                        <0x38>; /* FLOW_ICSSG_1_RX_CHAN */
+            };
+        };
+    };
index 09a3f66dcd263e4e366eb49cb489b6d7455ec648..bfd057b21a0000f704cfbd5a2ce26f37075642e6 100644 (file)
@@ -120,7 +120,9 @@ The details of these operations are:
 
   .. code-block:: c
 
-     nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
+     struct device *dma_dev = dmaengine_get_dma_device(chan);
+
+     nr_sg = dma_map_sg(dma_dev, sgl, sg_len);
        if (nr_sg == 0)
                /* error */
 
index 90284ffda58a7b43e45d4617ab8cdde1a1411a80..d242c76326217a6907dfa0814d459ae66f2ee360 100644 (file)
@@ -296,6 +296,16 @@ config INTEL_IDXD
 
          If unsure, say N.
 
+# Config symbol that collects all the dependencies that's necessary to
+# support shared virtual memory for the devices supported by idxd.
+config INTEL_IDXD_SVM
+       bool "Accelerator Shared Virtual Memory Support"
+       depends on INTEL_IDXD
+       depends on INTEL_IOMMU_SVM
+       depends on PCI_PRI
+       depends on PCI_PASID
+       depends on PCI_IOV
+
 config INTEL_IOATDMA
        tristate "Intel I/OAT DMA support"
        depends on PCI && X86_64
index 3b53115db2686a773f7958c70033c1c9db2ec70f..fe45ad5d06c414c9fd6f19237bfb501c329537cb 100644 (file)
 #define                AT_XDMAC_FIFO_SZ(i)     (((i) >> 5) & 0x7FF)            /* Number of Bytes */
 #define                AT_XDMAC_NB_REQ(i)      ((((i) >> 16) & 0x3F) + 1)      /* Number of Peripheral Requests Minus One */
 #define AT_XDMAC_GCFG          0x04    /* Global Configuration Register */
+#define                AT_XDMAC_WRHP(i)                (((i) & 0xF) << 4)
+#define                AT_XDMAC_WRMP(i)                (((i) & 0xF) << 8)
+#define                AT_XDMAC_WRLP(i)                (((i) & 0xF) << 12)
+#define                AT_XDMAC_RDHP(i)                (((i) & 0xF) << 16)
+#define                AT_XDMAC_RDMP(i)                (((i) & 0xF) << 20)
+#define                AT_XDMAC_RDLP(i)                (((i) & 0xF) << 24)
+#define                AT_XDMAC_RDSG(i)                (((i) & 0xF) << 28)
+#define AT_XDMAC_GCFG_M2M      (AT_XDMAC_RDLP(0xF) | AT_XDMAC_WRLP(0xF))
+#define AT_XDMAC_GCFG_P2M      (AT_XDMAC_RDSG(0x1) | AT_XDMAC_RDHP(0x3) | \
+                               AT_XDMAC_WRHP(0x5))
 #define AT_XDMAC_GWAC          0x08    /* Global Weighted Arbiter Configuration Register */
+#define                AT_XDMAC_PW0(i)         (((i) & 0xF) << 0)
+#define                AT_XDMAC_PW1(i)         (((i) & 0xF) << 4)
+#define                AT_XDMAC_PW2(i)         (((i) & 0xF) << 8)
+#define                AT_XDMAC_PW3(i)         (((i) & 0xF) << 12)
+#define AT_XDMAC_GWAC_M2M      0
+#define AT_XDMAC_GWAC_P2M      (AT_XDMAC_PW0(0xF) | AT_XDMAC_PW2(0xF))
+
 #define AT_XDMAC_GIE           0x0C    /* Global Interrupt Enable Register */
 #define AT_XDMAC_GID           0x10    /* Global Interrupt Disable Register */
 #define AT_XDMAC_GIM           0x14    /* Global Interrupt Mask Register */
 #define AT_XDMAC_GE            0x1C    /* Global Channel Enable Register */
 #define AT_XDMAC_GD            0x20    /* Global Channel Disable Register */
 #define AT_XDMAC_GS            0x24    /* Global Channel Status Register */
-#define AT_XDMAC_GRS           0x28    /* Global Channel Read Suspend Register */
-#define AT_XDMAC_GWS           0x2C    /* Global Write Suspend Register */
-#define AT_XDMAC_GRWS          0x30    /* Global Channel Read Write Suspend Register */
-#define AT_XDMAC_GRWR          0x34    /* Global Channel Read Write Resume Register */
-#define AT_XDMAC_GSWR          0x38    /* Global Channel Software Request Register */
-#define AT_XDMAC_GSWS          0x3C    /* Global channel Software Request Status Register */
-#define AT_XDMAC_GSWF          0x40    /* Global Channel Software Flush Request Register */
 #define AT_XDMAC_VERSION       0xFFC   /* XDMAC Version Register */
 
 /* Channel relative registers offsets */
 #define AT_XDMAC_CSUS          0x30    /* Channel Source Microblock Stride */
 #define AT_XDMAC_CDUS          0x34    /* Channel Destination Microblock Stride */
 
-#define AT_XDMAC_CHAN_REG_BASE 0x50    /* Channel registers base address */
-
 /* Microblock control members */
 #define AT_XDMAC_MBR_UBC_UBLEN_MAX     0xFFFFFFUL      /* Maximum Microblock Length */
 #define AT_XDMAC_MBR_UBC_NDE           (0x1 << 24)     /* Next Descriptor Enable */
@@ -179,6 +187,29 @@ enum atc_status {
        AT_XDMAC_CHAN_IS_PAUSED,
 };
 
+struct at_xdmac_layout {
+       /* Global Channel Read Suspend Register */
+       u8                              grs;
+       /* Global Write Suspend Register */
+       u8                              gws;
+       /* Global Channel Read Write Suspend Register */
+       u8                              grws;
+       /* Global Channel Read Write Resume Register */
+       u8                              grwr;
+       /* Global Channel Software Request Register */
+       u8                              gswr;
+       /* Global channel Software Request Status Register */
+       u8                              gsws;
+       /* Global Channel Software Flush Request Register */
+       u8                              gswf;
+       /* Channel reg base */
+       u8                              chan_cc_reg_base;
+       /* Source/Destination Interface must be specified or not */
+       bool                            sdif;
+       /* AXI queue priority configuration supported */
+       bool                            axi_config;
+};
+
 /* ----- Channels ----- */
 struct at_xdmac_chan {
        struct dma_chan                 chan;
@@ -212,6 +243,7 @@ struct at_xdmac {
        struct clk              *clk;
        u32                     save_gim;
        struct dma_pool         *at_xdmac_desc_pool;
+       const struct at_xdmac_layout    *layout;
        struct at_xdmac_chan    chan[];
 };
 
@@ -244,9 +276,35 @@ struct at_xdmac_desc {
        struct list_head                xfer_node;
 } __aligned(sizeof(u64));
 
+static const struct at_xdmac_layout at_xdmac_sama5d4_layout = {
+       .grs = 0x28,
+       .gws = 0x2C,
+       .grws = 0x30,
+       .grwr = 0x34,
+       .gswr = 0x38,
+       .gsws = 0x3C,
+       .gswf = 0x40,
+       .chan_cc_reg_base = 0x50,
+       .sdif = true,
+       .axi_config = false,
+};
+
+static const struct at_xdmac_layout at_xdmac_sama7g5_layout = {
+       .grs = 0x30,
+       .gws = 0x38,
+       .grws = 0x40,
+       .grwr = 0x44,
+       .gswr = 0x48,
+       .gsws = 0x4C,
+       .gswf = 0x50,
+       .chan_cc_reg_base = 0x60,
+       .sdif = false,
+       .axi_config = true,
+};
+
 static inline void __iomem *at_xdmac_chan_reg_base(struct at_xdmac *atxdmac, unsigned int chan_nb)
 {
-       return atxdmac->regs + (AT_XDMAC_CHAN_REG_BASE + chan_nb * 0x40);
+       return atxdmac->regs + (atxdmac->layout->chan_cc_reg_base + chan_nb * 0x40);
 }
 
 #define at_xdmac_read(atxdmac, reg) readl_relaxed((atxdmac)->regs + (reg))
@@ -345,8 +403,10 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,
        first->active_xfer = true;
 
        /* Tell xdmac where to get the first descriptor. */
-       reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys)
-             | AT_XDMAC_CNDA_NDAIF(atchan->memif);
+       reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys);
+       if (atxdmac->layout->sdif)
+               reg |= AT_XDMAC_CNDA_NDAIF(atchan->memif);
+
        at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg);
 
        /*
@@ -541,6 +601,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                                      enum dma_transfer_direction direction)
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
+       struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
        int                     csize, dwidth;
 
        if (direction == DMA_DEV_TO_MEM) {
@@ -548,12 +609,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                        AT91_XDMAC_DT_PERID(atchan->perid)
                        | AT_XDMAC_CC_DAM_INCREMENTED_AM
                        | AT_XDMAC_CC_SAM_FIXED_AM
-                       | AT_XDMAC_CC_DIF(atchan->memif)
-                       | AT_XDMAC_CC_SIF(atchan->perif)
                        | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
                        | AT_XDMAC_CC_DSYNC_PER2MEM
                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                        | AT_XDMAC_CC_TYPE_PER_TRAN;
+               if (atxdmac->layout->sdif)
+                       atchan->cfg |= AT_XDMAC_CC_DIF(atchan->memif) |
+                                      AT_XDMAC_CC_SIF(atchan->perif);
+
                csize = ffs(atchan->sconfig.src_maxburst) - 1;
                if (csize < 0) {
                        dev_err(chan2dev(chan), "invalid src maxburst value\n");
@@ -571,12 +634,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                        AT91_XDMAC_DT_PERID(atchan->perid)
                        | AT_XDMAC_CC_DAM_FIXED_AM
                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                       | AT_XDMAC_CC_DIF(atchan->perif)
-                       | AT_XDMAC_CC_SIF(atchan->memif)
                        | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
                        | AT_XDMAC_CC_DSYNC_MEM2PER
                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                        | AT_XDMAC_CC_TYPE_PER_TRAN;
+               if (atxdmac->layout->sdif)
+                       atchan->cfg |= AT_XDMAC_CC_DIF(atchan->perif) |
+                                      AT_XDMAC_CC_SIF(atchan->memif);
+
                csize = ffs(atchan->sconfig.dst_maxburst) - 1;
                if (csize < 0) {
                        dev_err(chan2dev(chan), "invalid src maxburst value\n");
@@ -866,10 +931,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
 
@@ -1048,12 +1115,14 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_DAM_INCREMENTED_AM
                                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
        unsigned long           irqflags;
@@ -1154,12 +1223,14 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_DAM_UBS_AM
                                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_MEMSET_HW_MODE
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
@@ -1438,7 +1509,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC;
        value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM;
        if ((desc->lld.mbr_cfg & mask) == value) {
-               at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
+               at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
                while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
                        cpu_relax();
        }
@@ -1496,7 +1567,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
         * FIFO flush ensures that data are really written.
         */
        if ((desc->lld.mbr_cfg & mask) == value) {
-               at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
+               at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
                while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
                        cpu_relax();
        }
@@ -1761,7 +1832,7 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
                return 0;
 
        spin_lock_irqsave(&atchan->lock, flags);
-       at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
+       at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
        while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
               & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
                cpu_relax();
@@ -1784,7 +1855,7 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
                return 0;
        }
 
-       at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
+       at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask);
        clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
        spin_unlock_irqrestore(&atchan->lock, flags);
 
@@ -1947,6 +2018,30 @@ static int atmel_xdmac_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
+static void at_xdmac_axi_config(struct platform_device *pdev)
+{
+       struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
+       bool dev_m2m = false;
+       u32 dma_requests;
+
+       if (!atxdmac->layout->axi_config)
+               return; /* Not supported */
+
+       if (!of_property_read_u32(pdev->dev.of_node, "dma-requests",
+                                 &dma_requests)) {
+               dev_info(&pdev->dev, "controller in mem2mem mode.\n");
+               dev_m2m = true;
+       }
+
+       if (dev_m2m) {
+               at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_M2M);
+               at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_M2M);
+       } else {
+               at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_P2M);
+               at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_P2M);
+       }
+}
+
 static int at_xdmac_probe(struct platform_device *pdev)
 {
        struct at_xdmac *atxdmac;
@@ -1986,6 +2081,10 @@ static int at_xdmac_probe(struct platform_device *pdev)
        atxdmac->regs = base;
        atxdmac->irq = irq;
 
+       atxdmac->layout = of_device_get_match_data(&pdev->dev);
+       if (!atxdmac->layout)
+               return -ENODEV;
+
        atxdmac->clk = devm_clk_get(&pdev->dev, "dma_clk");
        if (IS_ERR(atxdmac->clk)) {
                dev_err(&pdev->dev, "can't get dma_clk\n");
@@ -2087,6 +2186,8 @@ static int at_xdmac_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "%d channels, mapped at 0x%p\n",
                 nr_channels, atxdmac->regs);
 
+       at_xdmac_axi_config(pdev);
+
        return 0;
 
 err_dma_unregister:
@@ -2128,6 +2229,10 @@ static const struct dev_pm_ops atmel_xdmac_dev_pm_ops = {
 static const struct of_device_id atmel_xdmac_dt_ids[] = {
        {
                .compatible = "atmel,sama5d4-dma",
+               .data = &at_xdmac_sama5d4_layout,
+       }, {
+               .compatible = "microchip,sama7g5-dma",
+               .data = &at_xdmac_sama7g5_layout,
        }, {
                /* sentinel */
        }
index a608efaa435fb791d9c1c819d84b5e70d25466c4..612d353648cf70c888daaf69ff0bca1db3321500 100644 (file)
@@ -1044,7 +1044,7 @@ static struct platform_driver jz4780_dma_driver = {
        .remove         = jz4780_dma_remove,
        .driver = {
                .name   = "jz4780-dma",
-               .of_match_table = of_match_ptr(jz4780_dma_dt_match),
+               .of_match_table = jz4780_dma_dt_match,
        },
 };
 
index a3a172173e345fc867d1397189b1fbbc8ec3c428..f696246f57fdb3734523930610bc650c52872fdb 100644 (file)
@@ -573,6 +573,7 @@ static int dmatest_func(void *data)
        struct dmatest_params   *params;
        struct dma_chan         *chan;
        struct dma_device       *dev;
+       struct device           *dma_dev;
        unsigned int            error_count;
        unsigned int            failed_tests = 0;
        unsigned int            total_tests = 0;
@@ -606,6 +607,8 @@ static int dmatest_func(void *data)
        params = &info->params;
        chan = thread->chan;
        dev = chan->device;
+       dma_dev = dmaengine_get_dma_device(chan);
+
        src = &thread->src;
        dst = &thread->dst;
        if (thread->type == DMA_MEMCPY) {
@@ -730,7 +733,7 @@ static int dmatest_func(void *data)
                        filltime = ktime_add(filltime, diff);
                }
 
-               um = dmaengine_get_unmap_data(dev->dev, src->cnt + dst->cnt,
+               um = dmaengine_get_unmap_data(dma_dev, src->cnt + dst->cnt,
                                              GFP_KERNEL);
                if (!um) {
                        failed_tests++;
@@ -745,10 +748,10 @@ static int dmatest_func(void *data)
                        struct page *pg = virt_to_page(buf);
                        unsigned long pg_off = offset_in_page(buf);
 
-                       um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
+                       um->addr[i] = dma_map_page(dma_dev, pg, pg_off,
                                                   um->len, DMA_TO_DEVICE);
                        srcs[i] = um->addr[i] + src->off;
-                       ret = dma_mapping_error(dev->dev, um->addr[i]);
+                       ret = dma_mapping_error(dma_dev, um->addr[i]);
                        if (ret) {
                                result("src mapping error", total_tests,
                                       src->off, dst->off, len, ret);
@@ -763,9 +766,9 @@ static int dmatest_func(void *data)
                        struct page *pg = virt_to_page(buf);
                        unsigned long pg_off = offset_in_page(buf);
 
-                       dsts[i] = dma_map_page(dev->dev, pg, pg_off, um->len,
+                       dsts[i] = dma_map_page(dma_dev, pg, pg_off, um->len,
                                               DMA_BIDIRECTIONAL);
-                       ret = dma_mapping_error(dev->dev, dsts[i]);
+                       ret = dma_mapping_error(dma_dev, dsts[i]);
                        if (ret) {
                                result("dst mapping error", total_tests,
                                       src->off, dst->off, len, ret);
index 14c1ac26f8664a5002005af7298488d725979107..e164f3295f5d7acaca24245a719e3383918d8e7c 100644 (file)
@@ -992,7 +992,7 @@ static struct platform_driver dw_driver = {
        .remove         = dw_remove,
        .driver = {
                .name   = KBUILD_MODNAME,
-               .of_match_table = of_match_ptr(dw_dma_of_id_table),
+               .of_match_table = dw_dma_of_id_table,
                .pm = &dw_axi_dma_pm_ops,
        },
 };
index 7ab83fe601ede6ebdd4467036c0437cae6cf2454..19a23767533ac90c690055697949de2873db6153 100644 (file)
@@ -982,8 +982,11 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
 
        dev_vdbg(chan2dev(chan), "%s\n", __func__);
 
+       pm_runtime_get_sync(dw->dma.dev);
+
        /* ASSERT:  channel is idle */
        if (dma_readl(dw, CH_EN) & dwc->mask) {
+               pm_runtime_put_sync_suspend(dw->dma.dev);
                dev_dbg(chan2dev(chan), "DMA channel not idle?\n");
                return -EIO;
        }
@@ -1000,6 +1003,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
         * We need controller-specific data to set up slave transfers.
         */
        if (chan->private && !dw_dma_filter(chan, chan->private)) {
+               pm_runtime_put_sync_suspend(dw->dma.dev);
                dev_warn(chan2dev(chan), "Wrong controller-specific data\n");
                return -EINVAL;
        }
@@ -1043,6 +1047,8 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
        if (!dw->in_use)
                do_dw_dma_off(dw);
 
+       pm_runtime_put_sync_suspend(dw->dma.dev);
+
        dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
 }
 
index e1a958ae7925477b1d7c2ba310721ac046e094cd..a259ee010e9b7eb8f652d705b9a8d0cac4025bbb 100644 (file)
@@ -431,9 +431,8 @@ static irqreturn_t hisi_dma_irq(int irq, void *data)
        struct hisi_dma_dev *hdma_dev = chan->hdma_dev;
        struct hisi_dma_desc *desc;
        struct hisi_dma_cqe *cqe;
-       unsigned long flags;
 
-       spin_lock_irqsave(&chan->vc.lock, flags);
+       spin_lock(&chan->vc.lock);
 
        desc = chan->desc;
        cqe = chan->cq + chan->cq_head;
@@ -452,7 +451,7 @@ static irqreturn_t hisi_dma_irq(int irq, void *data)
                chan->desc = NULL;
        }
 
-       spin_unlock_irqrestore(&chan->vc.lock, flags);
+       spin_unlock(&chan->vc.lock);
 
        return IRQ_HANDLED;
 }
index f5a84c8463945298ebb3cc1cf7354f1f26f2bba0..f4c07ad3be15b427a43b7c9a60e4e5d43f9a9fa2 100644 (file)
@@ -667,9 +667,7 @@ static int idma64_platform_remove(struct platform_device *pdev)
        return idma64_remove(chip);
 }
 
-#ifdef CONFIG_PM_SLEEP
-
-static int idma64_pm_suspend(struct device *dev)
+static int __maybe_unused idma64_pm_suspend(struct device *dev)
 {
        struct idma64_chip *chip = dev_get_drvdata(dev);
 
@@ -677,7 +675,7 @@ static int idma64_pm_suspend(struct device *dev)
        return 0;
 }
 
-static int idma64_pm_resume(struct device *dev)
+static int __maybe_unused idma64_pm_resume(struct device *dev)
 {
        struct idma64_chip *chip = dev_get_drvdata(dev);
 
@@ -685,8 +683,6 @@ static int idma64_pm_resume(struct device *dev)
        return 0;
 }
 
-#endif /* CONFIG_PM_SLEEP */
-
 static const struct dev_pm_ops idma64_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(idma64_pm_suspend, idma64_pm_resume)
 };
index c3976156db2fc0d90fb96399b327414c7a121b77..0db9b82ed8cf57d27f22bbb45420427678c72240 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/cdev.h>
 #include <linux/fs.h>
 #include <linux/poll.h>
+#include <linux/iommu.h>
 #include <uapi/linux/idxd.h>
 #include "registers.h"
 #include "idxd.h"
@@ -27,12 +28,15 @@ struct idxd_cdev_context {
  */
 static struct idxd_cdev_context ictx[IDXD_TYPE_MAX] = {
        { .name = "dsa" },
+       { .name = "iax" }
 };
 
 struct idxd_user_context {
        struct idxd_wq *wq;
        struct task_struct *task;
+       unsigned int pasid;
        unsigned int flags;
+       struct iommu_sva *sva;
 };
 
 enum idxd_cdev_cleanup {
@@ -75,6 +79,8 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
        struct idxd_wq *wq;
        struct device *dev;
        int rc = 0;
+       struct iommu_sva *sva;
+       unsigned int pasid;
 
        wq = inode_wq(inode);
        idxd = wq->idxd;
@@ -95,6 +101,34 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
 
        ctx->wq = wq;
        filp->private_data = ctx;
+
+       if (device_pasid_enabled(idxd)) {
+               sva = iommu_sva_bind_device(dev, current->mm, NULL);
+               if (IS_ERR(sva)) {
+                       rc = PTR_ERR(sva);
+                       dev_err(dev, "pasid allocation failed: %d\n", rc);
+                       goto failed;
+               }
+
+               pasid = iommu_sva_get_pasid(sva);
+               if (pasid == IOMMU_PASID_INVALID) {
+                       iommu_sva_unbind_device(sva);
+                       goto failed;
+               }
+
+               ctx->sva = sva;
+               ctx->pasid = pasid;
+
+               if (wq_dedicated(wq)) {
+                       rc = idxd_wq_set_pasid(wq, pasid);
+                       if (rc < 0) {
+                               iommu_sva_unbind_device(sva);
+                               dev_err(dev, "wq set pasid failed: %d\n", rc);
+                               goto failed;
+                       }
+               }
+       }
+
        idxd_wq_get(wq);
        mutex_unlock(&wq->wq_lock);
        return 0;
@@ -111,13 +145,27 @@ static int idxd_cdev_release(struct inode *node, struct file *filep)
        struct idxd_wq *wq = ctx->wq;
        struct idxd_device *idxd = wq->idxd;
        struct device *dev = &idxd->pdev->dev;
+       int rc;
 
        dev_dbg(dev, "%s called\n", __func__);
        filep->private_data = NULL;
 
        /* Wait for in-flight operations to complete. */
-       idxd_wq_drain(wq);
+       if (wq_shared(wq)) {
+               idxd_device_drain_pasid(idxd, ctx->pasid);
+       } else {
+               if (device_pasid_enabled(idxd)) {
+                       /* The wq disable in the disable pasid function will drain the wq */
+                       rc = idxd_wq_disable_pasid(wq);
+                       if (rc < 0)
+                               dev_err(dev, "wq disable pasid failed.\n");
+               } else {
+                       idxd_wq_drain(wq);
+               }
+       }
 
+       if (ctx->sva)
+               iommu_sva_unbind_device(ctx->sva);
        kfree(ctx);
        mutex_lock(&wq->wq_lock);
        idxd_wq_put(wq);
index 663344987e3f31129359a2ce01602dae904bbe64..95f94a3ed6beb44f466fb511f3a78332a5903687 100644 (file)
@@ -131,6 +131,8 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq)
        struct idxd_device *idxd = wq->idxd;
        struct device *dev = &idxd->pdev->dev;
        int rc, num_descs, i;
+       int align;
+       u64 tmp;
 
        if (wq->type != IDXD_WQT_KERNEL)
                return 0;
@@ -142,14 +144,27 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq)
        if (rc < 0)
                return rc;
 
-       wq->compls_size = num_descs * sizeof(struct dsa_completion_record);
-       wq->compls = dma_alloc_coherent(dev, wq->compls_size,
-                                       &wq->compls_addr, GFP_KERNEL);
-       if (!wq->compls) {
+       if (idxd->type == IDXD_TYPE_DSA)
+               align = 32;
+       else if (idxd->type == IDXD_TYPE_IAX)
+               align = 64;
+       else
+               return -ENODEV;
+
+       wq->compls_size = num_descs * idxd->compl_size + align;
+       wq->compls_raw = dma_alloc_coherent(dev, wq->compls_size,
+                                           &wq->compls_addr_raw, GFP_KERNEL);
+       if (!wq->compls_raw) {
                rc = -ENOMEM;
                goto fail_alloc_compls;
        }
 
+       /* Adjust alignment */
+       wq->compls_addr = (wq->compls_addr_raw + (align - 1)) & ~(align - 1);
+       tmp = (u64)wq->compls_raw;
+       tmp = (tmp + (align - 1)) & ~(align - 1);
+       wq->compls = (struct dsa_completion_record *)tmp;
+
        rc = alloc_descs(wq, num_descs);
        if (rc < 0)
                goto fail_alloc_descs;
@@ -163,9 +178,11 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq)
                struct idxd_desc *desc = wq->descs[i];
 
                desc->hw = wq->hw_descs[i];
-               desc->completion = &wq->compls[i];
-               desc->compl_dma  = wq->compls_addr +
-                       sizeof(struct dsa_completion_record) * i;
+               if (idxd->type == IDXD_TYPE_DSA)
+                       desc->completion = &wq->compls[i];
+               else if (idxd->type == IDXD_TYPE_IAX)
+                       desc->iax_completion = &wq->iax_compls[i];
+               desc->compl_dma = wq->compls_addr + idxd->compl_size * i;
                desc->id = i;
                desc->wq = wq;
                desc->cpu = -1;
@@ -178,7 +195,8 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq)
  fail_sbitmap_init:
        free_descs(wq);
  fail_alloc_descs:
-       dma_free_coherent(dev, wq->compls_size, wq->compls, wq->compls_addr);
+       dma_free_coherent(dev, wq->compls_size, wq->compls_raw,
+                         wq->compls_addr_raw);
  fail_alloc_compls:
        free_hw_descs(wq);
        return rc;
@@ -193,7 +211,8 @@ void idxd_wq_free_resources(struct idxd_wq *wq)
 
        free_hw_descs(wq);
        free_descs(wq);
-       dma_free_coherent(dev, wq->compls_size, wq->compls, wq->compls_addr);
+       dma_free_coherent(dev, wq->compls_size, wq->compls_raw,
+                         wq->compls_addr_raw);
        sbitmap_queue_free(&wq->sbq);
 }
 
@@ -273,10 +292,9 @@ int idxd_wq_map_portal(struct idxd_wq *wq)
        start = pci_resource_start(pdev, IDXD_WQ_BAR);
        start += idxd_get_wq_portal_full_offset(wq->id, IDXD_PORTAL_LIMITED);
 
-       wq->dportal = devm_ioremap(dev, start, IDXD_PORTAL_SIZE);
-       if (!wq->dportal)
+       wq->portal = devm_ioremap(dev, start, IDXD_PORTAL_SIZE);
+       if (!wq->portal)
                return -ENOMEM;
-       dev_dbg(dev, "wq %d portal mapped at %p\n", wq->id, wq->dportal);
 
        return 0;
 }
@@ -285,7 +303,61 @@ void idxd_wq_unmap_portal(struct idxd_wq *wq)
 {
        struct device *dev = &wq->idxd->pdev->dev;
 
-       devm_iounmap(dev, wq->dportal);
+       devm_iounmap(dev, wq->portal);
+}
+
+int idxd_wq_set_pasid(struct idxd_wq *wq, int pasid)
+{
+       struct idxd_device *idxd = wq->idxd;
+       int rc;
+       union wqcfg wqcfg;
+       unsigned int offset;
+       unsigned long flags;
+
+       rc = idxd_wq_disable(wq);
+       if (rc < 0)
+               return rc;
+
+       offset = WQCFG_OFFSET(idxd, wq->id, WQCFG_PASID_IDX);
+       spin_lock_irqsave(&idxd->dev_lock, flags);
+       wqcfg.bits[WQCFG_PASID_IDX] = ioread32(idxd->reg_base + offset);
+       wqcfg.pasid_en = 1;
+       wqcfg.pasid = pasid;
+       iowrite32(wqcfg.bits[WQCFG_PASID_IDX], idxd->reg_base + offset);
+       spin_unlock_irqrestore(&idxd->dev_lock, flags);
+
+       rc = idxd_wq_enable(wq);
+       if (rc < 0)
+               return rc;
+
+       return 0;
+}
+
+int idxd_wq_disable_pasid(struct idxd_wq *wq)
+{
+       struct idxd_device *idxd = wq->idxd;
+       int rc;
+       union wqcfg wqcfg;
+       unsigned int offset;
+       unsigned long flags;
+
+       rc = idxd_wq_disable(wq);
+       if (rc < 0)
+               return rc;
+
+       offset = WQCFG_OFFSET(idxd, wq->id, WQCFG_PASID_IDX);
+       spin_lock_irqsave(&idxd->dev_lock, flags);
+       wqcfg.bits[WQCFG_PASID_IDX] = ioread32(idxd->reg_base + offset);
+       wqcfg.pasid_en = 0;
+       wqcfg.pasid = 0;
+       iowrite32(wqcfg.bits[WQCFG_PASID_IDX], idxd->reg_base + offset);
+       spin_unlock_irqrestore(&idxd->dev_lock, flags);
+
+       rc = idxd_wq_enable(wq);
+       if (rc < 0)
+               return rc;
+
+       return 0;
 }
 
 void idxd_wq_disable_cleanup(struct idxd_wq *wq)
@@ -301,6 +373,7 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq)
        wq->group = NULL;
        wq->threshold = 0;
        wq->priority = 0;
+       wq->ats_dis = 0;
        clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
        memset(wq->name, 0, WQ_NAME_SIZE);
 
@@ -468,6 +541,17 @@ void idxd_device_reset(struct idxd_device *idxd)
        spin_unlock_irqrestore(&idxd->dev_lock, flags);
 }
 
+void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid)
+{
+       struct device *dev = &idxd->pdev->dev;
+       u32 operand;
+
+       operand = pasid;
+       dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_DRAIN_PASID, operand);
+       idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_PASID, operand, NULL);
+       dev_dbg(dev, "pasid %d drained\n", pasid);
+}
+
 /* Device configuration bits */
 static void idxd_group_config_write(struct idxd_group *group)
 {
@@ -479,24 +563,22 @@ static void idxd_group_config_write(struct idxd_group *group)
        dev_dbg(dev, "Writing group %d cfg registers\n", group->id);
 
        /* setup GRPWQCFG */
-       for (i = 0; i < 4; i++) {
-               grpcfg_offset = idxd->grpcfg_offset +
-                       group->id * 64 + i * sizeof(u64);
-               iowrite64(group->grpcfg.wqs[i],
-                         idxd->reg_base + grpcfg_offset);
+       for (i = 0; i < GRPWQCFG_STRIDES; i++) {
+               grpcfg_offset = GRPWQCFG_OFFSET(idxd, group->id, i);
+               iowrite64(group->grpcfg.wqs[i], idxd->reg_base + grpcfg_offset);
                dev_dbg(dev, "GRPCFG wq[%d:%d: %#x]: %#llx\n",
                        group->id, i, grpcfg_offset,
                        ioread64(idxd->reg_base + grpcfg_offset));
        }
 
        /* setup GRPENGCFG */
-       grpcfg_offset = idxd->grpcfg_offset + group->id * 64 + 32;
+       grpcfg_offset = GRPENGCFG_OFFSET(idxd, group->id);
        iowrite64(group->grpcfg.engines, idxd->reg_base + grpcfg_offset);
        dev_dbg(dev, "GRPCFG engs[%d: %#x]: %#llx\n", group->id,
                grpcfg_offset, ioread64(idxd->reg_base + grpcfg_offset));
 
        /* setup GRPFLAGS */
-       grpcfg_offset = idxd->grpcfg_offset + group->id * 64 + 40;
+       grpcfg_offset = GRPFLGCFG_OFFSET(idxd, group->id);
        iowrite32(group->grpcfg.flags.bits, idxd->reg_base + grpcfg_offset);
        dev_dbg(dev, "GRPFLAGS flags[%d: %#x]: %#x\n",
                group->id, grpcfg_offset,
@@ -554,9 +636,24 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
 
        /* byte 8-11 */
        wq->wqcfg->priv = !!(wq->type == IDXD_WQT_KERNEL);
-       wq->wqcfg->mode = 1;
+       if (wq_dedicated(wq))
+               wq->wqcfg->mode = 1;
+
+       if (device_pasid_enabled(idxd)) {
+               wq->wqcfg->pasid_en = 1;
+               if (wq->type == IDXD_WQT_KERNEL && wq_dedicated(wq))
+                       wq->wqcfg->pasid = idxd->pasid;
+       }
+
        wq->wqcfg->priority = wq->priority;
 
+       if (idxd->hw.gen_cap.block_on_fault &&
+           test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags))
+               wq->wqcfg->bof = 1;
+
+       if (idxd->hw.wq_cap.wq_ats_support)
+               wq->wqcfg->wq_ats_disable = wq->ats_dis;
+
        /* bytes 12-15 */
        wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
        wq->wqcfg->max_batch_shift = ilog2(wq->max_batch_size);
@@ -664,8 +761,8 @@ static int idxd_wqs_setup(struct idxd_device *idxd)
                if (!wq->size)
                        continue;
 
-               if (!wq_dedicated(wq)) {
-                       dev_warn(dev, "No shared workqueue support.\n");
+               if (wq_shared(wq) && !device_swq_supported(idxd)) {
+                       dev_warn(dev, "No shared wq support but configured.\n");
                        return -EINVAL;
                }
 
index 0c892cbd72e019251d6e5de364a44b6f6a4cb7db..8ed2773d82859867c0d0f9b91ffc6cb38b226ee5 100644 (file)
@@ -61,8 +61,6 @@ static inline void idxd_prep_desc_common(struct idxd_wq *wq,
                                         u64 addr_f1, u64 addr_f2, u64 len,
                                         u64 compl, u32 flags)
 {
-       struct idxd_device *idxd = wq->idxd;
-
        hw->flags = flags;
        hw->opcode = opcode;
        hw->src_addr = addr_f1;
@@ -70,13 +68,6 @@ static inline void idxd_prep_desc_common(struct idxd_wq *wq,
        hw->xfer_size = len;
        hw->priv = !!(wq->type == IDXD_WQT_KERNEL);
        hw->completion_addr = compl;
-
-       /*
-        * Descriptor completion vectors are 1-8 for MSIX. We will round
-        * robin through the 8 vectors.
-        */
-       wq->vec_ptr = (wq->vec_ptr % idxd->num_wq_irqs) + 1;
-       hw->int_handle =  wq->vec_ptr;
 }
 
 static struct dma_async_tx_descriptor *
index d48f193daacc08bd93a0a95dc4db2f14d91865d1..5a50e91c71bf01fcfee884d8244be1fb6681dcc3 100644 (file)
@@ -20,7 +20,8 @@ extern struct kmem_cache *idxd_desc_pool;
 enum idxd_type {
        IDXD_TYPE_UNKNOWN = -1,
        IDXD_TYPE_DSA = 0,
-       IDXD_TYPE_MAX
+       IDXD_TYPE_IAX,
+       IDXD_TYPE_MAX,
 };
 
 #define IDXD_NAME_SIZE         128
@@ -34,6 +35,11 @@ struct idxd_irq_entry {
        int id;
        struct llist_head pending_llist;
        struct list_head work_list;
+       /*
+        * Lock to protect access between irq thread process descriptor
+        * and irq thread processing error descriptor.
+        */
+       spinlock_t list_lock;
 };
 
 struct idxd_group {
@@ -59,6 +65,7 @@ enum idxd_wq_state {
 
 enum idxd_wq_flag {
        WQ_FLAG_DEDICATED = 0,
+       WQ_FLAG_BLOCK_ON_FAULT,
 };
 
 enum idxd_wq_type {
@@ -86,10 +93,11 @@ enum idxd_op_type {
 enum idxd_complete_type {
        IDXD_COMPLETE_NORMAL = 0,
        IDXD_COMPLETE_ABORT,
+       IDXD_COMPLETE_DEV_FAIL,
 };
 
 struct idxd_wq {
-       void __iomem *dportal;
+       void __iomem *portal;
        struct device conf_dev;
        struct idxd_cdev idxd_cdev;
        struct idxd_device *idxd;
@@ -107,8 +115,13 @@ struct idxd_wq {
        u32 vec_ptr;            /* interrupt steering */
        struct dsa_hw_desc **hw_descs;
        int num_descs;
-       struct dsa_completion_record *compls;
+       union {
+               struct dsa_completion_record *compls;
+               struct iax_completion_record *iax_compls;
+       };
+       void *compls_raw;
        dma_addr_t compls_addr;
+       dma_addr_t compls_addr_raw;
        int compls_size;
        struct idxd_desc **descs;
        struct sbitmap_queue sbq;
@@ -116,6 +129,7 @@ struct idxd_wq {
        char name[WQ_NAME_SIZE + 1];
        u64 max_xfer_bytes;
        u32 max_batch_size;
+       bool ats_dis;
 };
 
 struct idxd_engine {
@@ -145,6 +159,7 @@ enum idxd_device_state {
 enum idxd_device_flag {
        IDXD_FLAG_CONFIGURABLE = 0,
        IDXD_FLAG_CMD_RUNNING,
+       IDXD_FLAG_PASID_ENABLED,
 };
 
 struct idxd_device {
@@ -167,6 +182,9 @@ struct idxd_device {
        struct idxd_wq *wqs;
        struct idxd_engine *engines;
 
+       struct iommu_sva *sva;
+       unsigned int pasid;
+
        int num_groups;
 
        u32 msix_perm_offset;
@@ -184,6 +202,7 @@ struct idxd_device {
        int token_limit;
        int nr_tokens;          /* non-reserved tokens */
        unsigned int wqcfg_size;
+       int compl_size;
 
        union sw_err_reg sw_err;
        wait_queue_head_t cmd_waitq;
@@ -198,9 +217,15 @@ struct idxd_device {
 
 /* IDXD software descriptor */
 struct idxd_desc {
-       struct dsa_hw_desc *hw;
+       union {
+               struct dsa_hw_desc *hw;
+               struct iax_hw_desc *iax_hw;
+       };
        dma_addr_t desc_dma;
-       struct dsa_completion_record *completion;
+       union {
+               struct dsa_completion_record *completion;
+               struct iax_completion_record *iax_completion;
+       };
        dma_addr_t compl_dma;
        struct dma_async_tx_descriptor txd;
        struct llist_node llnode;
@@ -214,12 +239,30 @@ struct idxd_desc {
 #define confdev_to_wq(dev) container_of(dev, struct idxd_wq, conf_dev)
 
 extern struct bus_type dsa_bus_type;
+extern struct bus_type iax_bus_type;
+
+extern bool support_enqcmd;
 
 static inline bool wq_dedicated(struct idxd_wq *wq)
 {
        return test_bit(WQ_FLAG_DEDICATED, &wq->flags);
 }
 
+static inline bool wq_shared(struct idxd_wq *wq)
+{
+       return !test_bit(WQ_FLAG_DEDICATED, &wq->flags);
+}
+
+static inline bool device_pasid_enabled(struct idxd_device *idxd)
+{
+       return test_bit(IDXD_FLAG_PASID_ENABLED, &idxd->flags);
+}
+
+static inline bool device_swq_supported(struct idxd_device *idxd)
+{
+       return (support_enqcmd && device_pasid_enabled(idxd));
+}
+
 enum idxd_portal_prot {
        IDXD_PORTAL_UNLIMITED = 0,
        IDXD_PORTAL_LIMITED,
@@ -242,6 +285,8 @@ static inline void idxd_set_type(struct idxd_device *idxd)
 
        if (pdev->device == PCI_DEVICE_ID_INTEL_DSA_SPR0)
                idxd->type = IDXD_TYPE_DSA;
+       else if (pdev->device == PCI_DEVICE_ID_INTEL_IAX_SPR0)
+               idxd->type = IDXD_TYPE_IAX;
        else
                idxd->type = IDXD_TYPE_UNKNOWN;
 }
@@ -288,6 +333,7 @@ void idxd_device_reset(struct idxd_device *idxd);
 void idxd_device_cleanup(struct idxd_device *idxd);
 int idxd_device_config(struct idxd_device *idxd);
 void idxd_device_wqs_clear_state(struct idxd_device *idxd);
+void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid);
 
 /* work queue control */
 int idxd_wq_alloc_resources(struct idxd_wq *wq);
@@ -298,6 +344,8 @@ void idxd_wq_drain(struct idxd_wq *wq);
 int idxd_wq_map_portal(struct idxd_wq *wq);
 void idxd_wq_unmap_portal(struct idxd_wq *wq);
 void idxd_wq_disable_cleanup(struct idxd_wq *wq);
+int idxd_wq_set_pasid(struct idxd_wq *wq, int pasid);
+int idxd_wq_disable_pasid(struct idxd_wq *wq);
 
 /* submission */
 int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc);
index 0a4432b063b5ceb260415d050b93e823dbedf88d..2c051e07c34c2410458e2590ead84dbdc9f72201 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/device.h>
 #include <linux/idr.h>
+#include <linux/intel-svm.h>
+#include <linux/iommu.h>
 #include <uapi/linux/idxd.h>
 #include <linux/dmaengine.h>
 #include "../dmaengine.h"
@@ -26,18 +28,24 @@ MODULE_AUTHOR("Intel Corporation");
 
 #define DRV_NAME "idxd"
 
+bool support_enqcmd;
+
 static struct idr idxd_idrs[IDXD_TYPE_MAX];
 static struct mutex idxd_idr_lock;
 
 static struct pci_device_id idxd_pci_tbl[] = {
        /* DSA ver 1.0 platforms */
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0) },
+
+       /* IAX ver 1.0 platforms */
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0) },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, idxd_pci_tbl);
 
 static char *idxd_name[] = {
        "dsa",
+       "iax"
 };
 
 const char *idxd_get_dev_name(struct idxd_device *idxd)
@@ -53,6 +61,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
        struct idxd_irq_entry *irq_entry;
        int i, msixcnt;
        int rc = 0;
+       union msix_perm mperm;
 
        msixcnt = pci_msix_vec_count(pdev);
        if (msixcnt < 0) {
@@ -92,6 +101,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
        for (i = 0; i < msixcnt; i++) {
                idxd->irq_entries[i].id = i;
                idxd->irq_entries[i].idxd = idxd;
+               spin_lock_init(&idxd->irq_entries[i].list_lock);
        }
 
        msix = &idxd->msix_entries[0];
@@ -131,6 +141,13 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
 
        idxd_unmask_error_interrupts(idxd);
 
+       /* Setup MSIX permission table */
+       mperm.bits = 0;
+       mperm.pasid = idxd->pasid;
+       mperm.pasid_en = device_pasid_enabled(idxd);
+       for (i = 1; i < msixcnt; i++)
+               iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
+
        return 0;
 
  err_no_irq:
@@ -201,17 +218,14 @@ static void idxd_read_table_offsets(struct idxd_device *idxd)
        struct device *dev = &idxd->pdev->dev;
 
        offsets.bits[0] = ioread64(idxd->reg_base + IDXD_TABLE_OFFSET);
-       offsets.bits[1] = ioread64(idxd->reg_base + IDXD_TABLE_OFFSET
-                       + sizeof(u64));
-       idxd->grpcfg_offset = offsets.grpcfg * 0x100;
+       offsets.bits[1] = ioread64(idxd->reg_base + IDXD_TABLE_OFFSET + sizeof(u64));
+       idxd->grpcfg_offset = offsets.grpcfg * IDXD_TABLE_MULT;
        dev_dbg(dev, "IDXD Group Config Offset: %#x\n", idxd->grpcfg_offset);
-       idxd->wqcfg_offset = offsets.wqcfg * 0x100;
-       dev_dbg(dev, "IDXD Work Queue Config Offset: %#x\n",
-               idxd->wqcfg_offset);
-       idxd->msix_perm_offset = offsets.msix_perm * 0x100;
-       dev_dbg(dev, "IDXD MSIX Permission Offset: %#x\n",
-               idxd->msix_perm_offset);
-       idxd->perfmon_offset = offsets.perfmon * 0x100;
+       idxd->wqcfg_offset = offsets.wqcfg * IDXD_TABLE_MULT;
+       dev_dbg(dev, "IDXD Work Queue Config Offset: %#x\n", idxd->wqcfg_offset);
+       idxd->msix_perm_offset = offsets.msix_perm * IDXD_TABLE_MULT;
+       dev_dbg(dev, "IDXD MSIX Permission Offset: %#x\n", idxd->msix_perm_offset);
+       idxd->perfmon_offset = offsets.perfmon * IDXD_TABLE_MULT;
        dev_dbg(dev, "IDXD Perfmon Offset: %#x\n", idxd->perfmon_offset);
 }
 
@@ -265,8 +279,7 @@ static void idxd_read_caps(struct idxd_device *idxd)
        }
 }
 
-static struct idxd_device *idxd_alloc(struct pci_dev *pdev,
-                                     void __iomem * const *iomap)
+static struct idxd_device *idxd_alloc(struct pci_dev *pdev)
 {
        struct device *dev = &pdev->dev;
        struct idxd_device *idxd;
@@ -276,12 +289,45 @@ static struct idxd_device *idxd_alloc(struct pci_dev *pdev,
                return NULL;
 
        idxd->pdev = pdev;
-       idxd->reg_base = iomap[IDXD_MMIO_BAR];
        spin_lock_init(&idxd->dev_lock);
 
        return idxd;
 }
 
+static int idxd_enable_system_pasid(struct idxd_device *idxd)
+{
+       int flags;
+       unsigned int pasid;
+       struct iommu_sva *sva;
+
+       flags = SVM_FLAG_SUPERVISOR_MODE;
+
+       sva = iommu_sva_bind_device(&idxd->pdev->dev, NULL, &flags);
+       if (IS_ERR(sva)) {
+               dev_warn(&idxd->pdev->dev,
+                        "iommu sva bind failed: %ld\n", PTR_ERR(sva));
+               return PTR_ERR(sva);
+       }
+
+       pasid = iommu_sva_get_pasid(sva);
+       if (pasid == IOMMU_PASID_INVALID) {
+               iommu_sva_unbind_device(sva);
+               return -ENODEV;
+       }
+
+       idxd->sva = sva;
+       idxd->pasid = pasid;
+       dev_dbg(&idxd->pdev->dev, "system pasid: %u\n", pasid);
+       return 0;
+}
+
+static void idxd_disable_system_pasid(struct idxd_device *idxd)
+{
+
+       iommu_sva_unbind_device(idxd->sva);
+       idxd->sva = NULL;
+}
+
 static int idxd_probe(struct idxd_device *idxd)
 {
        struct pci_dev *pdev = idxd->pdev;
@@ -292,6 +338,14 @@ static int idxd_probe(struct idxd_device *idxd)
        idxd_device_init_reset(idxd);
        dev_dbg(dev, "IDXD reset complete\n");
 
+       if (IS_ENABLED(CONFIG_INTEL_IDXD_SVM)) {
+               rc = idxd_enable_system_pasid(idxd);
+               if (rc < 0)
+                       dev_warn(dev, "Failed to enable PASID. No SVA support: %d\n", rc);
+               else
+                       set_bit(IDXD_FLAG_PASID_ENABLED, &idxd->flags);
+       }
+
        idxd_read_caps(idxd);
        idxd_read_table_offsets(idxd);
 
@@ -322,29 +376,37 @@ static int idxd_probe(struct idxd_device *idxd)
        idxd_mask_error_interrupts(idxd);
        idxd_mask_msix_vectors(idxd);
  err_setup:
+       if (device_pasid_enabled(idxd))
+               idxd_disable_system_pasid(idxd);
        return rc;
 }
 
+static void idxd_type_init(struct idxd_device *idxd)
+{
+       if (idxd->type == IDXD_TYPE_DSA)
+               idxd->compl_size = sizeof(struct dsa_completion_record);
+       else if (idxd->type == IDXD_TYPE_IAX)
+               idxd->compl_size = sizeof(struct iax_completion_record);
+}
+
 static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-       void __iomem * const *iomap;
        struct device *dev = &pdev->dev;
        struct idxd_device *idxd;
        int rc;
-       unsigned int mask;
 
        rc = pcim_enable_device(pdev);
        if (rc)
                return rc;
 
-       dev_dbg(dev, "Mapping BARs\n");
-       mask = (1 << IDXD_MMIO_BAR);
-       rc = pcim_iomap_regions(pdev, mask, DRV_NAME);
-       if (rc)
-               return rc;
+       dev_dbg(dev, "Alloc IDXD context\n");
+       idxd = idxd_alloc(pdev);
+       if (!idxd)
+               return -ENOMEM;
 
-       iomap = pcim_iomap_table(pdev);
-       if (!iomap)
+       dev_dbg(dev, "Mapping BARs\n");
+       idxd->reg_base = pcim_iomap(pdev, IDXD_MMIO_BAR, 0);
+       if (!idxd->reg_base)
                return -ENOMEM;
 
        dev_dbg(dev, "Set DMA masks\n");
@@ -360,13 +422,10 @@ static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (rc)
                return rc;
 
-       dev_dbg(dev, "Alloc IDXD context\n");
-       idxd = idxd_alloc(pdev, iomap);
-       if (!idxd)
-               return -ENOMEM;
-
        idxd_set_type(idxd);
 
+       idxd_type_init(idxd);
+
        dev_dbg(dev, "Set PCI master\n");
        pci_set_master(pdev);
        pci_set_drvdata(pdev, idxd);
@@ -452,6 +511,8 @@ static void idxd_remove(struct pci_dev *pdev)
        dev_dbg(&pdev->dev, "%s called\n", __func__);
        idxd_cleanup_sysfs(idxd);
        idxd_shutdown(pdev);
+       if (device_pasid_enabled(idxd))
+               idxd_disable_system_pasid(idxd);
        mutex_lock(&idxd_idr_lock);
        idr_remove(&idxd_idrs[idxd->type], idxd->id);
        mutex_unlock(&idxd_idr_lock);
@@ -470,7 +531,7 @@ static int __init idxd_init_module(void)
        int err, i;
 
        /*
-        * If the CPU does not support write512, there's no point in
+        * If the CPU does not support MOVDIR64B or ENQCMDS, there's no point in
         * enumerating the device. We can not utilize it.
         */
        if (!boot_cpu_has(X86_FEATURE_MOVDIR64B)) {
@@ -478,8 +539,10 @@ static int __init idxd_init_module(void)
                return -ENODEV;
        }
 
-       pr_info("%s: Intel(R) Accelerator Devices Driver %s\n",
-               DRV_NAME, IDXD_DRIVER_VERSION);
+       if (!boot_cpu_has(X86_FEATURE_ENQCMD))
+               pr_warn("Platform does not have ENQCMD(S) support.\n");
+       else
+               support_enqcmd = true;
 
        mutex_init(&idxd_idr_lock);
        for (i = 0; i < IDXD_TYPE_MAX; i++)
index 17a65a13fb649472b3d2fc1f28561f3ef4f3109e..593a2f6ed16cb64e8bdf6dd5eaf5c92b596be984 100644 (file)
 #include "idxd.h"
 #include "registers.h"
 
+enum irq_work_type {
+       IRQ_WORK_NORMAL = 0,
+       IRQ_WORK_PROCESS_FAULT,
+};
+
+struct idxd_fault {
+       struct work_struct work;
+       u64 addr;
+       struct idxd_device *idxd;
+};
+
+static int irq_process_work_list(struct idxd_irq_entry *irq_entry,
+                                enum irq_work_type wtype,
+                                int *processed, u64 data);
+static int irq_process_pending_llist(struct idxd_irq_entry *irq_entry,
+                                    enum irq_work_type wtype,
+                                    int *processed, u64 data);
+
 static void idxd_device_reinit(struct work_struct *work)
 {
        struct idxd_device *idxd = container_of(work, struct idxd_device, work);
@@ -44,6 +62,46 @@ static void idxd_device_reinit(struct work_struct *work)
        idxd_device_wqs_clear_state(idxd);
 }
 
+static void idxd_device_fault_work(struct work_struct *work)
+{
+       struct idxd_fault *fault = container_of(work, struct idxd_fault, work);
+       struct idxd_irq_entry *ie;
+       int i;
+       int processed;
+       int irqcnt = fault->idxd->num_wq_irqs + 1;
+
+       for (i = 1; i < irqcnt; i++) {
+               ie = &fault->idxd->irq_entries[i];
+               irq_process_work_list(ie, IRQ_WORK_PROCESS_FAULT,
+                                     &processed, fault->addr);
+               if (processed)
+                       break;
+
+               irq_process_pending_llist(ie, IRQ_WORK_PROCESS_FAULT,
+                                         &processed, fault->addr);
+               if (processed)
+                       break;
+       }
+
+       kfree(fault);
+}
+
+static int idxd_device_schedule_fault_process(struct idxd_device *idxd,
+                                             u64 fault_addr)
+{
+       struct idxd_fault *fault;
+
+       fault = kmalloc(sizeof(*fault), GFP_ATOMIC);
+       if (!fault)
+               return -ENOMEM;
+
+       fault->addr = fault_addr;
+       fault->idxd = idxd;
+       INIT_WORK(&fault->work, idxd_device_fault_work);
+       queue_work(idxd->wq, &fault->work);
+       return 0;
+}
+
 irqreturn_t idxd_irq_handler(int vec, void *data)
 {
        struct idxd_irq_entry *irq_entry = data;
@@ -125,6 +183,15 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
        if (!err)
                goto out;
 
+       /*
+        * This case should rarely happen and typically is due to software
+        * programming error by the driver.
+        */
+       if (idxd->sw_err.valid &&
+           idxd->sw_err.desc_valid &&
+           idxd->sw_err.fault_addr)
+               idxd_device_schedule_fault_process(idxd, idxd->sw_err.fault_addr);
+
        gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
        if (gensts.state == IDXD_DEVICE_STATE_HALT) {
                idxd->state = IDXD_DEV_HALTED;
@@ -152,57 +219,110 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
        return IRQ_HANDLED;
 }
 
+static bool process_fault(struct idxd_desc *desc, u64 fault_addr)
+{
+       /*
+        * Completion address can be bad as well. Check fault address match for descriptor
+        * and completion address.
+        */
+       if ((u64)desc->hw == fault_addr ||
+           (u64)desc->completion == fault_addr) {
+               idxd_dma_complete_txd(desc, IDXD_COMPLETE_DEV_FAIL);
+               return true;
+       }
+
+       return false;
+}
+
+static bool complete_desc(struct idxd_desc *desc)
+{
+       if (desc->completion->status) {
+               idxd_dma_complete_txd(desc, IDXD_COMPLETE_NORMAL);
+               return true;
+       }
+
+       return false;
+}
+
 static int irq_process_pending_llist(struct idxd_irq_entry *irq_entry,
-                                    int *processed)
+                                    enum irq_work_type wtype,
+                                    int *processed, u64 data)
 {
        struct idxd_desc *desc, *t;
        struct llist_node *head;
        int queued = 0;
+       bool completed = false;
+       unsigned long flags;
 
        *processed = 0;
        head = llist_del_all(&irq_entry->pending_llist);
        if (!head)
-               return 0;
+               goto out;
 
        llist_for_each_entry_safe(desc, t, head, llnode) {
-               if (desc->completion->status) {
-                       idxd_dma_complete_txd(desc, IDXD_COMPLETE_NORMAL);
+               if (wtype == IRQ_WORK_NORMAL)
+                       completed = complete_desc(desc);
+               else if (wtype == IRQ_WORK_PROCESS_FAULT)
+                       completed = process_fault(desc, data);
+
+               if (completed) {
                        idxd_free_desc(desc->wq, desc);
                        (*processed)++;
+                       if (wtype == IRQ_WORK_PROCESS_FAULT)
+                               break;
                } else {
-                       list_add_tail(&desc->list, &irq_entry->work_list);
+                       spin_lock_irqsave(&irq_entry->list_lock, flags);
+                       list_add_tail(&desc->list,
+                                     &irq_entry->work_list);
+                       spin_unlock_irqrestore(&irq_entry->list_lock, flags);
                        queued++;
                }
        }
 
+ out:
        return queued;
 }
 
 static int irq_process_work_list(struct idxd_irq_entry *irq_entry,
-                                int *processed)
+                                enum irq_work_type wtype,
+                                int *processed, u64 data)
 {
        struct list_head *node, *next;
        int queued = 0;
+       bool completed = false;
+       unsigned long flags;
 
        *processed = 0;
+       spin_lock_irqsave(&irq_entry->list_lock, flags);
        if (list_empty(&irq_entry->work_list))
-               return 0;
+               goto out;
 
        list_for_each_safe(node, next, &irq_entry->work_list) {
                struct idxd_desc *desc =
                        container_of(node, struct idxd_desc, list);
 
-               if (desc->completion->status) {
+               spin_unlock_irqrestore(&irq_entry->list_lock, flags);
+               if (wtype == IRQ_WORK_NORMAL)
+                       completed = complete_desc(desc);
+               else if (wtype == IRQ_WORK_PROCESS_FAULT)
+                       completed = process_fault(desc, data);
+
+               if (completed) {
+                       spin_lock_irqsave(&irq_entry->list_lock, flags);
                        list_del(&desc->list);
-                       /* process and callback */
-                       idxd_dma_complete_txd(desc, IDXD_COMPLETE_NORMAL);
+                       spin_unlock_irqrestore(&irq_entry->list_lock, flags);
                        idxd_free_desc(desc->wq, desc);
                        (*processed)++;
+                       if (wtype == IRQ_WORK_PROCESS_FAULT)
+                               return queued;
                } else {
                        queued++;
                }
+               spin_lock_irqsave(&irq_entry->list_lock, flags);
        }
 
+ out:
+       spin_unlock_irqrestore(&irq_entry->list_lock, flags);
        return queued;
 }
 
@@ -230,12 +350,14 @@ static int idxd_desc_process(struct idxd_irq_entry *irq_entry)
         * 5. Repeat until no more descriptors.
         */
        do {
-               rc = irq_process_work_list(irq_entry, &processed);
+               rc = irq_process_work_list(irq_entry, IRQ_WORK_NORMAL,
+                                          &processed, 0);
                total += processed;
                if (rc != 0)
                        continue;
 
-               rc = irq_process_pending_llist(irq_entry, &processed);
+               rc = irq_process_pending_llist(irq_entry, IRQ_WORK_NORMAL,
+                                              &processed, 0);
                total += processed;
        } while (rc != 0);
 
index 54390334c243a01b448dbd09f411c68d6a361c9e..751ecb4f9f810e60023a252152490ecdaf733c9e 100644 (file)
@@ -5,6 +5,7 @@
 
 /* PCI Config */
 #define PCI_DEVICE_ID_INTEL_DSA_SPR0   0x0b25
+#define PCI_DEVICE_ID_INTEL_IAX_SPR0   0x0cfe
 
 #define IDXD_MMIO_BAR          0
 #define IDXD_WQ_BAR            2
@@ -47,7 +48,7 @@ union wq_cap_reg {
                u64 rsvd:20;
                u64 shared_mode:1;
                u64 dedicated_mode:1;
-               u64 rsvd2:1;
+               u64 wq_ats_support:1;
                u64 priority:1;
                u64 occupancy:1;
                u64 occupancy_int:1;
@@ -102,6 +103,8 @@ union offsets_reg {
        u64 bits[2];
 } __packed;
 
+#define IDXD_TABLE_MULT                        0x100
+
 #define IDXD_GENCFG_OFFSET             0x80
 union gencfg_reg {
        struct {
@@ -301,7 +304,8 @@ union wqcfg {
                /* bytes 8-11 */
                u32 mode:1;     /* shared or dedicated */
                u32 bof:1;      /* block on fault */
-               u32 rsvd2:2;
+               u32 wq_ats_disable:1;
+               u32 rsvd2:1;
                u32 priority:4;
                u32 pasid:20;
                u32 pasid_en:1;
@@ -336,6 +340,8 @@ union wqcfg {
        u32 bits[8];
 } __packed;
 
+#define WQCFG_PASID_IDX                2
+
 /*
  * This macro calculates the offset into the WQCFG register
  * idxd - struct idxd *
@@ -354,4 +360,22 @@ union wqcfg {
 
 #define WQCFG_STRIDES(_idxd_dev) ((_idxd_dev)->wqcfg_size / sizeof(u32))
 
+#define GRPCFG_SIZE            64
+#define GRPWQCFG_STRIDES       4
+
+/*
+ * This macro calculates the offset into the GRPCFG register
+ * idxd - struct idxd *
+ * n - wq id
+ * ofs - the index of the 32b dword for the config register
+ *
+ * The WQCFG register block is divided into groups per each wq. The n index
+ * allows us to move to the register group that's for that particular wq.
+ * Each register is 32bits. The ofs gives us the number of register to access.
+ */
+#define GRPWQCFG_OFFSET(idxd_dev, n, ofs) ((idxd_dev)->grpcfg_offset +\
+                                          (n) * GRPCFG_SIZE + sizeof(u64) * (ofs))
+#define GRPENGCFG_OFFSET(idxd_dev, n) ((idxd_dev)->grpcfg_offset + (n) * GRPCFG_SIZE + 32)
+#define GRPFLGCFG_OFFSET(idxd_dev, n) ((idxd_dev)->grpcfg_offset + (n) * GRPCFG_SIZE + 40)
+
 #endif
index 417048e3c42aa88cdb27288625695720407018a1..a7a61bcc17d58ff45b88db9e47f00c19c2ddfbb4 100644 (file)
 static struct idxd_desc *__get_desc(struct idxd_wq *wq, int idx, int cpu)
 {
        struct idxd_desc *desc;
+       struct idxd_device *idxd = wq->idxd;
 
        desc = wq->descs[idx];
        memset(desc->hw, 0, sizeof(struct dsa_hw_desc));
-       memset(desc->completion, 0, sizeof(struct dsa_completion_record));
+       memset(desc->completion, 0, idxd->compl_size);
        desc->cpu = cpu;
+
+       if (device_pasid_enabled(idxd))
+               desc->hw->pasid = idxd->pasid;
+
+       /*
+        * Descriptor completion vectors are 1-8 for MSIX. We will round
+        * robin through the 8 vectors.
+        */
+       wq->vec_ptr = (wq->vec_ptr % idxd->num_wq_irqs) + 1;
+       desc->hw->int_handle = wq->vec_ptr;
        return desc;
 }
 
@@ -70,18 +81,32 @@ int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc)
        struct idxd_device *idxd = wq->idxd;
        int vec = desc->hw->int_handle;
        void __iomem *portal;
+       int rc;
 
        if (idxd->state != IDXD_DEV_ENABLED)
                return -EIO;
 
-       portal = wq->dportal;
+       portal = wq->portal;
+
        /*
-        * The wmb() flushes writes to coherent DMA data before possibly
-        * triggering a DMA read. The wmb() is necessary even on UP because
-        * the recipient is a device.
+        * The wmb() flushes writes to coherent DMA data before
+        * possibly triggering a DMA read. The wmb() is necessary
+        * even on UP because the recipient is a device.
         */
        wmb();
-       iosubmit_cmds512(portal, desc->hw, 1);
+       if (wq_dedicated(wq)) {
+               iosubmit_cmds512(portal, desc->hw, 1);
+       } else {
+               /*
+                * It's not likely that we would receive queue full rejection
+                * since the descriptor allocation gates at wq size. If we
+                * receive a -EAGAIN, that means something went wrong such as the
+                * device is not accepting descriptor at all.
+                */
+               rc = enqcmds(portal, desc->hw);
+               if (rc < 0)
+                       return rc;
+       }
 
        /*
         * Pending the descriptor to the lockless list for the irq_entry
index 07a5db06a29ad3738eab05717933b674f2fa0769..266423a2cabc7ddfa773d7e7e198f904fa77069c 100644 (file)
@@ -41,14 +41,24 @@ static struct device_type dsa_device_type = {
        .release = idxd_conf_device_release,
 };
 
+static struct device_type iax_device_type = {
+       .name = "iax",
+       .release = idxd_conf_device_release,
+};
+
 static inline bool is_dsa_dev(struct device *dev)
 {
        return dev ? dev->type == &dsa_device_type : false;
 }
 
+static inline bool is_iax_dev(struct device *dev)
+{
+       return dev ? dev->type == &iax_device_type : false;
+}
+
 static inline bool is_idxd_dev(struct device *dev)
 {
-       return is_dsa_dev(dev);
+       return is_dsa_dev(dev) || is_iax_dev(dev);
 }
 
 static inline bool is_idxd_wq_dev(struct device *dev)
@@ -175,6 +185,30 @@ static int idxd_config_bus_probe(struct device *dev)
                        return -EINVAL;
                }
 
+               /* Shared WQ checks */
+               if (wq_shared(wq)) {
+                       if (!device_swq_supported(idxd)) {
+                               dev_warn(dev,
+                                        "PASID not enabled and shared WQ.\n");
+                               mutex_unlock(&wq->wq_lock);
+                               return -ENXIO;
+                       }
+                       /*
+                        * Shared wq with the threshold set to 0 means the user
+                        * did not set the threshold or transitioned from a
+                        * dedicated wq but did not set threshold. A value
+                        * of 0 would effectively disable the shared wq. The
+                        * driver does not allow a value of 0 to be set for
+                        * threshold via sysfs.
+                        */
+                       if (wq->threshold == 0) {
+                               dev_warn(dev,
+                                        "Shared WQ and threshold 0.\n");
+                               mutex_unlock(&wq->wq_lock);
+                               return -EINVAL;
+                       }
+               }
+
                rc = idxd_wq_alloc_resources(wq);
                if (rc < 0) {
                        mutex_unlock(&wq->wq_lock);
@@ -335,8 +369,17 @@ struct bus_type dsa_bus_type = {
        .shutdown = idxd_config_bus_shutdown,
 };
 
+struct bus_type iax_bus_type = {
+       .name = "iax",
+       .match = idxd_config_bus_match,
+       .probe = idxd_config_bus_probe,
+       .remove = idxd_config_bus_remove,
+       .shutdown = idxd_config_bus_shutdown,
+};
+
 static struct bus_type *idxd_bus_types[] = {
-       &dsa_bus_type
+       &dsa_bus_type,
+       &iax_bus_type
 };
 
 static struct idxd_device_driver dsa_drv = {
@@ -348,8 +391,18 @@ static struct idxd_device_driver dsa_drv = {
        },
 };
 
+static struct idxd_device_driver iax_drv = {
+       .drv = {
+               .name = "iax",
+               .bus = &iax_bus_type,
+               .owner = THIS_MODULE,
+               .mod_name = KBUILD_MODNAME,
+       },
+};
+
 static struct idxd_device_driver *idxd_drvs[] = {
-       &dsa_drv
+       &dsa_drv,
+       &iax_drv
 };
 
 struct bus_type *idxd_get_bus_type(struct idxd_device *idxd)
@@ -361,6 +414,8 @@ static struct device_type *idxd_get_device_type(struct idxd_device *idxd)
 {
        if (idxd->type == IDXD_TYPE_DSA)
                return &dsa_device_type;
+       else if (idxd->type == IDXD_TYPE_IAX)
+               return &iax_device_type;
        else
                return NULL;
 }
@@ -501,6 +556,9 @@ static ssize_t group_tokens_reserved_store(struct device *dev,
        if (rc < 0)
                return -EINVAL;
 
+       if (idxd->type == IDXD_TYPE_IAX)
+               return -EOPNOTSUPP;
+
        if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
                return -EPERM;
 
@@ -546,6 +604,9 @@ static ssize_t group_tokens_allowed_store(struct device *dev,
        if (rc < 0)
                return -EINVAL;
 
+       if (idxd->type == IDXD_TYPE_IAX)
+               return -EOPNOTSUPP;
+
        if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
                return -EPERM;
 
@@ -588,6 +649,9 @@ static ssize_t group_use_token_limit_store(struct device *dev,
        if (rc < 0)
                return -EINVAL;
 
+       if (idxd->type == IDXD_TYPE_IAX)
+               return -EOPNOTSUPP;
+
        if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
                return -EPERM;
 
@@ -875,6 +939,8 @@ static ssize_t wq_mode_store(struct device *dev,
        if (sysfs_streq(buf, "dedicated")) {
                set_bit(WQ_FLAG_DEDICATED, &wq->flags);
                wq->threshold = 0;
+       } else if (sysfs_streq(buf, "shared") && device_swq_supported(idxd)) {
+               clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
        } else {
                return -EINVAL;
        }
@@ -973,6 +1039,87 @@ static ssize_t wq_priority_store(struct device *dev,
 static struct device_attribute dev_attr_wq_priority =
                __ATTR(priority, 0644, wq_priority_show, wq_priority_store);
 
+static ssize_t wq_block_on_fault_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+
+       return sprintf(buf, "%u\n",
+                      test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags));
+}
+
+static ssize_t wq_block_on_fault_store(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+       struct idxd_device *idxd = wq->idxd;
+       bool bof;
+       int rc;
+
+       if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
+               return -EPERM;
+
+       if (wq->state != IDXD_WQ_DISABLED)
+               return -ENXIO;
+
+       rc = kstrtobool(buf, &bof);
+       if (rc < 0)
+               return rc;
+
+       if (bof)
+               set_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags);
+       else
+               clear_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags);
+
+       return count;
+}
+
+static struct device_attribute dev_attr_wq_block_on_fault =
+               __ATTR(block_on_fault, 0644, wq_block_on_fault_show,
+                      wq_block_on_fault_store);
+
+static ssize_t wq_threshold_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+
+       return sprintf(buf, "%u\n", wq->threshold);
+}
+
+static ssize_t wq_threshold_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+       struct idxd_device *idxd = wq->idxd;
+       unsigned int val;
+       int rc;
+
+       rc = kstrtouint(buf, 0, &val);
+       if (rc < 0)
+               return -EINVAL;
+
+       if (val > wq->size || val <= 0)
+               return -EINVAL;
+
+       if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
+               return -EPERM;
+
+       if (wq->state != IDXD_WQ_DISABLED)
+               return -ENXIO;
+
+       if (test_bit(WQ_FLAG_DEDICATED, &wq->flags))
+               return -EINVAL;
+
+       wq->threshold = val;
+
+       return count;
+}
+
+static struct device_attribute dev_attr_wq_threshold =
+               __ATTR(threshold, 0644, wq_threshold_show, wq_threshold_store);
+
 static ssize_t wq_type_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -1044,6 +1191,13 @@ static ssize_t wq_name_store(struct device *dev,
        if (strlen(buf) > WQ_NAME_SIZE || strlen(buf) == 0)
                return -EINVAL;
 
+       /*
+        * This is temporarily placed here until we have SVM support for
+        * dmaengine.
+        */
+       if (wq->type == IDXD_WQT_KERNEL && device_pasid_enabled(wq->idxd))
+               return -EOPNOTSUPP;
+
        memset(wq->name, 0, WQ_NAME_SIZE + 1);
        strncpy(wq->name, buf, WQ_NAME_SIZE);
        strreplace(wq->name, '\n', '\0');
@@ -1147,6 +1301,39 @@ static ssize_t wq_max_batch_size_store(struct device *dev, struct device_attribu
 static struct device_attribute dev_attr_wq_max_batch_size =
                __ATTR(max_batch_size, 0644, wq_max_batch_size_show, wq_max_batch_size_store);
 
+static ssize_t wq_ats_disable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+
+       return sprintf(buf, "%u\n", wq->ats_dis);
+}
+
+static ssize_t wq_ats_disable_store(struct device *dev, struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct idxd_wq *wq = container_of(dev, struct idxd_wq, conf_dev);
+       struct idxd_device *idxd = wq->idxd;
+       bool ats_dis;
+       int rc;
+
+       if (wq->state != IDXD_WQ_DISABLED)
+               return -EPERM;
+
+       if (!idxd->hw.wq_cap.wq_ats_support)
+               return -EOPNOTSUPP;
+
+       rc = kstrtobool(buf, &ats_dis);
+       if (rc < 0)
+               return rc;
+
+       wq->ats_dis = ats_dis;
+
+       return count;
+}
+
+static struct device_attribute dev_attr_wq_ats_disable =
+               __ATTR(ats_disable, 0644, wq_ats_disable_show, wq_ats_disable_store);
+
 static struct attribute *idxd_wq_attributes[] = {
        &dev_attr_wq_clients.attr,
        &dev_attr_wq_state.attr,
@@ -1154,11 +1341,14 @@ static struct attribute *idxd_wq_attributes[] = {
        &dev_attr_wq_mode.attr,
        &dev_attr_wq_size.attr,
        &dev_attr_wq_priority.attr,
+       &dev_attr_wq_block_on_fault.attr,
+       &dev_attr_wq_threshold.attr,
        &dev_attr_wq_type.attr,
        &dev_attr_wq_name.attr,
        &dev_attr_wq_cdev_minor.attr,
        &dev_attr_wq_max_transfer_size.attr,
        &dev_attr_wq_max_batch_size.attr,
+       &dev_attr_wq_ats_disable.attr,
        NULL,
 };
 
@@ -1305,6 +1495,16 @@ static ssize_t clients_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(clients);
 
+static ssize_t pasid_enabled_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct idxd_device *idxd =
+               container_of(dev, struct idxd_device, conf_dev);
+
+       return sprintf(buf, "%u\n", device_pasid_enabled(idxd));
+}
+static DEVICE_ATTR_RO(pasid_enabled);
+
 static ssize_t state_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
 {
@@ -1424,6 +1624,7 @@ static struct attribute *idxd_device_attributes[] = {
        &dev_attr_gen_cap.attr,
        &dev_attr_configurable.attr,
        &dev_attr_clients.attr,
+       &dev_attr_pasid_enabled.attr,
        &dev_attr_state.attr,
        &dev_attr_errors.attr,
        &dev_attr_max_tokens.attr,
index 670db04b07571852ba0f0fda29fdb6ade9a38e54..7f116bbcfad2ae1b6cda62529bf29059e58e70a2 100644 (file)
@@ -191,32 +191,13 @@ struct imxdma_filter_data {
        int                      request;
 };
 
-static const struct platform_device_id imx_dma_devtype[] = {
-       {
-               .name = "imx1-dma",
-               .driver_data = IMX1_DMA,
-       }, {
-               .name = "imx21-dma",
-               .driver_data = IMX21_DMA,
-       }, {
-               .name = "imx27-dma",
-               .driver_data = IMX27_DMA,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, imx_dma_devtype);
-
 static const struct of_device_id imx_dma_of_dev_id[] = {
        {
-               .compatible = "fsl,imx1-dma",
-               .data = &imx_dma_devtype[IMX1_DMA],
+               .compatible = "fsl,imx1-dma", .data = (const void *)IMX1_DMA,
        }, {
-               .compatible = "fsl,imx21-dma",
-               .data = &imx_dma_devtype[IMX21_DMA],
+               .compatible = "fsl,imx21-dma", .data = (const void *)IMX21_DMA,
        }, {
-               .compatible = "fsl,imx27-dma",
-               .data = &imx_dma_devtype[IMX27_DMA],
+               .compatible = "fsl,imx27-dma", .data = (const void *)IMX27_DMA,
        }, {
                /* sentinel */
        }
@@ -1056,20 +1037,15 @@ static int __init imxdma_probe(struct platform_device *pdev)
 {
        struct imxdma_engine *imxdma;
        struct resource *res;
-       const struct of_device_id *of_id;
        int ret, i;
        int irq, irq_err;
 
-       of_id = of_match_device(imx_dma_of_dev_id, &pdev->dev);
-       if (of_id)
-               pdev->id_entry = of_id->data;
-
        imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL);
        if (!imxdma)
                return -ENOMEM;
 
        imxdma->dev = &pdev->dev;
-       imxdma->devtype = pdev->id_entry->driver_data;
+       imxdma->devtype = (enum imx_dma_type)of_device_get_match_data(&pdev->dev);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        imxdma->base = devm_ioremap_resource(&pdev->dev, res);
@@ -1263,7 +1239,6 @@ static struct platform_driver imxdma_driver = {
                .name   = "imx-dma",
                .of_match_table = imx_dma_of_dev_id,
        },
-       .id_table       = imx_dma_devtype,
        .remove         = imxdma_remove,
 };
 
index 16b908c77db309367c6a7fb918c3a2fc35b57120..41ba21eea7c8b2fee9125f27fa47b2fab7cc5cc7 100644 (file)
@@ -566,37 +566,6 @@ static struct sdma_driver_data sdma_imx8mq = {
        .check_ratio = 1,
 };
 
-static const struct platform_device_id sdma_devtypes[] = {
-       {
-               .name = "imx25-sdma",
-               .driver_data = (unsigned long)&sdma_imx25,
-       }, {
-               .name = "imx31-sdma",
-               .driver_data = (unsigned long)&sdma_imx31,
-       }, {
-               .name = "imx35-sdma",
-               .driver_data = (unsigned long)&sdma_imx35,
-       }, {
-               .name = "imx51-sdma",
-               .driver_data = (unsigned long)&sdma_imx51,
-       }, {
-               .name = "imx53-sdma",
-               .driver_data = (unsigned long)&sdma_imx53,
-       }, {
-               .name = "imx6q-sdma",
-               .driver_data = (unsigned long)&sdma_imx6q,
-       }, {
-               .name = "imx7d-sdma",
-               .driver_data = (unsigned long)&sdma_imx7d,
-       }, {
-               .name = "imx8mq-sdma",
-               .driver_data = (unsigned long)&sdma_imx8mq,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, sdma_devtypes);
-
 static const struct of_device_id sdma_dt_ids[] = {
        { .compatible = "fsl,imx6q-sdma", .data = &sdma_imx6q, },
        { .compatible = "fsl,imx53-sdma", .data = &sdma_imx53, },
@@ -1998,11 +1967,7 @@ static int sdma_probe(struct platform_device *pdev)
        s32 *saddr_arr;
        const struct sdma_driver_data *drvdata = NULL;
 
-       if (of_id)
-               drvdata = of_id->data;
-       else if (pdev->id_entry)
-               drvdata = (void *)pdev->id_entry->driver_data;
-
+       drvdata = of_id->data;
        if (!drvdata) {
                dev_err(&pdev->dev, "unable to find driver data\n");
                return -EINVAL;
@@ -2211,7 +2176,6 @@ static struct platform_driver sdma_driver = {
                .name   = "imx-sdma",
                .of_match_table = sdma_dt_ids,
        },
-       .id_table       = sdma_devtypes,
        .remove         = sdma_remove,
        .probe          = sdma_probe,
 };
index 38036db284cb06a3d4186c1a42b4e6f8d0ecaf25..104ad420abbeef91e444bfc51c326ac753b1fc18 100644 (file)
@@ -1160,14 +1160,13 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
        struct idmac_tx_desc *desc, *descnew;
        bool done = false;
        u32 ready0, ready1, curbuf, err;
-       unsigned long flags;
        struct dmaengine_desc_callback cb;
 
        /* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */
 
        dev_dbg(dev, "IDMAC irq %d, buf %d\n", irq, ichan->active_buffer);
 
-       spin_lock_irqsave(&ipu_data.lock, flags);
+       spin_lock(&ipu_data.lock);
 
        ready0  = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY);
        ready1  = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY);
@@ -1176,7 +1175,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
 
        if (err & (1 << chan_id)) {
                idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4);
-               spin_unlock_irqrestore(&ipu_data.lock, flags);
+               spin_unlock(&ipu_data.lock);
                /*
                 * Doing this
                 * ichan->sg[0] = ichan->sg[1] = NULL;
@@ -1188,7 +1187,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
                         chan_id, ready0, ready1, curbuf);
                return IRQ_HANDLED;
        }
-       spin_unlock_irqrestore(&ipu_data.lock, flags);
+       spin_unlock(&ipu_data.lock);
 
        /* Other interrupts do not interfere with this channel */
        spin_lock(&ichan->lock);
@@ -1251,9 +1250,9 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
                if (unlikely(sgnew)) {
                        ipu_submit_buffer(ichan, descnew, sgnew, !ichan->active_buffer);
                } else {
-                       spin_lock_irqsave(&ipu_data.lock, flags);
+                       spin_lock(&ipu_data.lock);
                        ipu_ic_disable_task(&ipu_data, chan_id);
-                       spin_unlock_irqrestore(&ipu_data.lock, flags);
+                       spin_unlock(&ipu_data.lock);
                        ichan->status = IPU_CHANNEL_READY;
                        /* Continue to check for complete descriptor */
                }
index f609a84c493ca8ad24e1d1240544ab8dc7ed4ad4..d0b2e601e3e5df6ec15373ae4e052a0f864df2fe 100644 (file)
@@ -223,24 +223,23 @@ static irqreturn_t k3_dma_int_handler(int irq, void *dev_id)
                i = __ffs(stat);
                stat &= ~BIT(i);
                if (likely(tc1 & BIT(i)) || (tc2 & BIT(i))) {
-                       unsigned long flags;
 
                        p = &d->phy[i];
                        c = p->vchan;
                        if (c && (tc1 & BIT(i))) {
-                               spin_lock_irqsave(&c->vc.lock, flags);
+                               spin_lock(&c->vc.lock);
                                if (p->ds_run != NULL) {
                                        vchan_cookie_complete(&p->ds_run->vd);
                                        p->ds_done = p->ds_run;
                                        p->ds_run = NULL;
                                }
-                               spin_unlock_irqrestore(&c->vc.lock, flags);
+                               spin_unlock(&c->vc.lock);
                        }
                        if (c && (tc2 & BIT(i))) {
-                               spin_lock_irqsave(&c->vc.lock, flags);
+                               spin_lock(&c->vc.lock);
                                if (p->ds_run != NULL)
                                        vchan_cyclic_callback(&p->ds_run->vd);
-                               spin_unlock_irqrestore(&c->vc.lock, flags);
+                               spin_unlock(&c->vc.lock);
                        }
                        irq_chan |= BIT(i);
                }
index 85a597228fb04bb89bcc0500092ed41c0059f196..584c931e807af3a824a1bd0adefec70195c7e6bc 100644 (file)
@@ -160,10 +160,9 @@ static irqreturn_t milbeaut_xdmac_interrupt(int irq, void *dev_id)
 {
        struct milbeaut_xdmac_chan *mc = dev_id;
        struct milbeaut_xdmac_desc *md;
-       unsigned long flags;
        u32 val;
 
-       spin_lock_irqsave(&mc->vc.lock, flags);
+       spin_lock(&mc->vc.lock);
 
        /* Ack and Stop */
        val = FIELD_PREP(M10V_XDDSD_IS_MASK, 0x0);
@@ -177,7 +176,7 @@ static irqreturn_t milbeaut_xdmac_interrupt(int irq, void *dev_id)
 
        milbeaut_xdmac_start(mc);
 out:
-       spin_unlock_irqrestore(&mc->vc.lock, flags);
+       spin_unlock(&mc->vc.lock);
        return IRQ_HANDLED;
 }
 
index 347146a6e1d03ac4824c02de5ef3f84c9b6b5556..74755093e14b03a299af7c1cea2e8ed1268428fa 100644 (file)
@@ -524,7 +524,6 @@ static irqreturn_t moxart_dma_interrupt(int irq, void *devid)
        struct moxart_dmadev *mc = devid;
        struct moxart_chan *ch = &mc->slave_chans[0];
        unsigned int i;
-       unsigned long flags;
        u32 ctrl;
 
        dev_dbg(chan2dev(&ch->vc.chan), "%s\n", __func__);
@@ -541,14 +540,14 @@ static irqreturn_t moxart_dma_interrupt(int irq, void *devid)
                if (ctrl & APB_DMA_FIN_INT_STS) {
                        ctrl &= ~APB_DMA_FIN_INT_STS;
                        if (ch->desc) {
-                               spin_lock_irqsave(&ch->vc.lock, flags);
+                               spin_lock(&ch->vc.lock);
                                if (++ch->sgidx < ch->desc->sglen) {
                                        moxart_dma_start_sg(ch, ch->sgidx);
                                } else {
                                        vchan_cookie_complete(&ch->desc->vd);
                                        moxart_dma_start_desc(&ch->vc.chan);
                                }
-                               spin_unlock_irqrestore(&ch->vc.lock, flags);
+                               spin_unlock(&ch->vc.lock);
                        }
                }
 
index 00cd1335eebae4237dc2fd5398e5f726d4daa376..23b232b5751844f8329a1f4c62fd3294e5de5391 100644 (file)
@@ -1455,7 +1455,7 @@ static struct platform_driver mv_xor_driver = {
        .resume         = mv_xor_resume,
        .driver         = {
                .name           = MV_XOR_NAME,
-               .of_match_table = of_match_ptr(mv_xor_dt_ids),
+               .of_match_table = mv_xor_dt_ids,
        },
 };
 
index 2753a6b916f60ed65d959d87e4e3c52c095c5802..9b0d463f89bbd478ca594476c8532241d6750d6d 100644 (file)
@@ -771,8 +771,10 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
                goto disable_clk;
 
        msi_desc = first_msi_entry(&pdev->dev);
-       if (!msi_desc)
+       if (!msi_desc) {
+               ret = -ENODEV;
                goto free_msi_irqs;
+       }
        xor_dev->msi_desc = msi_desc;
 
        ret = devm_request_irq(&pdev->dev, msi_desc->irq,
index 65f816b40c32807e07c90c0418ed91e3a83dd9d5..994fc4d2aca42e6a6a0c34995877a3f97137a948 100644 (file)
@@ -167,29 +167,11 @@ static struct mxs_dma_type mxs_dma_types[] = {
        }
 };
 
-static const struct platform_device_id mxs_dma_ids[] = {
-       {
-               .name = "imx23-dma-apbh",
-               .driver_data = (kernel_ulong_t) &mxs_dma_types[0],
-       }, {
-               .name = "imx23-dma-apbx",
-               .driver_data = (kernel_ulong_t) &mxs_dma_types[1],
-       }, {
-               .name = "imx28-dma-apbh",
-               .driver_data = (kernel_ulong_t) &mxs_dma_types[2],
-       }, {
-               .name = "imx28-dma-apbx",
-               .driver_data = (kernel_ulong_t) &mxs_dma_types[3],
-       }, {
-               /* end of list */
-       }
-};
-
 static const struct of_device_id mxs_dma_dt_ids[] = {
-       { .compatible = "fsl,imx23-dma-apbh", .data = &mxs_dma_ids[0], },
-       { .compatible = "fsl,imx23-dma-apbx", .data = &mxs_dma_ids[1], },
-       { .compatible = "fsl,imx28-dma-apbh", .data = &mxs_dma_ids[2], },
-       { .compatible = "fsl,imx28-dma-apbx", .data = &mxs_dma_ids[3], },
+       { .compatible = "fsl,imx23-dma-apbh", .data = &mxs_dma_types[0], },
+       { .compatible = "fsl,imx23-dma-apbx", .data = &mxs_dma_types[1], },
+       { .compatible = "fsl,imx28-dma-apbh", .data = &mxs_dma_types[2], },
+       { .compatible = "fsl,imx28-dma-apbx", .data = &mxs_dma_types[3], },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, mxs_dma_dt_ids);
@@ -762,8 +744,6 @@ static struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec,
 static int __init mxs_dma_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
-       const struct platform_device_id *id_entry;
-       const struct of_device_id *of_id;
        const struct mxs_dma_type *dma_type;
        struct mxs_dma_engine *mxs_dma;
        struct resource *iores;
@@ -779,13 +759,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
                return ret;
        }
 
-       of_id = of_match_device(mxs_dma_dt_ids, &pdev->dev);
-       if (of_id)
-               id_entry = of_id->data;
-       else
-               id_entry = platform_get_device_id(pdev);
-
-       dma_type = (struct mxs_dma_type *)id_entry->driver_data;
+       dma_type = (struct mxs_dma_type *)of_device_get_match_data(&pdev->dev);
        mxs_dma->type = dma_type->type;
        mxs_dma->dev_id = dma_type->id;
 
@@ -865,7 +839,6 @@ static struct platform_driver mxs_dma_driver = {
                .name   = "mxs-dma",
                .of_match_table = mxs_dma_dt_ids,
        },
-       .id_table       = mxs_dma_ids,
 };
 
 static int __init mxs_dma_module_init(void)
index 8a4f608904b984c65bf4211aaffb4fcd9fc37b4b..ec00b20ae8e4c43d7d79ffc14cea9e8c7007c1f0 100644 (file)
@@ -75,8 +75,18 @@ static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec,
                ofdma->dma_router->route_free(ofdma->dma_router->dev,
                                              route_data);
        } else {
+               int ret = 0;
+
                chan->router = ofdma->dma_router;
                chan->route_data = route_data;
+
+               if (chan->device->device_router_config)
+                       ret = chan->device->device_router_config(chan);
+
+               if (ret) {
+                       dma_release_channel(chan);
+                       chan = ERR_PTR(ret);
+               }
        }
 
        /*
index 0f5c19370f6d7b7225e47229eed334a623cdfe8e..bc0f66af0f1125c76740506f0ef32a5f74ac446b 100644 (file)
@@ -1527,8 +1527,6 @@ static int pl330_submit_req(struct pl330_thread *thrd,
 
        /* First dry run to check if req is acceptable */
        ret = _setup_req(pl330, 1, thrd, idx, &xs);
-       if (ret < 0)
-               goto xfer_exit;
 
        if (ret > pl330->mcbufsz / 2) {
                dev_info(pl330->ddma.dev, "%s:%d Try increasing mcbufsz (%i/%i)\n",
index 71cdaaa8134c4261ad45e5f2a2a5e03af42a2882..df7704053d9128dd2b335219aac4d29262accad7 100644 (file)
@@ -69,7 +69,7 @@ struct ppc_dma_chan_ref {
 };
 
 /* The list of channels exported by ppc440spe ADMA */
-struct list_head
+static struct list_head
 ppc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list);
 
 /* This flag is set when want to refetch the xor chain in the interrupt
@@ -559,7 +559,6 @@ static void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
                        int sg_index, unsigned char mult_value)
 {
        struct dma_cdb *dma_hw_desc;
-       struct xor_cb *xor_hw_desc;
        u32 *psgu;
 
        switch (chan->device->id) {
@@ -590,7 +589,6 @@ static void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
                *psgu |= cpu_to_le32(mult_value << mult_index);
                break;
        case PPC440SPE_XOR_ID:
-               xor_hw_desc = desc->hw_desc;
                break;
        default:
                BUG();
index 349fb312c8725678a2a1268a4371047dbcb2ed11..4a2a796e348c105812acafc8a1185b93280dde1d 100644 (file)
@@ -606,7 +606,6 @@ static irqreturn_t pxad_chan_handler(int irq, void *dev_id)
        struct pxad_chan *chan = phy->vchan;
        struct virt_dma_desc *vd, *tmp;
        unsigned int dcsr;
-       unsigned long flags;
        bool vd_completed;
        dma_cookie_t last_started = 0;
 
@@ -616,7 +615,7 @@ static irqreturn_t pxad_chan_handler(int irq, void *dev_id)
        if (dcsr & PXA_DCSR_RUN)
                return IRQ_NONE;
 
-       spin_lock_irqsave(&chan->vc.lock, flags);
+       spin_lock(&chan->vc.lock);
        list_for_each_entry_safe(vd, tmp, &chan->vc.desc_issued, node) {
                vd_completed = is_desc_completed(vd);
                dev_dbg(&chan->vc.chan.dev->device,
@@ -658,7 +657,7 @@ static irqreturn_t pxad_chan_handler(int irq, void *dev_id)
                        pxad_launch_chan(chan, to_pxad_sw_desc(vd));
                }
        }
-       spin_unlock_irqrestore(&chan->vc.lock, flags);
+       spin_unlock(&chan->vc.lock);
        wake_up(&chan->wq_state);
 
        return IRQ_HANDLED;
index 3bcb689162c67a2d1494ba3cee5454005b24593d..365f94eb3b0817c768d3c815c037f4adb5c14bb7 100644 (file)
@@ -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)
@@ -8,6 +19,18 @@ config QCOM_BAM_DMA
          Enable support for the QCOM BAM DMA controller.  This controller
          provides DMA capabilities for a variety of on-chip devices.
 
+config QCOM_GPI_DMA
+        tristate "Qualcomm Technologies GPI DMA support"
+        depends on ARCH_QCOM
+        select DMA_ENGINE
+        select DMA_VIRTUAL_CHANNELS
+        help
+          Enable support for the QCOM GPI DMA controller. This controller
+          provides DMA capabilities for a variety of peripheral buses such
+          as I2C, UART, and SPI. By using GPI dmaengine driver, bus drivers
+          can use a standardize interface that is protocol independent to
+          transfer data between DDR and peripheral.
+
 config QCOM_HIDMA_MGMT
        tristate "Qualcomm Technologies HIDMA Management support"
        select DMA_ENGINE
index 1ae92da88b0c97f5c926871335583f95fa5fbe9e..50f1e701469333a29b523a223883595e40718ff8 100644 (file)
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
 obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o
+obj-$(CONFIG_QCOM_GPI_DMA) += gpi.o
 obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o
 hdma_mgmt-objs  := hidma_mgmt.o hidma_mgmt_sys.o
 obj-$(CONFIG_QCOM_HIDMA) +=  hdma.o
index 4eeb8bb27279ff0b61cf578bea6691ce3a4b8c3a..d5773d474d8f5c04b382959e648413636239bbd3 100644 (file)
@@ -875,7 +875,7 @@ static irqreturn_t bam_dma_irq(int irq, void *data)
 
        ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
-               return ret;
+               return IRQ_NONE;
 
        if (srcs & BAM_IRQ) {
                clr_mask = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_STTS));
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
new file mode 100644 (file)
index 0000000..d2334f5
--- /dev/null
@@ -0,0 +1,2303 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#include <dt-bindings/dma/qcom-gpi.h>
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/dma/qcom-gpi-dma.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+#define TRE_TYPE_DMA           0x10
+#define TRE_TYPE_GO            0x20
+#define TRE_TYPE_CONFIG0       0x22
+
+/* TRE flags */
+#define TRE_FLAGS_CHAIN                BIT(0)
+#define TRE_FLAGS_IEOB         BIT(8)
+#define TRE_FLAGS_IEOT         BIT(9)
+#define TRE_FLAGS_BEI          BIT(10)
+#define TRE_FLAGS_LINK         BIT(11)
+#define TRE_FLAGS_TYPE         GENMASK(23, 16)
+
+/* SPI CONFIG0 WD0 */
+#define TRE_SPI_C0_WORD_SZ     GENMASK(4, 0)
+#define TRE_SPI_C0_LOOPBACK    BIT(8)
+#define TRE_SPI_C0_CS          BIT(11)
+#define TRE_SPI_C0_CPHA                BIT(12)
+#define TRE_SPI_C0_CPOL                BIT(13)
+#define TRE_SPI_C0_TX_PACK     BIT(24)
+#define TRE_SPI_C0_RX_PACK     BIT(25)
+
+/* CONFIG0 WD2 */
+#define TRE_C0_CLK_DIV         GENMASK(11, 0)
+#define TRE_C0_CLK_SRC         GENMASK(19, 16)
+
+/* SPI GO WD0 */
+#define TRE_SPI_GO_CMD         GENMASK(4, 0)
+#define TRE_SPI_GO_CS          GENMASK(10, 8)
+#define TRE_SPI_GO_FRAG                BIT(26)
+
+/* GO WD2 */
+#define TRE_RX_LEN             GENMASK(23, 0)
+
+/* I2C Config0 WD0 */
+#define TRE_I2C_C0_TLOW                GENMASK(7, 0)
+#define TRE_I2C_C0_THIGH       GENMASK(15, 8)
+#define TRE_I2C_C0_TCYL                GENMASK(23, 16)
+#define TRE_I2C_C0_TX_PACK     BIT(24)
+#define TRE_I2C_C0_RX_PACK      BIT(25)
+
+/* I2C GO WD0 */
+#define TRE_I2C_GO_CMD          GENMASK(4, 0)
+#define TRE_I2C_GO_ADDR                GENMASK(14, 8)
+#define TRE_I2C_GO_STRETCH     BIT(26)
+
+/* DMA TRE */
+#define TRE_DMA_LEN            GENMASK(23, 0)
+
+/* Register offsets from gpi-top */
+#define GPII_n_CH_k_CNTXT_0_OFFS(n, k) (0x20000 + (0x4000 * (n)) + (0x80 * (k)))
+#define GPII_n_CH_k_CNTXT_0_EL_SIZE    GENMASK(31, 24)
+#define GPII_n_CH_k_CNTXT_0_CHSTATE    GENMASK(23, 20)
+#define GPII_n_CH_k_CNTXT_0_ERIDX      GENMASK(18, 14)
+#define GPII_n_CH_k_CNTXT_0_DIR                BIT(3)
+#define GPII_n_CH_k_CNTXT_0_PROTO      GENMASK(2, 0)
+
+#define GPII_n_CH_k_CNTXT_0(el_size, erindex, dir, chtype_proto)  \
+       (FIELD_PREP(GPII_n_CH_k_CNTXT_0_EL_SIZE, el_size)       | \
+        FIELD_PREP(GPII_n_CH_k_CNTXT_0_ERIDX, erindex)         | \
+        FIELD_PREP(GPII_n_CH_k_CNTXT_0_DIR, dir)               | \
+        FIELD_PREP(GPII_n_CH_k_CNTXT_0_PROTO, chtype_proto))
+
+#define GPI_CHTYPE_DIR_IN      (0)
+#define GPI_CHTYPE_DIR_OUT     (1)
+
+#define GPI_CHTYPE_PROTO_GPI   (0x2)
+
+#define GPII_n_CH_k_DOORBELL_0_OFFS(n, k)      (0x22000 + (0x4000 * (n)) + (0x8 * (k)))
+#define GPII_n_CH_CMD_OFFS(n)                  (0x23008 + (0x4000 * (n)))
+#define GPII_n_CH_CMD_OPCODE                   GENMASK(31, 24)
+#define GPII_n_CH_CMD_CHID                     GENMASK(7, 0)
+#define GPII_n_CH_CMD(opcode, chid)                             \
+                    (FIELD_PREP(GPII_n_CH_CMD_OPCODE, opcode) | \
+                     FIELD_PREP(GPII_n_CH_CMD_CHID, chid))
+
+#define GPII_n_CH_CMD_ALLOCATE         (0)
+#define GPII_n_CH_CMD_START            (1)
+#define GPII_n_CH_CMD_STOP             (2)
+#define GPII_n_CH_CMD_RESET            (9)
+#define GPII_n_CH_CMD_DE_ALLOC         (10)
+#define GPII_n_CH_CMD_UART_SW_STALE    (32)
+#define GPII_n_CH_CMD_UART_RFR_READY   (33)
+#define GPII_n_CH_CMD_UART_RFR_NOT_READY (34)
+
+/* EV Context Array */
+#define GPII_n_EV_CH_k_CNTXT_0_OFFS(n, k) (0x21000 + (0x4000 * (n)) + (0x80 * (k)))
+#define GPII_n_EV_k_CNTXT_0_EL_SIZE    GENMASK(31, 24)
+#define GPII_n_EV_k_CNTXT_0_CHSTATE    GENMASK(23, 20)
+#define GPII_n_EV_k_CNTXT_0_INTYPE     BIT(16)
+#define GPII_n_EV_k_CNTXT_0_CHTYPE     GENMASK(3, 0)
+
+#define GPII_n_EV_k_CNTXT_0(el_size, inttype, chtype)          \
+       (FIELD_PREP(GPII_n_EV_k_CNTXT_0_EL_SIZE, el_size) |     \
+        FIELD_PREP(GPII_n_EV_k_CNTXT_0_INTYPE, inttype)  |     \
+        FIELD_PREP(GPII_n_EV_k_CNTXT_0_CHTYPE, chtype))
+
+#define GPI_INTTYPE_IRQ                (1)
+#define GPI_CHTYPE_GPI_EV      (0x2)
+
+enum CNTXT_OFFS {
+       CNTXT_0_CONFIG = 0x0,
+       CNTXT_1_R_LENGTH = 0x4,
+       CNTXT_2_RING_BASE_LSB = 0x8,
+       CNTXT_3_RING_BASE_MSB = 0xC,
+       CNTXT_4_RING_RP_LSB = 0x10,
+       CNTXT_5_RING_RP_MSB = 0x14,
+       CNTXT_6_RING_WP_LSB = 0x18,
+       CNTXT_7_RING_WP_MSB = 0x1C,
+       CNTXT_8_RING_INT_MOD = 0x20,
+       CNTXT_9_RING_INTVEC = 0x24,
+       CNTXT_10_RING_MSI_LSB = 0x28,
+       CNTXT_11_RING_MSI_MSB = 0x2C,
+       CNTXT_12_RING_RP_UPDATE_LSB = 0x30,
+       CNTXT_13_RING_RP_UPDATE_MSB = 0x34,
+};
+
+#define GPII_n_EV_CH_k_DOORBELL_0_OFFS(n, k)   (0x22100 + (0x4000 * (n)) + (0x8 * (k)))
+#define GPII_n_EV_CH_CMD_OFFS(n)               (0x23010 + (0x4000 * (n)))
+#define GPII_n_EV_CMD_OPCODE                   GENMASK(31, 24)
+#define GPII_n_EV_CMD_CHID                     GENMASK(7, 0)
+#define GPII_n_EV_CMD(opcode, chid)                             \
+                    (FIELD_PREP(GPII_n_EV_CMD_OPCODE, opcode) | \
+                     FIELD_PREP(GPII_n_EV_CMD_CHID, chid))
+
+#define GPII_n_EV_CH_CMD_ALLOCATE              (0x00)
+#define GPII_n_EV_CH_CMD_RESET                 (0x09)
+#define GPII_n_EV_CH_CMD_DE_ALLOC              (0x0A)
+
+#define GPII_n_CNTXT_TYPE_IRQ_OFFS(n)          (0x23080 + (0x4000 * (n)))
+
+/* mask type register */
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(n)      (0x23088 + (0x4000 * (n)))
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK         GENMASK(6, 0)
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_GENERAL      BIT(6)
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB         BIT(3)
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB         BIT(2)
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL      BIT(1)
+#define GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL      BIT(0)
+
+#define GPII_n_CNTXT_SRC_GPII_CH_IRQ_OFFS(n)   (0x23090 + (0x4000 * (n)))
+#define GPII_n_CNTXT_SRC_EV_CH_IRQ_OFFS(n)     (0x23094 + (0x4000 * (n)))
+
+/* Mask channel control interrupt register */
+#define GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(n)    (0x23098 + (0x4000 * (n)))
+#define GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK       GENMASK(1, 0)
+
+/* Mask event control interrupt register */
+#define GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(n) (0x2309C + (0x4000 * (n)))
+#define GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK    BIT(0)
+
+#define GPII_n_CNTXT_SRC_CH_IRQ_CLR_OFFS(n)    (0x230A0 + (0x4000 * (n)))
+#define GPII_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS(n) (0x230A4 + (0x4000 * (n)))
+
+/* Mask event interrupt register */
+#define GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(n)  (0x230B8 + (0x4000 * (n)))
+#define GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK     BIT(0)
+
+#define GPII_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(n)  (0x230C0 + (0x4000 * (n)))
+#define GPII_n_CNTXT_GLOB_IRQ_STTS_OFFS(n)     (0x23100 + (0x4000 * (n)))
+#define GPI_GLOB_IRQ_ERROR_INT_MSK             BIT(0)
+
+/* GPII specific Global - Enable bit register */
+#define GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(n)       (0x23108 + (0x4000 * (n)))
+#define GPII_n_CNTXT_GLOB_IRQ_CLR_OFFS(n)      (0x23110 + (0x4000 * (n)))
+#define GPII_n_CNTXT_GPII_IRQ_STTS_OFFS(n)     (0x23118 + (0x4000 * (n)))
+
+/* GPII general interrupt - Enable bit register */
+#define GPII_n_CNTXT_GPII_IRQ_EN_OFFS(n)       (0x23120 + (0x4000 * (n)))
+#define GPII_n_CNTXT_GPII_IRQ_EN_BMSK          GENMASK(3, 0)
+
+#define GPII_n_CNTXT_GPII_IRQ_CLR_OFFS(n)      (0x23128 + (0x4000 * (n)))
+
+/* GPII Interrupt Type register */
+#define GPII_n_CNTXT_INTSET_OFFS(n)            (0x23180 + (0x4000 * (n)))
+#define GPII_n_CNTXT_INTSET_BMSK               BIT(0)
+
+#define GPII_n_CNTXT_MSI_BASE_LSB_OFFS(n)      (0x23188 + (0x4000 * (n)))
+#define GPII_n_CNTXT_MSI_BASE_MSB_OFFS(n)      (0x2318C + (0x4000 * (n)))
+#define GPII_n_CNTXT_SCRATCH_0_OFFS(n)         (0x23400 + (0x4000 * (n)))
+#define GPII_n_CNTXT_SCRATCH_1_OFFS(n)         (0x23404 + (0x4000 * (n)))
+
+#define GPII_n_ERROR_LOG_OFFS(n)               (0x23200 + (0x4000 * (n)))
+
+/* QOS Registers */
+#define GPII_n_CH_k_QOS_OFFS(n, k)             (0x2005C + (0x4000 * (n)) + (0x80 * (k)))
+
+/* Scratch registers */
+#define GPII_n_CH_k_SCRATCH_0_OFFS(n, k)       (0x20060 + (0x4000 * (n)) + (0x80 * (k)))
+#define GPII_n_CH_k_SCRATCH_0_SEID             GENMASK(2, 0)
+#define GPII_n_CH_k_SCRATCH_0_PROTO            GENMASK(7, 4)
+#define GPII_n_CH_k_SCRATCH_0_PAIR             GENMASK(20, 16)
+#define GPII_n_CH_k_SCRATCH_0(pair, proto, seid)               \
+                            (FIELD_PREP(GPII_n_CH_k_SCRATCH_0_PAIR, pair)      | \
+                             FIELD_PREP(GPII_n_CH_k_SCRATCH_0_PROTO, proto)    | \
+                             FIELD_PREP(GPII_n_CH_k_SCRATCH_0_SEID, seid))
+#define GPII_n_CH_k_SCRATCH_1_OFFS(n, k)       (0x20064 + (0x4000 * (n)) + (0x80 * (k)))
+#define GPII_n_CH_k_SCRATCH_2_OFFS(n, k)       (0x20068 + (0x4000 * (n)) + (0x80 * (k)))
+#define GPII_n_CH_k_SCRATCH_3_OFFS(n, k)       (0x2006C + (0x4000 * (n)) + (0x80 * (k)))
+
+struct __packed gpi_tre {
+       u32 dword[4];
+};
+
+enum msm_gpi_tce_code {
+       MSM_GPI_TCE_SUCCESS = 1,
+       MSM_GPI_TCE_EOT = 2,
+       MSM_GPI_TCE_EOB = 4,
+       MSM_GPI_TCE_UNEXP_ERR = 16,
+};
+
+#define CMD_TIMEOUT_MS         (250)
+
+#define MAX_CHANNELS_PER_GPII  (2)
+#define GPI_TX_CHAN            (0)
+#define GPI_RX_CHAN            (1)
+#define STATE_IGNORE           (U32_MAX)
+#define EV_FACTOR              (2)
+#define REQ_OF_DMA_ARGS                (5) /* # of arguments required from client */
+#define CHAN_TRES              64
+
+struct __packed xfer_compl_event {
+       u64 ptr;
+       u32 length:24;
+       u8 code;
+       u16 status;
+       u8 type;
+       u8 chid;
+};
+
+struct __packed immediate_data_event {
+       u8 data_bytes[8];
+       u8 length:4;
+       u8 resvd:4;
+       u16 tre_index;
+       u8 code;
+       u16 status;
+       u8 type;
+       u8 chid;
+};
+
+struct __packed qup_notif_event {
+       u32 status;
+       u32 time;
+       u32 count:24;
+       u8 resvd;
+       u16 resvd1;
+       u8 type;
+       u8 chid;
+};
+
+struct __packed gpi_ere {
+       u32 dword[4];
+};
+
+enum GPI_EV_TYPE {
+       XFER_COMPLETE_EV_TYPE = 0x22,
+       IMMEDIATE_DATA_EV_TYPE = 0x30,
+       QUP_NOTIF_EV_TYPE = 0x31,
+       STALE_EV_TYPE = 0xFF,
+};
+
+union __packed gpi_event {
+       struct __packed xfer_compl_event xfer_compl_event;
+       struct __packed immediate_data_event immediate_data_event;
+       struct __packed qup_notif_event qup_notif_event;
+       struct __packed gpi_ere gpi_ere;
+};
+
+enum gpii_irq_settings {
+       DEFAULT_IRQ_SETTINGS,
+       MASK_IEOB_SETTINGS,
+};
+
+enum gpi_ev_state {
+       DEFAULT_EV_CH_STATE = 0,
+       EV_STATE_NOT_ALLOCATED = DEFAULT_EV_CH_STATE,
+       EV_STATE_ALLOCATED,
+       MAX_EV_STATES
+};
+
+static const char *const gpi_ev_state_str[MAX_EV_STATES] = {
+       [EV_STATE_NOT_ALLOCATED] = "NOT ALLOCATED",
+       [EV_STATE_ALLOCATED] = "ALLOCATED",
+};
+
+#define TO_GPI_EV_STATE_STR(_state) (((_state) >= MAX_EV_STATES) ? \
+                                   "INVALID" : gpi_ev_state_str[(_state)])
+
+enum gpi_ch_state {
+       DEFAULT_CH_STATE = 0x0,
+       CH_STATE_NOT_ALLOCATED = DEFAULT_CH_STATE,
+       CH_STATE_ALLOCATED = 0x1,
+       CH_STATE_STARTED = 0x2,
+       CH_STATE_STOPPED = 0x3,
+       CH_STATE_STOP_IN_PROC = 0x4,
+       CH_STATE_ERROR = 0xf,
+       MAX_CH_STATES
+};
+
+enum gpi_cmd {
+       GPI_CH_CMD_BEGIN,
+       GPI_CH_CMD_ALLOCATE = GPI_CH_CMD_BEGIN,
+       GPI_CH_CMD_START,
+       GPI_CH_CMD_STOP,
+       GPI_CH_CMD_RESET,
+       GPI_CH_CMD_DE_ALLOC,
+       GPI_CH_CMD_UART_SW_STALE,
+       GPI_CH_CMD_UART_RFR_READY,
+       GPI_CH_CMD_UART_RFR_NOT_READY,
+       GPI_CH_CMD_END = GPI_CH_CMD_UART_RFR_NOT_READY,
+       GPI_EV_CMD_BEGIN,
+       GPI_EV_CMD_ALLOCATE = GPI_EV_CMD_BEGIN,
+       GPI_EV_CMD_RESET,
+       GPI_EV_CMD_DEALLOC,
+       GPI_EV_CMD_END = GPI_EV_CMD_DEALLOC,
+       GPI_MAX_CMD,
+};
+
+#define IS_CHAN_CMD(_cmd) ((_cmd) <= GPI_CH_CMD_END)
+
+static const char *const gpi_cmd_str[GPI_MAX_CMD] = {
+       [GPI_CH_CMD_ALLOCATE] = "CH ALLOCATE",
+       [GPI_CH_CMD_START] = "CH START",
+       [GPI_CH_CMD_STOP] = "CH STOP",
+       [GPI_CH_CMD_RESET] = "CH_RESET",
+       [GPI_CH_CMD_DE_ALLOC] = "DE ALLOC",
+       [GPI_CH_CMD_UART_SW_STALE] = "UART SW STALE",
+       [GPI_CH_CMD_UART_RFR_READY] = "UART RFR READY",
+       [GPI_CH_CMD_UART_RFR_NOT_READY] = "UART RFR NOT READY",
+       [GPI_EV_CMD_ALLOCATE] = "EV ALLOCATE",
+       [GPI_EV_CMD_RESET] = "EV RESET",
+       [GPI_EV_CMD_DEALLOC] = "EV DEALLOC",
+};
+
+#define TO_GPI_CMD_STR(_cmd) (((_cmd) >= GPI_MAX_CMD) ? "INVALID" : \
+                                 gpi_cmd_str[(_cmd)])
+
+/*
+ * @DISABLE_STATE: no register access allowed
+ * @CONFIG_STATE:  client has configured the channel
+ * @PREP_HARDWARE: register access is allowed
+ *                however, no processing EVENTS
+ * @ACTIVE_STATE: channels are fully operational
+ * @PREPARE_TERMINATE: graceful termination of channels
+ *                    register access is allowed
+ * @PAUSE_STATE: channels are active, but not processing any events
+ */
+enum gpi_pm_state {
+       DISABLE_STATE,
+       CONFIG_STATE,
+       PREPARE_HARDWARE,
+       ACTIVE_STATE,
+       PREPARE_TERMINATE,
+       PAUSE_STATE,
+       MAX_PM_STATE
+};
+
+#define REG_ACCESS_VALID(_pm_state) ((_pm_state) >= PREPARE_HARDWARE)
+
+static const char *const gpi_pm_state_str[MAX_PM_STATE] = {
+       [DISABLE_STATE] = "DISABLE",
+       [CONFIG_STATE] = "CONFIG",
+       [PREPARE_HARDWARE] = "PREPARE HARDWARE",
+       [ACTIVE_STATE] = "ACTIVE",
+       [PREPARE_TERMINATE] = "PREPARE TERMINATE",
+       [PAUSE_STATE] = "PAUSE",
+};
+
+#define TO_GPI_PM_STR(_state) (((_state) >= MAX_PM_STATE) ? \
+                             "INVALID" : gpi_pm_state_str[(_state)])
+
+static const struct {
+       enum gpi_cmd gpi_cmd;
+       u32 opcode;
+       u32 state;
+} gpi_cmd_info[GPI_MAX_CMD] = {
+       {
+               GPI_CH_CMD_ALLOCATE,
+               GPII_n_CH_CMD_ALLOCATE,
+               CH_STATE_ALLOCATED,
+       },
+       {
+               GPI_CH_CMD_START,
+               GPII_n_CH_CMD_START,
+               CH_STATE_STARTED,
+       },
+       {
+               GPI_CH_CMD_STOP,
+               GPII_n_CH_CMD_STOP,
+               CH_STATE_STOPPED,
+       },
+       {
+               GPI_CH_CMD_RESET,
+               GPII_n_CH_CMD_RESET,
+               CH_STATE_ALLOCATED,
+       },
+       {
+               GPI_CH_CMD_DE_ALLOC,
+               GPII_n_CH_CMD_DE_ALLOC,
+               CH_STATE_NOT_ALLOCATED,
+       },
+       {
+               GPI_CH_CMD_UART_SW_STALE,
+               GPII_n_CH_CMD_UART_SW_STALE,
+               STATE_IGNORE,
+       },
+       {
+               GPI_CH_CMD_UART_RFR_READY,
+               GPII_n_CH_CMD_UART_RFR_READY,
+               STATE_IGNORE,
+       },
+       {
+               GPI_CH_CMD_UART_RFR_NOT_READY,
+               GPII_n_CH_CMD_UART_RFR_NOT_READY,
+               STATE_IGNORE,
+       },
+       {
+               GPI_EV_CMD_ALLOCATE,
+               GPII_n_EV_CH_CMD_ALLOCATE,
+               EV_STATE_ALLOCATED,
+       },
+       {
+               GPI_EV_CMD_RESET,
+               GPII_n_EV_CH_CMD_RESET,
+               EV_STATE_ALLOCATED,
+       },
+       {
+               GPI_EV_CMD_DEALLOC,
+               GPII_n_EV_CH_CMD_DE_ALLOC,
+               EV_STATE_NOT_ALLOCATED,
+       },
+};
+
+struct gpi_ring {
+       void *pre_aligned;
+       size_t alloc_size;
+       phys_addr_t phys_addr;
+       dma_addr_t dma_handle;
+       void *base;
+       void *wp;
+       void *rp;
+       u32 len;
+       u32 el_size;
+       u32 elements;
+       bool configured;
+};
+
+struct gpi_dev {
+       struct dma_device dma_device;
+       struct device *dev;
+       struct resource *res;
+       void __iomem *regs;
+       void __iomem *ee_base; /*ee register base address*/
+       u32 max_gpii; /* maximum # of gpii instances available per gpi block */
+       u32 gpii_mask; /* gpii instances available for apps */
+       u32 ev_factor; /* ev ring length factor */
+       struct gpii *gpiis;
+};
+
+struct reg_info {
+       char *name;
+       u32 offset;
+       u32 val;
+};
+
+struct gchan {
+       struct virt_dma_chan vc;
+       u32 chid;
+       u32 seid;
+       u32 protocol;
+       struct gpii *gpii;
+       enum gpi_ch_state ch_state;
+       enum gpi_pm_state pm_state;
+       void __iomem *ch_cntxt_base_reg;
+       void __iomem *ch_cntxt_db_reg;
+       void __iomem *ch_cmd_reg;
+       u32 dir;
+       struct gpi_ring ch_ring;
+       void *config;
+};
+
+struct gpii {
+       u32 gpii_id;
+       struct gchan gchan[MAX_CHANNELS_PER_GPII];
+       struct gpi_dev *gpi_dev;
+       int irq;
+       void __iomem *regs; /* points to gpi top */
+       void __iomem *ev_cntxt_base_reg;
+       void __iomem *ev_cntxt_db_reg;
+       void __iomem *ev_ring_rp_lsb_reg;
+       void __iomem *ev_cmd_reg;
+       void __iomem *ieob_clr_reg;
+       struct mutex ctrl_lock;
+       enum gpi_ev_state ev_state;
+       bool configured_irq;
+       enum gpi_pm_state pm_state;
+       rwlock_t pm_lock;
+       struct gpi_ring ev_ring;
+       struct tasklet_struct ev_task; /* event processing tasklet */
+       struct completion cmd_completion;
+       enum gpi_cmd gpi_cmd;
+       u32 cntxt_type_irq_msk;
+       bool ieob_set;
+};
+
+#define MAX_TRE 3
+
+struct gpi_desc {
+       struct virt_dma_desc vd;
+       size_t len;
+       void *db; /* DB register to program */
+       struct gchan *gchan;
+       struct gpi_tre tre[MAX_TRE];
+       u32 num_tre;
+};
+
+static const u32 GPII_CHAN_DIR[MAX_CHANNELS_PER_GPII] = {
+       GPI_CHTYPE_DIR_OUT, GPI_CHTYPE_DIR_IN
+};
+
+static irqreturn_t gpi_handle_irq(int irq, void *data);
+static void gpi_ring_recycle_ev_element(struct gpi_ring *ring);
+static int gpi_ring_add_element(struct gpi_ring *ring, void **wp);
+static void gpi_process_events(struct gpii *gpii);
+
+static inline struct gchan *to_gchan(struct dma_chan *dma_chan)
+{
+       return container_of(dma_chan, struct gchan, vc.chan);
+}
+
+static inline struct gpi_desc *to_gpi_desc(struct virt_dma_desc *vd)
+{
+       return container_of(vd, struct gpi_desc, vd);
+}
+
+static inline phys_addr_t to_physical(const struct gpi_ring *const ring,
+                                     void *addr)
+{
+       return ring->phys_addr + (addr - ring->base);
+}
+
+static inline void *to_virtual(const struct gpi_ring *const ring, phys_addr_t addr)
+{
+       return ring->base + (addr - ring->phys_addr);
+}
+
+static inline u32 gpi_read_reg(struct gpii *gpii, void __iomem *addr)
+{
+       return readl_relaxed(addr);
+}
+
+static inline void gpi_write_reg(struct gpii *gpii, void __iomem *addr, u32 val)
+{
+       writel_relaxed(val, addr);
+}
+
+/* gpi_write_reg_field - write to specific bit field */
+static inline void gpi_write_reg_field(struct gpii *gpii, void __iomem *addr,
+                                      u32 mask, u32 shift, u32 val)
+{
+       u32 tmp = gpi_read_reg(gpii, addr);
+
+       tmp &= ~mask;
+       val = tmp | ((val << shift) & mask);
+       gpi_write_reg(gpii, addr, val);
+}
+
+static inline void
+gpi_update_reg(struct gpii *gpii, u32 offset, u32 mask, u32 val)
+{
+       void __iomem *addr = gpii->regs + offset;
+       u32 tmp = gpi_read_reg(gpii, addr);
+
+       tmp &= ~mask;
+       tmp |= u32_encode_bits(val, mask);
+
+       gpi_write_reg(gpii, addr, tmp);
+}
+
+static void gpi_disable_interrupts(struct gpii *gpii)
+{
+       gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_GPII_IRQ_EN_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_GPII_IRQ_EN_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_GPII_IRQ_EN_BMSK, 0);
+       gpi_update_reg(gpii, GPII_n_CNTXT_INTSET_OFFS(gpii->gpii_id),
+                      GPII_n_CNTXT_INTSET_BMSK, 0);
+
+       gpii->cntxt_type_irq_msk = 0;
+       devm_free_irq(gpii->gpi_dev->dev, gpii->irq, gpii);
+       gpii->configured_irq = false;
+}
+
+/* configure and enable interrupts */
+static int gpi_config_interrupts(struct gpii *gpii, enum gpii_irq_settings settings, bool mask)
+{
+       const u32 enable = (GPII_n_CNTXT_TYPE_IRQ_MSK_GENERAL |
+                             GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB |
+                             GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB |
+                             GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL |
+                             GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL);
+       int ret;
+
+       if (!gpii->configured_irq) {
+               ret = devm_request_irq(gpii->gpi_dev->dev, gpii->irq,
+                                      gpi_handle_irq, IRQF_TRIGGER_HIGH,
+                                      "gpi-dma", gpii);
+               if (ret < 0) {
+                       dev_err(gpii->gpi_dev->dev, "error request irq:%d ret:%d\n",
+                               gpii->irq, ret);
+                       return ret;
+               }
+       }
+
+       if (settings == MASK_IEOB_SETTINGS) {
+               /*
+                * GPII only uses one EV ring per gpii so we can globally
+                * enable/disable IEOB interrupt
+                */
+               if (mask)
+                       gpii->cntxt_type_irq_msk |= GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB;
+               else
+                       gpii->cntxt_type_irq_msk &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB);
+               gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, gpii->cntxt_type_irq_msk);
+       } else {
+               gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, enable);
+               gpi_update_reg(gpii, GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK,
+                              GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK);
+               gpi_update_reg(gpii, GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK,
+                              GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK);
+               gpi_update_reg(gpii, GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK,
+                              GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK);
+               gpi_update_reg(gpii, GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_GPII_IRQ_EN_BMSK,
+                              GPII_n_CNTXT_GPII_IRQ_EN_BMSK);
+               gpi_update_reg(gpii, GPII_n_CNTXT_GPII_IRQ_EN_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_GPII_IRQ_EN_BMSK, GPII_n_CNTXT_GPII_IRQ_EN_BMSK);
+               gpi_update_reg(gpii, GPII_n_CNTXT_MSI_BASE_LSB_OFFS(gpii->gpii_id), U32_MAX, 0);
+               gpi_update_reg(gpii, GPII_n_CNTXT_MSI_BASE_MSB_OFFS(gpii->gpii_id), U32_MAX, 0);
+               gpi_update_reg(gpii, GPII_n_CNTXT_SCRATCH_0_OFFS(gpii->gpii_id), U32_MAX, 0);
+               gpi_update_reg(gpii, GPII_n_CNTXT_SCRATCH_1_OFFS(gpii->gpii_id), U32_MAX, 0);
+               gpi_update_reg(gpii, GPII_n_CNTXT_INTSET_OFFS(gpii->gpii_id),
+                              GPII_n_CNTXT_INTSET_BMSK, 1);
+               gpi_update_reg(gpii, GPII_n_ERROR_LOG_OFFS(gpii->gpii_id), U32_MAX, 0);
+
+               gpii->cntxt_type_irq_msk = enable;
+       }
+
+       gpii->configured_irq = true;
+       return 0;
+}
+
+/* Sends gpii event or channel command */
+static int gpi_send_cmd(struct gpii *gpii, struct gchan *gchan,
+                       enum gpi_cmd gpi_cmd)
+{
+       u32 chid = MAX_CHANNELS_PER_GPII;
+       unsigned long timeout;
+       void __iomem *cmd_reg;
+       u32 cmd;
+
+       if (gpi_cmd >= GPI_MAX_CMD)
+               return -EINVAL;
+       if (IS_CHAN_CMD(gpi_cmd))
+               chid = gchan->chid;
+
+       dev_dbg(gpii->gpi_dev->dev,
+               "sending cmd: %s:%u\n", TO_GPI_CMD_STR(gpi_cmd), chid);
+
+       /* send opcode and wait for completion */
+       reinit_completion(&gpii->cmd_completion);
+       gpii->gpi_cmd = gpi_cmd;
+
+       cmd_reg = IS_CHAN_CMD(gpi_cmd) ? gchan->ch_cmd_reg : gpii->ev_cmd_reg;
+       cmd = IS_CHAN_CMD(gpi_cmd) ? GPII_n_CH_CMD(gpi_cmd_info[gpi_cmd].opcode, chid) :
+                                    GPII_n_EV_CMD(gpi_cmd_info[gpi_cmd].opcode, 0);
+       gpi_write_reg(gpii, cmd_reg, cmd);
+       timeout = wait_for_completion_timeout(&gpii->cmd_completion,
+                                             msecs_to_jiffies(CMD_TIMEOUT_MS));
+       if (!timeout) {
+               dev_err(gpii->gpi_dev->dev, "cmd: %s completion timeout:%u\n",
+                       TO_GPI_CMD_STR(gpi_cmd), chid);
+               return -EIO;
+       }
+
+       /* confirm new ch state is correct , if the cmd is a state change cmd */
+       if (gpi_cmd_info[gpi_cmd].state == STATE_IGNORE)
+               return 0;
+
+       if (IS_CHAN_CMD(gpi_cmd) && gchan->ch_state == gpi_cmd_info[gpi_cmd].state)
+               return 0;
+
+       if (!IS_CHAN_CMD(gpi_cmd) && gpii->ev_state == gpi_cmd_info[gpi_cmd].state)
+               return 0;
+
+       return -EIO;
+}
+
+/* program transfer ring DB register */
+static inline void gpi_write_ch_db(struct gchan *gchan,
+                                  struct gpi_ring *ring, void *wp)
+{
+       struct gpii *gpii = gchan->gpii;
+       phys_addr_t p_wp;
+
+       p_wp = to_physical(ring, wp);
+       gpi_write_reg(gpii, gchan->ch_cntxt_db_reg, p_wp);
+}
+
+/* program event ring DB register */
+static inline void gpi_write_ev_db(struct gpii *gpii,
+                                  struct gpi_ring *ring, void *wp)
+{
+       phys_addr_t p_wp;
+
+       p_wp = ring->phys_addr + (wp - ring->base);
+       gpi_write_reg(gpii, gpii->ev_cntxt_db_reg, p_wp);
+}
+
+/* process transfer completion interrupt */
+static void gpi_process_ieob(struct gpii *gpii)
+{
+       gpi_write_reg(gpii, gpii->ieob_clr_reg, BIT(0));
+
+       gpi_config_interrupts(gpii, MASK_IEOB_SETTINGS, 0);
+       tasklet_hi_schedule(&gpii->ev_task);
+}
+
+/* process channel control interrupt */
+static void gpi_process_ch_ctrl_irq(struct gpii *gpii)
+{
+       u32 gpii_id = gpii->gpii_id;
+       u32 offset = GPII_n_CNTXT_SRC_GPII_CH_IRQ_OFFS(gpii_id);
+       u32 ch_irq = gpi_read_reg(gpii, gpii->regs + offset);
+       struct gchan *gchan;
+       u32 chid, state;
+
+       /* clear the status */
+       offset = GPII_n_CNTXT_SRC_CH_IRQ_CLR_OFFS(gpii_id);
+       gpi_write_reg(gpii, gpii->regs + offset, (u32)ch_irq);
+
+       for (chid = 0; chid < MAX_CHANNELS_PER_GPII; chid++) {
+               if (!(BIT(chid) & ch_irq))
+                       continue;
+
+               gchan = &gpii->gchan[chid];
+               state = gpi_read_reg(gpii, gchan->ch_cntxt_base_reg +
+                                    CNTXT_0_CONFIG);
+               state = FIELD_GET(GPII_n_CH_k_CNTXT_0_CHSTATE, state);
+
+               /*
+                * CH_CMD_DEALLOC cmd always successful. However cmd does
+                * not change hardware status. So overwriting software state
+                * to default state.
+                */
+               if (gpii->gpi_cmd == GPI_CH_CMD_DE_ALLOC)
+                       state = DEFAULT_CH_STATE;
+               gchan->ch_state = state;
+
+               /*
+                * Triggering complete all if ch_state is not a stop in process.
+                * Stop in process is a transition state and we will wait for
+                * stop interrupt before notifying.
+                */
+               if (gchan->ch_state != CH_STATE_STOP_IN_PROC)
+                       complete_all(&gpii->cmd_completion);
+       }
+}
+
+/* processing gpi general error interrupts */
+static void gpi_process_gen_err_irq(struct gpii *gpii)
+{
+       u32 gpii_id = gpii->gpii_id;
+       u32 offset = GPII_n_CNTXT_GPII_IRQ_STTS_OFFS(gpii_id);
+       u32 irq_stts = gpi_read_reg(gpii, gpii->regs + offset);
+
+       /* clear the status */
+       dev_dbg(gpii->gpi_dev->dev, "irq_stts:0x%x\n", irq_stts);
+
+       /* Clear the register */
+       offset = GPII_n_CNTXT_GPII_IRQ_CLR_OFFS(gpii_id);
+       gpi_write_reg(gpii, gpii->regs + offset, irq_stts);
+}
+
+/* processing gpi level error interrupts */
+static void gpi_process_glob_err_irq(struct gpii *gpii)
+{
+       u32 gpii_id = gpii->gpii_id;
+       u32 offset = GPII_n_CNTXT_GLOB_IRQ_STTS_OFFS(gpii_id);
+       u32 irq_stts = gpi_read_reg(gpii, gpii->regs + offset);
+
+       offset = GPII_n_CNTXT_GLOB_IRQ_CLR_OFFS(gpii_id);
+       gpi_write_reg(gpii, gpii->regs + offset, irq_stts);
+
+       /* only error interrupt should be set */
+       if (irq_stts & ~GPI_GLOB_IRQ_ERROR_INT_MSK) {
+               dev_err(gpii->gpi_dev->dev, "invalid error status:0x%x\n", irq_stts);
+               return;
+       }
+
+       offset = GPII_n_ERROR_LOG_OFFS(gpii_id);
+       gpi_write_reg(gpii, gpii->regs + offset, 0);
+}
+
+/* gpii interrupt handler */
+static irqreturn_t gpi_handle_irq(int irq, void *data)
+{
+       struct gpii *gpii = data;
+       u32 gpii_id = gpii->gpii_id;
+       u32 type, offset;
+       unsigned long flags;
+
+       read_lock_irqsave(&gpii->pm_lock, flags);
+
+       /*
+        * States are out of sync to receive interrupt
+        * while software state is in DISABLE state, bailing out.
+        */
+       if (!REG_ACCESS_VALID(gpii->pm_state)) {
+               dev_err(gpii->gpi_dev->dev, "receive interrupt while in %s state\n",
+                       TO_GPI_PM_STR(gpii->pm_state));
+               goto exit_irq;
+       }
+
+       offset = GPII_n_CNTXT_TYPE_IRQ_OFFS(gpii->gpii_id);
+       type = gpi_read_reg(gpii, gpii->regs + offset);
+
+       do {
+               /* global gpii error */
+               if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB) {
+                       gpi_process_glob_err_irq(gpii);
+                       type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB);
+               }
+
+               /* transfer complete interrupt */
+               if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB) {
+                       gpi_process_ieob(gpii);
+                       type &= ~GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB;
+               }
+
+               /* event control irq */
+               if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL) {
+                       u32 ev_state;
+                       u32 ev_ch_irq;
+
+                       dev_dbg(gpii->gpi_dev->dev,
+                               "processing EV CTRL interrupt\n");
+                       offset = GPII_n_CNTXT_SRC_EV_CH_IRQ_OFFS(gpii_id);
+                       ev_ch_irq = gpi_read_reg(gpii, gpii->regs + offset);
+
+                       offset = GPII_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS
+                               (gpii_id);
+                       gpi_write_reg(gpii, gpii->regs + offset, ev_ch_irq);
+                       ev_state = gpi_read_reg(gpii, gpii->ev_cntxt_base_reg +
+                                               CNTXT_0_CONFIG);
+                       ev_state = FIELD_GET(GPII_n_EV_k_CNTXT_0_CHSTATE, ev_state);
+
+                       /*
+                        * CMD EV_CMD_DEALLOC is always successful. However
+                        * cmd does not change hardware status. So overwriting
+                        * software state to default state.
+                        */
+                       if (gpii->gpi_cmd == GPI_EV_CMD_DEALLOC)
+                               ev_state = DEFAULT_EV_CH_STATE;
+
+                       gpii->ev_state = ev_state;
+                       dev_dbg(gpii->gpi_dev->dev, "setting EV state to %s\n",
+                               TO_GPI_EV_STATE_STR(gpii->ev_state));
+                       complete_all(&gpii->cmd_completion);
+                       type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL);
+               }
+
+               /* channel control irq */
+               if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL) {
+                       dev_dbg(gpii->gpi_dev->dev, "process CH CTRL interrupts\n");
+                       gpi_process_ch_ctrl_irq(gpii);
+                       type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL);
+               }
+
+               if (type) {
+                       dev_err(gpii->gpi_dev->dev, "Unhandled interrupt status:0x%x\n", type);
+                       gpi_process_gen_err_irq(gpii);
+                       goto exit_irq;
+               }
+
+               offset = GPII_n_CNTXT_TYPE_IRQ_OFFS(gpii->gpii_id);
+               type = gpi_read_reg(gpii, gpii->regs + offset);
+       } while (type);
+
+exit_irq:
+       read_unlock_irqrestore(&gpii->pm_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+/* process DMA Immediate completion data events */
+static void gpi_process_imed_data_event(struct gchan *gchan,
+                                       struct immediate_data_event *imed_event)
+{
+       struct gpii *gpii = gchan->gpii;
+       struct gpi_ring *ch_ring = &gchan->ch_ring;
+       void *tre = ch_ring->base + (ch_ring->el_size * imed_event->tre_index);
+       struct dmaengine_result result;
+       struct gpi_desc *gpi_desc;
+       struct virt_dma_desc *vd;
+       unsigned long flags;
+       u32 chid;
+
+       /*
+        * If channel not active don't process event
+        */
+       if (gchan->pm_state != ACTIVE_STATE) {
+               dev_err(gpii->gpi_dev->dev, "skipping processing event because ch @ %s state\n",
+                       TO_GPI_PM_STR(gchan->pm_state));
+               return;
+       }
+
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       vd = vchan_next_desc(&gchan->vc);
+       if (!vd) {
+               struct gpi_ere *gpi_ere;
+               struct gpi_tre *gpi_tre;
+
+               spin_unlock_irqrestore(&gchan->vc.lock, flags);
+               dev_dbg(gpii->gpi_dev->dev, "event without a pending descriptor!\n");
+               gpi_ere = (struct gpi_ere *)imed_event;
+               dev_dbg(gpii->gpi_dev->dev,
+                       "Event: %08x %08x %08x %08x\n",
+                       gpi_ere->dword[0], gpi_ere->dword[1],
+                       gpi_ere->dword[2], gpi_ere->dword[3]);
+               gpi_tre = tre;
+               dev_dbg(gpii->gpi_dev->dev,
+                       "Pending TRE: %08x %08x %08x %08x\n",
+                       gpi_tre->dword[0], gpi_tre->dword[1],
+                       gpi_tre->dword[2], gpi_tre->dword[3]);
+               return;
+       }
+       gpi_desc = to_gpi_desc(vd);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+
+       /*
+        * RP pointed by Event is to last TRE processed,
+        * we need to update ring rp to tre + 1
+        */
+       tre += ch_ring->el_size;
+       if (tre >= (ch_ring->base + ch_ring->len))
+               tre = ch_ring->base;
+       ch_ring->rp = tre;
+
+       /* make sure rp updates are immediately visible to all cores */
+       smp_wmb();
+
+       chid = imed_event->chid;
+       if (imed_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) {
+               if (chid == GPI_RX_CHAN)
+                       goto gpi_free_desc;
+               else
+                       return;
+       }
+
+       if (imed_event->code == MSM_GPI_TCE_UNEXP_ERR)
+               result.result = DMA_TRANS_ABORTED;
+       else
+               result.result = DMA_TRANS_NOERROR;
+       result.residue = gpi_desc->len - imed_event->length;
+
+       dma_cookie_complete(&vd->tx);
+       dmaengine_desc_get_callback_invoke(&vd->tx, &result);
+
+gpi_free_desc:
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       list_del(&vd->node);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+       kfree(gpi_desc);
+       gpi_desc = NULL;
+}
+
+/* processing transfer completion events */
+static void gpi_process_xfer_compl_event(struct gchan *gchan,
+                                        struct xfer_compl_event *compl_event)
+{
+       struct gpii *gpii = gchan->gpii;
+       struct gpi_ring *ch_ring = &gchan->ch_ring;
+       void *ev_rp = to_virtual(ch_ring, compl_event->ptr);
+       struct virt_dma_desc *vd;
+       struct gpi_desc *gpi_desc;
+       struct dmaengine_result result;
+       unsigned long flags;
+       u32 chid;
+
+       /* only process events on active channel */
+       if (unlikely(gchan->pm_state != ACTIVE_STATE)) {
+               dev_err(gpii->gpi_dev->dev, "skipping processing event because ch @ %s state\n",
+                       TO_GPI_PM_STR(gchan->pm_state));
+               return;
+       }
+
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       vd = vchan_next_desc(&gchan->vc);
+       if (!vd) {
+               struct gpi_ere *gpi_ere;
+
+               spin_unlock_irqrestore(&gchan->vc.lock, flags);
+               dev_err(gpii->gpi_dev->dev, "Event without a pending descriptor!\n");
+               gpi_ere = (struct gpi_ere *)compl_event;
+               dev_err(gpii->gpi_dev->dev,
+                       "Event: %08x %08x %08x %08x\n",
+                       gpi_ere->dword[0], gpi_ere->dword[1],
+                       gpi_ere->dword[2], gpi_ere->dword[3]);
+               return;
+       }
+
+       gpi_desc = to_gpi_desc(vd);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+
+       /*
+        * RP pointed by Event is to last TRE processed,
+        * we need to update ring rp to ev_rp + 1
+        */
+       ev_rp += ch_ring->el_size;
+       if (ev_rp >= (ch_ring->base + ch_ring->len))
+               ev_rp = ch_ring->base;
+       ch_ring->rp = ev_rp;
+
+       /* update must be visible to other cores */
+       smp_wmb();
+
+       chid = compl_event->chid;
+       if (compl_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) {
+               if (chid == GPI_RX_CHAN)
+                       goto gpi_free_desc;
+               else
+                       return;
+       }
+
+       if (compl_event->code == MSM_GPI_TCE_UNEXP_ERR) {
+               dev_err(gpii->gpi_dev->dev, "Error in Transaction\n");
+               result.result = DMA_TRANS_ABORTED;
+       } else {
+               dev_dbg(gpii->gpi_dev->dev, "Transaction Success\n");
+               result.result = DMA_TRANS_NOERROR;
+       }
+       result.residue = gpi_desc->len - compl_event->length;
+       dev_dbg(gpii->gpi_dev->dev, "Residue %d\n", result.residue);
+
+       dma_cookie_complete(&vd->tx);
+       dmaengine_desc_get_callback_invoke(&vd->tx, &result);
+
+gpi_free_desc:
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       list_del(&vd->node);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+       kfree(gpi_desc);
+       gpi_desc = NULL;
+}
+
+/* process all events */
+static void gpi_process_events(struct gpii *gpii)
+{
+       struct gpi_ring *ev_ring = &gpii->ev_ring;
+       phys_addr_t cntxt_rp;
+       void *rp;
+       union gpi_event *gpi_event;
+       struct gchan *gchan;
+       u32 chid, type;
+
+       cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg);
+       rp = to_virtual(ev_ring, cntxt_rp);
+
+       do {
+               while (rp != ev_ring->rp) {
+                       gpi_event = ev_ring->rp;
+                       chid = gpi_event->xfer_compl_event.chid;
+                       type = gpi_event->xfer_compl_event.type;
+
+                       dev_dbg(gpii->gpi_dev->dev,
+                               "Event: CHID:%u, type:%x %08x %08x %08x %08x\n",
+                               chid, type, gpi_event->gpi_ere.dword[0],
+                               gpi_event->gpi_ere.dword[1], gpi_event->gpi_ere.dword[2],
+                               gpi_event->gpi_ere.dword[3]);
+
+                       switch (type) {
+                       case XFER_COMPLETE_EV_TYPE:
+                               gchan = &gpii->gchan[chid];
+                               gpi_process_xfer_compl_event(gchan,
+                                                            &gpi_event->xfer_compl_event);
+                               break;
+                       case STALE_EV_TYPE:
+                               dev_dbg(gpii->gpi_dev->dev, "stale event, not processing\n");
+                               break;
+                       case IMMEDIATE_DATA_EV_TYPE:
+                               gchan = &gpii->gchan[chid];
+                               gpi_process_imed_data_event(gchan,
+                                                           &gpi_event->immediate_data_event);
+                               break;
+                       case QUP_NOTIF_EV_TYPE:
+                               dev_dbg(gpii->gpi_dev->dev, "QUP_NOTIF_EV_TYPE\n");
+                               break;
+                       default:
+                               dev_dbg(gpii->gpi_dev->dev,
+                                       "not supported event type:0x%x\n", type);
+                       }
+                       gpi_ring_recycle_ev_element(ev_ring);
+               }
+               gpi_write_ev_db(gpii, ev_ring, ev_ring->wp);
+
+               /* clear pending IEOB events */
+               gpi_write_reg(gpii, gpii->ieob_clr_reg, BIT(0));
+
+               cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg);
+               rp = to_virtual(ev_ring, cntxt_rp);
+
+       } while (rp != ev_ring->rp);
+}
+
+/* processing events using tasklet */
+static void gpi_ev_tasklet(unsigned long data)
+{
+       struct gpii *gpii = (struct gpii *)data;
+
+       read_lock_bh(&gpii->pm_lock);
+       if (!REG_ACCESS_VALID(gpii->pm_state)) {
+               read_unlock_bh(&gpii->pm_lock);
+               dev_err(gpii->gpi_dev->dev, "not processing any events, pm_state:%s\n",
+                       TO_GPI_PM_STR(gpii->pm_state));
+               return;
+       }
+
+       /* process the events */
+       gpi_process_events(gpii);
+
+       /* enable IEOB, switching back to interrupts */
+       gpi_config_interrupts(gpii, MASK_IEOB_SETTINGS, 1);
+       read_unlock_bh(&gpii->pm_lock);
+}
+
+/* marks all pending events for the channel as stale */
+static void gpi_mark_stale_events(struct gchan *gchan)
+{
+       struct gpii *gpii = gchan->gpii;
+       struct gpi_ring *ev_ring = &gpii->ev_ring;
+       u32 cntxt_rp, local_rp;
+       void *ev_rp;
+
+       cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg);
+
+       ev_rp = ev_ring->rp;
+       local_rp = (u32)to_physical(ev_ring, ev_rp);
+       while (local_rp != cntxt_rp) {
+               union gpi_event *gpi_event = ev_rp;
+               u32 chid = gpi_event->xfer_compl_event.chid;
+
+               if (chid == gchan->chid)
+                       gpi_event->xfer_compl_event.type = STALE_EV_TYPE;
+               ev_rp += ev_ring->el_size;
+               if (ev_rp >= (ev_ring->base + ev_ring->len))
+                       ev_rp = ev_ring->base;
+               cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg);
+               local_rp = (u32)to_physical(ev_ring, ev_rp);
+       }
+}
+
+/* reset sw state and issue channel reset or de-alloc */
+static int gpi_reset_chan(struct gchan *gchan, enum gpi_cmd gpi_cmd)
+{
+       struct gpii *gpii = gchan->gpii;
+       struct gpi_ring *ch_ring = &gchan->ch_ring;
+       unsigned long flags;
+       LIST_HEAD(list);
+       int ret;
+
+       ret = gpi_send_cmd(gpii, gchan, gpi_cmd);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n",
+                       TO_GPI_CMD_STR(gpi_cmd), ret);
+               return ret;
+       }
+
+       /* initialize the local ring ptrs */
+       ch_ring->rp = ch_ring->base;
+       ch_ring->wp = ch_ring->base;
+
+       /* visible to other cores */
+       smp_wmb();
+
+       /* check event ring for any stale events */
+       write_lock_irq(&gpii->pm_lock);
+       gpi_mark_stale_events(gchan);
+
+       /* remove all async descriptors */
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       vchan_get_all_descriptors(&gchan->vc, &list);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+       write_unlock_irq(&gpii->pm_lock);
+       vchan_dma_desc_free_list(&gchan->vc, &list);
+
+       return 0;
+}
+
+static int gpi_start_chan(struct gchan *gchan)
+{
+       struct gpii *gpii = gchan->gpii;
+       int ret;
+
+       ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_START);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n",
+                       TO_GPI_CMD_STR(GPI_CH_CMD_START), ret);
+               return ret;
+       }
+
+       /* gpii CH is active now */
+       write_lock_irq(&gpii->pm_lock);
+       gchan->pm_state = ACTIVE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+
+       return 0;
+}
+
+static int gpi_stop_chan(struct gchan *gchan)
+{
+       struct gpii *gpii = gchan->gpii;
+       int ret;
+
+       ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_STOP);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n",
+                       TO_GPI_CMD_STR(GPI_CH_CMD_STOP), ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/* allocate and configure the transfer channel */
+static int gpi_alloc_chan(struct gchan *chan, bool send_alloc_cmd)
+{
+       struct gpii *gpii = chan->gpii;
+       struct gpi_ring *ring = &chan->ch_ring;
+       int ret;
+       u32 id = gpii->gpii_id;
+       u32 chid = chan->chid;
+       u32 pair_chid = !chid;
+
+       if (send_alloc_cmd) {
+               ret = gpi_send_cmd(gpii, chan, GPI_CH_CMD_ALLOCATE);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n",
+                               TO_GPI_CMD_STR(GPI_CH_CMD_ALLOCATE), ret);
+                       return ret;
+               }
+       }
+
+       gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_0_CONFIG,
+                     GPII_n_CH_k_CNTXT_0(ring->el_size, 0, chan->dir, GPI_CHTYPE_PROTO_GPI));
+       gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_1_R_LENGTH, ring->len);
+       gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_2_RING_BASE_LSB, ring->phys_addr);
+       gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_3_RING_BASE_MSB,
+                     upper_32_bits(ring->phys_addr));
+       gpi_write_reg(gpii, chan->ch_cntxt_db_reg + CNTXT_5_RING_RP_MSB - CNTXT_4_RING_RP_LSB,
+                     upper_32_bits(ring->phys_addr));
+       gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_0_OFFS(id, chid),
+                     GPII_n_CH_k_SCRATCH_0(pair_chid, chan->protocol, chan->seid));
+       gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_1_OFFS(id, chid), 0);
+       gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_2_OFFS(id, chid), 0);
+       gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_3_OFFS(id, chid), 0);
+       gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_QOS_OFFS(id, chid), 1);
+
+       /* flush all the writes */
+       wmb();
+       return 0;
+}
+
+/* allocate and configure event ring */
+static int gpi_alloc_ev_chan(struct gpii *gpii)
+{
+       struct gpi_ring *ring = &gpii->ev_ring;
+       void __iomem *base = gpii->ev_cntxt_base_reg;
+       int ret;
+
+       ret = gpi_send_cmd(gpii, NULL, GPI_EV_CMD_ALLOCATE);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "error with cmd:%s ret:%d\n",
+                       TO_GPI_CMD_STR(GPI_EV_CMD_ALLOCATE), ret);
+               return ret;
+       }
+
+       /* program event context */
+       gpi_write_reg(gpii, base + CNTXT_0_CONFIG,
+                     GPII_n_EV_k_CNTXT_0(ring->el_size, GPI_INTTYPE_IRQ, GPI_CHTYPE_GPI_EV));
+       gpi_write_reg(gpii, base + CNTXT_1_R_LENGTH, ring->len);
+       gpi_write_reg(gpii, base + CNTXT_2_RING_BASE_LSB, lower_32_bits(ring->phys_addr));
+       gpi_write_reg(gpii, base + CNTXT_3_RING_BASE_MSB, upper_32_bits(ring->phys_addr));
+       gpi_write_reg(gpii, gpii->ev_cntxt_db_reg + CNTXT_5_RING_RP_MSB - CNTXT_4_RING_RP_LSB,
+                     upper_32_bits(ring->phys_addr));
+       gpi_write_reg(gpii, base + CNTXT_8_RING_INT_MOD, 0);
+       gpi_write_reg(gpii, base + CNTXT_10_RING_MSI_LSB, 0);
+       gpi_write_reg(gpii, base + CNTXT_11_RING_MSI_MSB, 0);
+       gpi_write_reg(gpii, base + CNTXT_8_RING_INT_MOD, 0);
+       gpi_write_reg(gpii, base + CNTXT_12_RING_RP_UPDATE_LSB, 0);
+       gpi_write_reg(gpii, base + CNTXT_13_RING_RP_UPDATE_MSB, 0);
+
+       /* add events to ring */
+       ring->wp = (ring->base + ring->len - ring->el_size);
+
+       /* flush all the writes */
+       wmb();
+
+       /* gpii is active now */
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = ACTIVE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+       gpi_write_ev_db(gpii, ring, ring->wp);
+
+       return 0;
+}
+
+/* calculate # of ERE/TRE available to queue */
+static int gpi_ring_num_elements_avail(const struct gpi_ring * const ring)
+{
+       int elements = 0;
+
+       if (ring->wp < ring->rp) {
+               elements = ((ring->rp - ring->wp) / ring->el_size) - 1;
+       } else {
+               elements = (ring->rp - ring->base) / ring->el_size;
+               elements += ((ring->base + ring->len - ring->wp) / ring->el_size) - 1;
+       }
+
+       return elements;
+}
+
+static int gpi_ring_add_element(struct gpi_ring *ring, void **wp)
+{
+       if (gpi_ring_num_elements_avail(ring) <= 0)
+               return -ENOMEM;
+
+       *wp = ring->wp;
+       ring->wp += ring->el_size;
+       if (ring->wp  >= (ring->base + ring->len))
+               ring->wp = ring->base;
+
+       /* visible to other cores */
+       smp_wmb();
+
+       return 0;
+}
+
+static void gpi_ring_recycle_ev_element(struct gpi_ring *ring)
+{
+       /* Update the WP */
+       ring->wp += ring->el_size;
+       if (ring->wp  >= (ring->base + ring->len))
+               ring->wp = ring->base;
+
+       /* Update the RP */
+       ring->rp += ring->el_size;
+       if (ring->rp  >= (ring->base + ring->len))
+               ring->rp = ring->base;
+
+       /* visible to other cores */
+       smp_wmb();
+}
+
+static void gpi_free_ring(struct gpi_ring *ring,
+                         struct gpii *gpii)
+{
+       dma_free_coherent(gpii->gpi_dev->dev, ring->alloc_size,
+                         ring->pre_aligned, ring->dma_handle);
+       memset(ring, 0, sizeof(*ring));
+}
+
+/* allocate memory for transfer and event rings */
+static int gpi_alloc_ring(struct gpi_ring *ring, u32 elements,
+                         u32 el_size, struct gpii *gpii)
+{
+       u64 len = elements * el_size;
+       int bit;
+
+       /* ring len must be power of 2 */
+       bit = find_last_bit((unsigned long *)&len, 32);
+       if (((1 << bit) - 1) & len)
+               bit++;
+       len = 1 << bit;
+       ring->alloc_size = (len + (len - 1));
+       dev_dbg(gpii->gpi_dev->dev,
+               "#el:%u el_size:%u len:%u actual_len:%llu alloc_size:%lu\n",
+                 elements, el_size, (elements * el_size), len,
+                 ring->alloc_size);
+
+       ring->pre_aligned = dma_alloc_coherent(gpii->gpi_dev->dev,
+                                              ring->alloc_size,
+                                              &ring->dma_handle, GFP_KERNEL);
+       if (!ring->pre_aligned) {
+               dev_err(gpii->gpi_dev->dev, "could not alloc size:%lu mem for ring\n",
+                       ring->alloc_size);
+               return -ENOMEM;
+       }
+
+       /* align the physical mem */
+       ring->phys_addr = (ring->dma_handle + (len - 1)) & ~(len - 1);
+       ring->base = ring->pre_aligned + (ring->phys_addr - ring->dma_handle);
+       ring->rp = ring->base;
+       ring->wp = ring->base;
+       ring->len = len;
+       ring->el_size = el_size;
+       ring->elements = ring->len / ring->el_size;
+       memset(ring->base, 0, ring->len);
+       ring->configured = true;
+
+       /* update to other cores */
+       smp_wmb();
+
+       dev_dbg(gpii->gpi_dev->dev,
+               "phy_pre:0x%0llx phy_alig:0x%0llx len:%u el_size:%u elements:%u\n",
+               ring->dma_handle, ring->phys_addr, ring->len,
+               ring->el_size, ring->elements);
+
+       return 0;
+}
+
+/* copy tre into transfer ring */
+static void gpi_queue_xfer(struct gpii *gpii, struct gchan *gchan,
+                          struct gpi_tre *gpi_tre, void **wp)
+{
+       struct gpi_tre *ch_tre;
+       int ret;
+
+       /* get next tre location we can copy */
+       ret = gpi_ring_add_element(&gchan->ch_ring, (void **)&ch_tre);
+       if (unlikely(ret)) {
+               dev_err(gpii->gpi_dev->dev, "Error adding ring element to xfer ring\n");
+               return;
+       }
+
+       /* copy the tre info */
+       memcpy(ch_tre, gpi_tre, sizeof(*ch_tre));
+       *wp = ch_tre;
+}
+
+/* reset and restart transfer channel */
+static int gpi_terminate_all(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       int schid, echid, i;
+       int ret = 0;
+
+       mutex_lock(&gpii->ctrl_lock);
+
+       /*
+        * treat both channels as a group if its protocol is not UART
+        * STOP, RESET, or START needs to be in lockstep
+        */
+       schid = (gchan->protocol == QCOM_GPI_UART) ? gchan->chid : 0;
+       echid = (gchan->protocol == QCOM_GPI_UART) ? schid + 1 : MAX_CHANNELS_PER_GPII;
+
+       /* stop the channel */
+       for (i = schid; i < echid; i++) {
+               gchan = &gpii->gchan[i];
+
+               /* disable ch state so no more TRE processing */
+               write_lock_irq(&gpii->pm_lock);
+               gchan->pm_state = PREPARE_TERMINATE;
+               write_unlock_irq(&gpii->pm_lock);
+
+               /* send command to Stop the channel */
+               ret = gpi_stop_chan(gchan);
+       }
+
+       /* reset the channels (clears any pending tre) */
+       for (i = schid; i < echid; i++) {
+               gchan = &gpii->gchan[i];
+
+               ret = gpi_reset_chan(gchan, GPI_CH_CMD_RESET);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error resetting channel ret:%d\n", ret);
+                       goto terminate_exit;
+               }
+
+               /* reprogram channel CNTXT */
+               ret = gpi_alloc_chan(gchan, false);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error alloc_channel ret:%d\n", ret);
+                       goto terminate_exit;
+               }
+       }
+
+       /* restart the channels */
+       for (i = schid; i < echid; i++) {
+               gchan = &gpii->gchan[i];
+
+               ret = gpi_start_chan(gchan);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error Starting Channel ret:%d\n", ret);
+                       goto terminate_exit;
+               }
+       }
+
+terminate_exit:
+       mutex_unlock(&gpii->ctrl_lock);
+       return ret;
+}
+
+/* pause dma transfer for all channels */
+static int gpi_pause(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       int i, ret;
+
+       mutex_lock(&gpii->ctrl_lock);
+
+       /*
+        * pause/resume are per gpii not per channel, so
+        * client needs to call pause only once
+        */
+       if (gpii->pm_state == PAUSE_STATE) {
+               dev_dbg(gpii->gpi_dev->dev, "channel is already paused\n");
+               mutex_unlock(&gpii->ctrl_lock);
+               return 0;
+       }
+
+       /* send stop command to stop the channels */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
+               ret = gpi_stop_chan(&gpii->gchan[i]);
+               if (ret) {
+                       mutex_unlock(&gpii->ctrl_lock);
+                       return ret;
+               }
+       }
+
+       disable_irq(gpii->irq);
+
+       /* Wait for threads to complete out */
+       tasklet_kill(&gpii->ev_task);
+
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = PAUSE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+       mutex_unlock(&gpii->ctrl_lock);
+
+       return 0;
+}
+
+/* resume dma transfer */
+static int gpi_resume(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       int i, ret;
+
+       mutex_lock(&gpii->ctrl_lock);
+       if (gpii->pm_state == ACTIVE_STATE) {
+               dev_dbg(gpii->gpi_dev->dev, "channel is already active\n");
+               mutex_unlock(&gpii->ctrl_lock);
+               return 0;
+       }
+
+       enable_irq(gpii->irq);
+
+       /* send start command to start the channels */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
+               ret = gpi_send_cmd(gpii, &gpii->gchan[i], GPI_CH_CMD_START);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error starting chan, ret:%d\n", ret);
+                       mutex_unlock(&gpii->ctrl_lock);
+                       return ret;
+               }
+       }
+
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = ACTIVE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+       mutex_unlock(&gpii->ctrl_lock);
+
+       return 0;
+}
+
+static void gpi_desc_free(struct virt_dma_desc *vd)
+{
+       struct gpi_desc *gpi_desc = to_gpi_desc(vd);
+
+       kfree(gpi_desc);
+       gpi_desc = NULL;
+}
+
+static int
+gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
+{
+       struct gchan *gchan = to_gchan(chan);
+
+       if (!config->peripheral_config)
+               return -EINVAL;
+
+       gchan->config = krealloc(gchan->config, config->peripheral_size, GFP_NOWAIT);
+       if (!gchan->config)
+               return -ENOMEM;
+
+       memcpy(gchan->config, config->peripheral_config, config->peripheral_size);
+
+       return 0;
+}
+
+static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
+                             struct scatterlist *sgl, enum dma_transfer_direction direction)
+{
+       struct gpi_i2c_config *i2c = chan->config;
+       struct device *dev = chan->gpii->gpi_dev->dev;
+       unsigned int tre_idx = 0;
+       dma_addr_t address;
+       struct gpi_tre *tre;
+       unsigned int i;
+
+       /* first create config tre if applicable */
+       if (i2c->set_config) {
+               tre = &desc->tre[tre_idx];
+               tre_idx++;
+
+               tre->dword[0] = u32_encode_bits(i2c->low_count, TRE_I2C_C0_TLOW);
+               tre->dword[0] |= u32_encode_bits(i2c->high_count, TRE_I2C_C0_THIGH);
+               tre->dword[0] |= u32_encode_bits(i2c->cycle_count, TRE_I2C_C0_TCYL);
+               tre->dword[0] |= u32_encode_bits(i2c->pack_enable, TRE_I2C_C0_TX_PACK);
+               tre->dword[0] |= u32_encode_bits(i2c->pack_enable, TRE_I2C_C0_RX_PACK);
+
+               tre->dword[1] = 0;
+
+               tre->dword[2] = u32_encode_bits(i2c->clk_div, TRE_C0_CLK_DIV);
+
+               tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE);
+               tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+       }
+
+       /* create the GO tre for Tx */
+       if (i2c->op == I2C_WRITE) {
+               tre = &desc->tre[tre_idx];
+               tre_idx++;
+
+               if (i2c->multi_msg)
+                       tre->dword[0] = u32_encode_bits(I2C_READ, TRE_I2C_GO_CMD);
+               else
+                       tre->dword[0] = u32_encode_bits(i2c->op, TRE_I2C_GO_CMD);
+
+               tre->dword[0] |= u32_encode_bits(i2c->addr, TRE_I2C_GO_ADDR);
+               tre->dword[0] |= u32_encode_bits(i2c->stretch, TRE_I2C_GO_STRETCH);
+
+               tre->dword[1] = 0;
+               tre->dword[2] = u32_encode_bits(i2c->rx_len, TRE_RX_LEN);
+
+               tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE);
+
+               if (i2c->multi_msg)
+                       tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK);
+               else
+                       tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+       }
+
+       if (i2c->op == I2C_READ || i2c->multi_msg == false) {
+               /* create the DMA TRE */
+               tre = &desc->tre[tre_idx];
+               tre_idx++;
+
+               address = sg_dma_address(sgl);
+               tre->dword[0] = lower_32_bits(address);
+               tre->dword[1] = upper_32_bits(address);
+
+               tre->dword[2] = u32_encode_bits(sg_dma_len(sgl), TRE_DMA_LEN);
+
+               tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE);
+               tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT);
+       };
+
+       for (i = 0; i < tre_idx; i++)
+               dev_dbg(dev, "TRE:%d %x:%x:%x:%x\n", i, desc->tre[i].dword[0],
+                       desc->tre[i].dword[1], desc->tre[i].dword[2], desc->tre[i].dword[3]);
+
+       return tre_idx;
+}
+
+static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
+                             struct scatterlist *sgl, enum dma_transfer_direction direction)
+{
+       struct gpi_spi_config *spi = chan->config;
+       struct device *dev = chan->gpii->gpi_dev->dev;
+       unsigned int tre_idx = 0;
+       dma_addr_t address;
+       struct gpi_tre *tre;
+       unsigned int i;
+
+       /* first create config tre if applicable */
+       if (direction == DMA_MEM_TO_DEV && spi->set_config) {
+               tre = &desc->tre[tre_idx];
+               tre_idx++;
+
+               tre->dword[0] = u32_encode_bits(spi->word_len, TRE_SPI_C0_WORD_SZ);
+               tre->dword[0] |= u32_encode_bits(spi->loopback_en, TRE_SPI_C0_LOOPBACK);
+               tre->dword[0] |= u32_encode_bits(spi->clock_pol_high, TRE_SPI_C0_CPOL);
+               tre->dword[0] |= u32_encode_bits(spi->data_pol_high, TRE_SPI_C0_CPHA);
+               tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_TX_PACK);
+               tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_RX_PACK);
+
+               tre->dword[1] = 0;
+
+               tre->dword[2] = u32_encode_bits(spi->clk_div, TRE_C0_CLK_DIV);
+               tre->dword[2] |= u32_encode_bits(spi->clk_src, TRE_C0_CLK_SRC);
+
+               tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE);
+               tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+       }
+
+       /* create the GO tre for Tx */
+       if (direction == DMA_MEM_TO_DEV) {
+               tre = &desc->tre[tre_idx];
+               tre_idx++;
+
+               tre->dword[0] = u32_encode_bits(spi->fragmentation, TRE_SPI_GO_FRAG);
+               tre->dword[0] |= u32_encode_bits(spi->cs, TRE_SPI_GO_CS);
+               tre->dword[0] |= u32_encode_bits(spi->cmd, TRE_SPI_GO_CMD);
+
+               tre->dword[1] = 0;
+
+               tre->dword[2] = u32_encode_bits(spi->rx_len, TRE_RX_LEN);
+
+               tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE);
+               if (spi->cmd == SPI_RX)
+                       tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
+               else
+                       tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
+       }
+
+       /* create the dma tre */
+       tre = &desc->tre[tre_idx];
+       tre_idx++;
+
+       address = sg_dma_address(sgl);
+       tre->dword[0] = lower_32_bits(address);
+       tre->dword[1] = upper_32_bits(address);
+
+       tre->dword[2] = u32_encode_bits(sg_dma_len(sgl), TRE_DMA_LEN);
+
+       tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE);
+       if (direction == DMA_MEM_TO_DEV)
+               tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT);
+
+       for (i = 0; i < tre_idx; i++)
+               dev_dbg(dev, "TRE:%d %x:%x:%x:%x\n", i, desc->tre[i].dword[0],
+                       desc->tre[i].dword[1], desc->tre[i].dword[2], desc->tre[i].dword[3]);
+
+       return tre_idx;
+}
+
+/* copy tre into transfer ring */
+static struct dma_async_tx_descriptor *
+gpi_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 gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       struct device *dev = gpii->gpi_dev->dev;
+       struct gpi_ring *ch_ring = &gchan->ch_ring;
+       struct gpi_desc *gpi_desc;
+       u32 nr, nr_tre = 0;
+       u8 set_config;
+       int i;
+
+       gpii->ieob_set = false;
+       if (!is_slave_direction(direction)) {
+               dev_err(gpii->gpi_dev->dev, "invalid dma direction: %d\n", direction);
+               return NULL;
+       }
+
+       if (sg_len > 1) {
+               dev_err(dev, "Multi sg sent, we support only one atm: %d\n", sg_len);
+               return NULL;
+       }
+
+       nr_tre = 3;
+       set_config = *(u32 *)gchan->config;
+       if (!set_config)
+               nr_tre = 2;
+       if (direction == DMA_DEV_TO_MEM) /* rx */
+               nr_tre = 1;
+
+       /* calculate # of elements required & available */
+       nr = gpi_ring_num_elements_avail(ch_ring);
+       if (nr < nr_tre) {
+               dev_err(dev, "not enough space in ring, avail:%u required:%u\n", nr, nr_tre);
+               return NULL;
+       }
+
+       gpi_desc = kzalloc(sizeof(*gpi_desc), GFP_NOWAIT);
+       if (!gpi_desc)
+               return NULL;
+
+       /* create TREs for xfer */
+       if (gchan->protocol == QCOM_GPI_SPI) {
+               i = gpi_create_spi_tre(gchan, gpi_desc, sgl, direction);
+       } else if (gchan->protocol == QCOM_GPI_I2C) {
+               i = gpi_create_i2c_tre(gchan, gpi_desc, sgl, direction);
+       } else {
+               dev_err(dev, "invalid peripheral: %d\n", gchan->protocol);
+               kfree(gpi_desc);
+               return NULL;
+       }
+
+       /* set up the descriptor */
+       gpi_desc->gchan = gchan;
+       gpi_desc->len = sg_dma_len(sgl);
+       gpi_desc->num_tre  = i;
+
+       return vchan_tx_prep(&gchan->vc, &gpi_desc->vd, flags);
+}
+
+/* rings transfer ring db to being transfer */
+static void gpi_issue_pending(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       unsigned long flags, pm_lock_flags;
+       struct virt_dma_desc *vd = NULL;
+       struct gpi_desc *gpi_desc;
+       struct gpi_ring *ch_ring = &gchan->ch_ring;
+       void *tre, *wp = NULL;
+       int i;
+
+       read_lock_irqsave(&gpii->pm_lock, pm_lock_flags);
+
+       /* move all submitted discriptors to issued list */
+       spin_lock_irqsave(&gchan->vc.lock, flags);
+       if (vchan_issue_pending(&gchan->vc))
+               vd = list_last_entry(&gchan->vc.desc_issued,
+                                    struct virt_dma_desc, node);
+       spin_unlock_irqrestore(&gchan->vc.lock, flags);
+
+       /* nothing to do list is empty */
+       if (!vd) {
+               read_unlock_irqrestore(&gpii->pm_lock, pm_lock_flags);
+               return;
+       }
+
+       gpi_desc = to_gpi_desc(vd);
+       for (i = 0; i < gpi_desc->num_tre; i++) {
+               tre = &gpi_desc->tre[i];
+               gpi_queue_xfer(gpii, gchan, tre, &wp);
+       }
+
+       gpi_desc->db = ch_ring->wp;
+       gpi_write_ch_db(gchan, &gchan->ch_ring, gpi_desc->db);
+       read_unlock_irqrestore(&gpii->pm_lock, pm_lock_flags);
+}
+
+static int gpi_ch_init(struct gchan *gchan)
+{
+       struct gpii *gpii = gchan->gpii;
+       const int ev_factor = gpii->gpi_dev->ev_factor;
+       u32 elements;
+       int i = 0, ret = 0;
+
+       gchan->pm_state = CONFIG_STATE;
+
+       /* check if both channels are configured before continue */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++)
+               if (gpii->gchan[i].pm_state != CONFIG_STATE)
+                       goto exit_gpi_init;
+
+       /* protocol must be same for both channels */
+       if (gpii->gchan[0].protocol != gpii->gchan[1].protocol) {
+               dev_err(gpii->gpi_dev->dev, "protocol did not match protocol %u != %u\n",
+                       gpii->gchan[0].protocol, gpii->gchan[1].protocol);
+               ret = -EINVAL;
+               goto exit_gpi_init;
+       }
+
+       /* allocate memory for event ring */
+       elements = CHAN_TRES << ev_factor;
+       ret = gpi_alloc_ring(&gpii->ev_ring, elements,
+                            sizeof(union gpi_event), gpii);
+       if (ret)
+               goto exit_gpi_init;
+
+       /* configure interrupts */
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = PREPARE_HARDWARE;
+       write_unlock_irq(&gpii->pm_lock);
+       ret = gpi_config_interrupts(gpii, DEFAULT_IRQ_SETTINGS, 0);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "error config. interrupts, ret:%d\n", ret);
+               goto error_config_int;
+       }
+
+       /* allocate event rings */
+       ret = gpi_alloc_ev_chan(gpii);
+       if (ret) {
+               dev_err(gpii->gpi_dev->dev, "error alloc_ev_chan:%d\n", ret);
+               goto error_alloc_ev_ring;
+       }
+
+       /* Allocate all channels */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
+               ret = gpi_alloc_chan(&gpii->gchan[i], true);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error allocating chan:%d\n", ret);
+                       goto error_alloc_chan;
+               }
+       }
+
+       /* start channels  */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
+               ret = gpi_start_chan(&gpii->gchan[i]);
+               if (ret) {
+                       dev_err(gpii->gpi_dev->dev, "Error start chan:%d\n", ret);
+                       goto error_start_chan;
+               }
+       }
+       return ret;
+
+error_start_chan:
+       for (i = i - 1; i >= 0; i++) {
+               gpi_stop_chan(&gpii->gchan[i]);
+               gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET);
+       }
+       i = 2;
+error_alloc_chan:
+       for (i = i - 1; i >= 0; i--)
+               gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC);
+error_alloc_ev_ring:
+       gpi_disable_interrupts(gpii);
+error_config_int:
+       gpi_free_ring(&gpii->ev_ring, gpii);
+exit_gpi_init:
+       mutex_unlock(&gpii->ctrl_lock);
+       return ret;
+}
+
+/* release all channel resources */
+static void gpi_free_chan_resources(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       enum gpi_pm_state cur_state;
+       int ret, i;
+
+       mutex_lock(&gpii->ctrl_lock);
+
+       cur_state = gchan->pm_state;
+
+       /* disable ch state so no more TRE processing for this channel */
+       write_lock_irq(&gpii->pm_lock);
+       gchan->pm_state = PREPARE_TERMINATE;
+       write_unlock_irq(&gpii->pm_lock);
+
+       /* attempt to do graceful hardware shutdown */
+       if (cur_state == ACTIVE_STATE) {
+               gpi_stop_chan(gchan);
+
+               ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET);
+               if (ret)
+                       dev_err(gpii->gpi_dev->dev, "error resetting channel:%d\n", ret);
+
+               gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC);
+       }
+
+       /* free all allocated memory */
+       gpi_free_ring(&gchan->ch_ring, gpii);
+       vchan_free_chan_resources(&gchan->vc);
+       kfree(gchan->config);
+
+       write_lock_irq(&gpii->pm_lock);
+       gchan->pm_state = DISABLE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+
+       /* if other rings are still active exit */
+       for (i = 0; i < MAX_CHANNELS_PER_GPII; i++)
+               if (gpii->gchan[i].ch_ring.configured)
+                       goto exit_free;
+
+       /* deallocate EV Ring */
+       cur_state = gpii->pm_state;
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = PREPARE_TERMINATE;
+       write_unlock_irq(&gpii->pm_lock);
+
+       /* wait for threads to complete out */
+       tasklet_kill(&gpii->ev_task);
+
+       /* send command to de allocate event ring */
+       if (cur_state == ACTIVE_STATE)
+               gpi_send_cmd(gpii, NULL, GPI_EV_CMD_DEALLOC);
+
+       gpi_free_ring(&gpii->ev_ring, gpii);
+
+       /* disable interrupts */
+       if (cur_state == ACTIVE_STATE)
+               gpi_disable_interrupts(gpii);
+
+       /* set final state to disable */
+       write_lock_irq(&gpii->pm_lock);
+       gpii->pm_state = DISABLE_STATE;
+       write_unlock_irq(&gpii->pm_lock);
+
+exit_free:
+       mutex_unlock(&gpii->ctrl_lock);
+}
+
+/* allocate channel resources */
+static int gpi_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct gchan *gchan = to_gchan(chan);
+       struct gpii *gpii = gchan->gpii;
+       int ret;
+
+       mutex_lock(&gpii->ctrl_lock);
+
+       /* allocate memory for transfer ring */
+       ret = gpi_alloc_ring(&gchan->ch_ring, CHAN_TRES,
+                            sizeof(struct gpi_tre), gpii);
+       if (ret)
+               goto xfer_alloc_err;
+
+       ret = gpi_ch_init(gchan);
+
+       mutex_unlock(&gpii->ctrl_lock);
+
+       return ret;
+xfer_alloc_err:
+       mutex_unlock(&gpii->ctrl_lock);
+
+       return ret;
+}
+
+static int gpi_find_avail_gpii(struct gpi_dev *gpi_dev, u32 seid)
+{
+       struct gchan *tx_chan, *rx_chan;
+       unsigned int gpii;
+
+       /* check if same seid is already configured for another chid */
+       for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) {
+               if (!((1 << gpii) & gpi_dev->gpii_mask))
+                       continue;
+
+               tx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_TX_CHAN];
+               rx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_RX_CHAN];
+
+               if (rx_chan->vc.chan.client_count && rx_chan->seid == seid)
+                       return gpii;
+               if (tx_chan->vc.chan.client_count && tx_chan->seid == seid)
+                       return gpii;
+       }
+
+       /* no channels configured with same seid, return next avail gpii */
+       for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) {
+               if (!((1 << gpii) & gpi_dev->gpii_mask))
+                       continue;
+
+               tx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_TX_CHAN];
+               rx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_RX_CHAN];
+
+               /* check if gpii is configured */
+               if (tx_chan->vc.chan.client_count ||
+                   rx_chan->vc.chan.client_count)
+                       continue;
+
+               /* found a free gpii */
+               return gpii;
+       }
+
+       /* no gpii instance available to use */
+       return -EIO;
+}
+
+/* gpi_of_dma_xlate: open client requested channel */
+static struct dma_chan *gpi_of_dma_xlate(struct of_phandle_args *args,
+                                        struct of_dma *of_dma)
+{
+       struct gpi_dev *gpi_dev = (struct gpi_dev *)of_dma->of_dma_data;
+       u32 seid, chid;
+       int gpii;
+       struct gchan *gchan;
+
+       if (args->args_count < 3) {
+               dev_err(gpi_dev->dev, "gpii require minimum 2 args, client passed:%d args\n",
+                       args->args_count);
+               return NULL;
+       }
+
+       chid = args->args[0];
+       if (chid >= MAX_CHANNELS_PER_GPII) {
+               dev_err(gpi_dev->dev, "gpii channel:%d not valid\n", chid);
+               return NULL;
+       }
+
+       seid = args->args[1];
+
+       /* find next available gpii to use */
+       gpii = gpi_find_avail_gpii(gpi_dev, seid);
+       if (gpii < 0) {
+               dev_err(gpi_dev->dev, "no available gpii instances\n");
+               return NULL;
+       }
+
+       gchan = &gpi_dev->gpiis[gpii].gchan[chid];
+       if (gchan->vc.chan.client_count) {
+               dev_err(gpi_dev->dev, "gpii:%d chid:%d seid:%d already configured\n",
+                       gpii, chid, gchan->seid);
+               return NULL;
+       }
+
+       gchan->seid = seid;
+       gchan->protocol = args->args[2];
+
+       return dma_get_slave_channel(&gchan->vc.chan);
+}
+
+static int gpi_probe(struct platform_device *pdev)
+{
+       struct gpi_dev *gpi_dev;
+       unsigned int i;
+       int ret;
+
+       gpi_dev = devm_kzalloc(&pdev->dev, sizeof(*gpi_dev), GFP_KERNEL);
+       if (!gpi_dev)
+               return -ENOMEM;
+
+       gpi_dev->dev = &pdev->dev;
+       gpi_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       gpi_dev->regs = devm_ioremap_resource(gpi_dev->dev, gpi_dev->res);
+       if (IS_ERR(gpi_dev->regs))
+               return PTR_ERR(gpi_dev->regs);
+       gpi_dev->ee_base = gpi_dev->regs;
+
+       ret = of_property_read_u32(gpi_dev->dev->of_node, "dma-channels",
+                                  &gpi_dev->max_gpii);
+       if (ret) {
+               dev_err(gpi_dev->dev, "missing 'max-no-gpii' DT node\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(gpi_dev->dev->of_node, "dma-channel-mask",
+                                  &gpi_dev->gpii_mask);
+       if (ret) {
+               dev_err(gpi_dev->dev, "missing 'gpii-mask' DT node\n");
+               return ret;
+       }
+
+       gpi_dev->ev_factor = EV_FACTOR;
+
+       ret = dma_set_mask(gpi_dev->dev, DMA_BIT_MASK(64));
+       if (ret) {
+               dev_err(gpi_dev->dev, "Error setting dma_mask to 64, ret:%d\n", ret);
+               return ret;
+       }
+
+       gpi_dev->gpiis = devm_kzalloc(gpi_dev->dev, sizeof(*gpi_dev->gpiis) *
+                                     gpi_dev->max_gpii, GFP_KERNEL);
+       if (!gpi_dev->gpiis)
+               return -ENOMEM;
+
+       /* setup all the supported gpii */
+       INIT_LIST_HEAD(&gpi_dev->dma_device.channels);
+       for (i = 0; i < gpi_dev->max_gpii; i++) {
+               struct gpii *gpii = &gpi_dev->gpiis[i];
+               int chan;
+
+               if (!((1 << i) & gpi_dev->gpii_mask))
+                       continue;
+
+               /* set up ev cntxt register map */
+               gpii->ev_cntxt_base_reg = gpi_dev->ee_base + GPII_n_EV_CH_k_CNTXT_0_OFFS(i, 0);
+               gpii->ev_cntxt_db_reg = gpi_dev->ee_base + GPII_n_EV_CH_k_DOORBELL_0_OFFS(i, 0);
+               gpii->ev_ring_rp_lsb_reg = gpii->ev_cntxt_base_reg + CNTXT_4_RING_RP_LSB;
+               gpii->ev_cmd_reg = gpi_dev->ee_base + GPII_n_EV_CH_CMD_OFFS(i);
+               gpii->ieob_clr_reg = gpi_dev->ee_base + GPII_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(i);
+
+               /* set up irq */
+               ret = platform_get_irq(pdev, i);
+               if (ret < 0) {
+                       dev_err(gpi_dev->dev, "platform_get_irq failed for %d:%d\n", i, ret);
+                       return ret;
+               }
+               gpii->irq = ret;
+
+               /* set up channel specific register info */
+               for (chan = 0; chan < MAX_CHANNELS_PER_GPII; chan++) {
+                       struct gchan *gchan = &gpii->gchan[chan];
+
+                       /* set up ch cntxt register map */
+                       gchan->ch_cntxt_base_reg = gpi_dev->ee_base +
+                               GPII_n_CH_k_CNTXT_0_OFFS(i, chan);
+                       gchan->ch_cntxt_db_reg = gpi_dev->ee_base +
+                               GPII_n_CH_k_DOORBELL_0_OFFS(i, chan);
+                       gchan->ch_cmd_reg = gpi_dev->ee_base + GPII_n_CH_CMD_OFFS(i);
+
+                       /* vchan setup */
+                       vchan_init(&gchan->vc, &gpi_dev->dma_device);
+                       gchan->vc.desc_free = gpi_desc_free;
+                       gchan->chid = chan;
+                       gchan->gpii = gpii;
+                       gchan->dir = GPII_CHAN_DIR[chan];
+               }
+               mutex_init(&gpii->ctrl_lock);
+               rwlock_init(&gpii->pm_lock);
+               tasklet_init(&gpii->ev_task, gpi_ev_tasklet,
+                            (unsigned long)gpii);
+               init_completion(&gpii->cmd_completion);
+               gpii->gpii_id = i;
+               gpii->regs = gpi_dev->ee_base;
+               gpii->gpi_dev = gpi_dev;
+       }
+
+       platform_set_drvdata(pdev, gpi_dev);
+
+       /* clear and Set capabilities */
+       dma_cap_zero(gpi_dev->dma_device.cap_mask);
+       dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask);
+
+       /* configure dmaengine apis */
+       gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+       gpi_dev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+       gpi_dev->dma_device.src_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES;
+       gpi_dev->dma_device.dst_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES;
+       gpi_dev->dma_device.device_alloc_chan_resources = gpi_alloc_chan_resources;
+       gpi_dev->dma_device.device_free_chan_resources = gpi_free_chan_resources;
+       gpi_dev->dma_device.device_tx_status = dma_cookie_status;
+       gpi_dev->dma_device.device_issue_pending = gpi_issue_pending;
+       gpi_dev->dma_device.device_prep_slave_sg = gpi_prep_slave_sg;
+       gpi_dev->dma_device.device_config = gpi_peripheral_config;
+       gpi_dev->dma_device.device_terminate_all = gpi_terminate_all;
+       gpi_dev->dma_device.dev = gpi_dev->dev;
+       gpi_dev->dma_device.device_pause = gpi_pause;
+       gpi_dev->dma_device.device_resume = gpi_resume;
+
+       /* register with dmaengine framework */
+       ret = dma_async_device_register(&gpi_dev->dma_device);
+       if (ret) {
+               dev_err(gpi_dev->dev, "async_device_register failed ret:%d", ret);
+               return ret;
+       }
+
+       ret = of_dma_controller_register(gpi_dev->dev->of_node,
+                                        gpi_of_dma_xlate, gpi_dev);
+       if (ret) {
+               dev_err(gpi_dev->dev, "of_dma_controller_reg failed ret:%d", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id gpi_of_match[] = {
+       { .compatible = "qcom,sdm845-gpi-dma" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, gpi_of_match);
+
+static struct platform_driver gpi_driver = {
+       .probe = gpi_probe,
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = gpi_of_match,
+       },
+};
+
+static int __init gpi_init(void)
+{
+       return platform_driver_register(&gpi_driver);
+}
+subsys_initcall(gpi_init)
+
+MODULE_DESCRIPTION("QCOM GPI DMA engine driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c
new file mode 100644 (file)
index 0000000..ee78bed
--- /dev/null
@@ -0,0 +1,905 @@
+// 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
+ *
+ * @chan: dma 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
+ *
+ * @burst: Burst size of transaction
+ */
+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
+ * @chan: 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");
index 528deb5d9f3147065800a7558752d67255bcb530..c4c4e857576435455864049e5950e3c8164981f8 100644 (file)
@@ -326,10 +326,9 @@ static irqreturn_t sf_pdma_done_isr(int irq, void *dev_id)
 {
        struct sf_pdma_chan *chan = dev_id;
        struct pdma_regs *regs = &chan->regs;
-       unsigned long flags;
        u64 residue;
 
-       spin_lock_irqsave(&chan->vchan.lock, flags);
+       spin_lock(&chan->vchan.lock);
        writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl);
        residue = readq(regs->residue);
 
@@ -346,7 +345,7 @@ static irqreturn_t sf_pdma_done_isr(int irq, void *dev_id)
                sf_pdma_xfer_desc(chan);
        }
 
-       spin_unlock_irqrestore(&chan->vchan.lock, flags);
+       spin_unlock(&chan->vchan.lock);
 
        return IRQ_HANDLED;
 }
@@ -355,11 +354,10 @@ static irqreturn_t sf_pdma_err_isr(int irq, void *dev_id)
 {
        struct sf_pdma_chan *chan = dev_id;
        struct pdma_regs *regs = &chan->regs;
-       unsigned long flags;
 
-       spin_lock_irqsave(&chan->lock, flags);
+       spin_lock(&chan->lock);
        writel((readl(regs->ctrl)) & ~PDMA_ERR_STATUS_MASK, regs->ctrl);
-       spin_unlock_irqrestore(&chan->lock, flags);
+       spin_unlock(&chan->lock);
 
        tasklet_schedule(&chan->err_tasklet);
 
@@ -584,7 +582,7 @@ static struct platform_driver sf_pdma_driver = {
        .remove         = sf_pdma_remove,
        .driver         = {
                .name   = "sf-pdma",
-               .of_match_table = of_match_ptr(sf_pdma_dt_ids),
+               .of_match_table = sf_pdma_dt_ids,
        },
 };
 
index 77ab1f4730be917707fb4646f9602c6f91ca0e57..4256e55bbf25ff15ad7027796aa98738b4f62138 100644 (file)
@@ -1643,13 +1643,12 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
        u32 row;
        long chan = -1;
        struct d40_chan *d40c;
-       unsigned long flags;
        struct d40_base *base = data;
        u32 *regs = base->regs_interrupt;
        struct d40_interrupt_lookup *il = base->gen_dmac.il;
        u32 il_size = base->gen_dmac.il_size;
 
-       spin_lock_irqsave(&base->interrupt_lock, flags);
+       spin_lock(&base->interrupt_lock);
 
        /* Read interrupt status of both logical and physical channels */
        for (i = 0; i < il_size; i++)
@@ -1694,7 +1693,7 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
                spin_unlock(&d40c->lock);
        }
 
-       spin_unlock_irqrestore(&base->interrupt_lock, flags);
+       spin_unlock(&base->interrupt_lock);
 
        return IRQ_HANDLED;
 }
index d0055d2f0b9a4201cf363f01f12ec5f7e1f497fa..f54ecb123a521df469505fb058b7ba8542efffa7 100644 (file)
@@ -264,9 +264,11 @@ static int stm32_dma_get_width(struct stm32_dma_chan *chan,
 }
 
 static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len,
+                                                      dma_addr_t buf_addr,
                                                       u32 threshold)
 {
        enum dma_slave_buswidth max_width;
+       u64 addr = buf_addr;
 
        if (threshold == STM32_DMA_FIFO_THRESHOLD_FULL)
                max_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -277,6 +279,9 @@ static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len,
               max_width > DMA_SLAVE_BUSWIDTH_1_BYTE)
                max_width = max_width >> 1;
 
+       if (do_div(addr, max_width))
+               max_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
        return max_width;
 }
 
@@ -648,21 +653,12 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
        scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
        sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id));
 
-       if (status & STM32_DMA_TCI) {
-               stm32_dma_irq_clear(chan, STM32_DMA_TCI);
-               if (scr & STM32_DMA_SCR_TCIE)
-                       stm32_dma_handle_chan_done(chan);
-               status &= ~STM32_DMA_TCI;
-       }
-       if (status & STM32_DMA_HTI) {
-               stm32_dma_irq_clear(chan, STM32_DMA_HTI);
-               status &= ~STM32_DMA_HTI;
-       }
        if (status & STM32_DMA_FEI) {
                stm32_dma_irq_clear(chan, STM32_DMA_FEI);
                status &= ~STM32_DMA_FEI;
                if (sfcr & STM32_DMA_SFCR_FEIE) {
-                       if (!(scr & STM32_DMA_SCR_EN))
+                       if (!(scr & STM32_DMA_SCR_EN) &&
+                           !(status & STM32_DMA_TCI))
                                dev_err(chan2dev(chan), "FIFO Error\n");
                        else
                                dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
@@ -674,6 +670,19 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
                if (sfcr & STM32_DMA_SCR_DMEIE)
                        dev_dbg(chan2dev(chan), "Direct mode overrun\n");
        }
+
+       if (status & STM32_DMA_TCI) {
+               stm32_dma_irq_clear(chan, STM32_DMA_TCI);
+               if (scr & STM32_DMA_SCR_TCIE)
+                       stm32_dma_handle_chan_done(chan);
+               status &= ~STM32_DMA_TCI;
+       }
+
+       if (status & STM32_DMA_HTI) {
+               stm32_dma_irq_clear(chan, STM32_DMA_HTI);
+               status &= ~STM32_DMA_HTI;
+       }
+
        if (status) {
                stm32_dma_irq_clear(chan, status);
                dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
@@ -703,7 +712,7 @@ static void stm32_dma_issue_pending(struct dma_chan *c)
 static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
                                    enum dma_transfer_direction direction,
                                    enum dma_slave_buswidth *buswidth,
-                                   u32 buf_len)
+                                   u32 buf_len, dma_addr_t buf_addr)
 {
        enum dma_slave_buswidth src_addr_width, dst_addr_width;
        int src_bus_width, dst_bus_width;
@@ -735,7 +744,8 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
                        return dst_burst_size;
 
                /* Set memory data size */
-               src_addr_width = stm32_dma_get_max_width(buf_len, fifoth);
+               src_addr_width = stm32_dma_get_max_width(buf_len, buf_addr,
+                                                        fifoth);
                chan->mem_width = src_addr_width;
                src_bus_width = stm32_dma_get_width(chan, src_addr_width);
                if (src_bus_width < 0)
@@ -784,7 +794,8 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
                        return src_burst_size;
 
                /* Set memory data size */
-               dst_addr_width = stm32_dma_get_max_width(buf_len, fifoth);
+               dst_addr_width = stm32_dma_get_max_width(buf_len, buf_addr,
+                                                        fifoth);
                chan->mem_width = dst_addr_width;
                dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
                if (dst_bus_width < 0)
@@ -872,7 +883,8 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
 
        for_each_sg(sgl, sg, sg_len, i) {
                ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
-                                              sg_dma_len(sg));
+                                              sg_dma_len(sg),
+                                              sg_dma_address(sg));
                if (ret < 0)
                        goto err;
 
@@ -940,7 +952,8 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
                return NULL;
        }
 
-       ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len);
+       ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len,
+                                      buf_addr);
        if (ret < 0)
                return NULL;
 
@@ -1216,6 +1229,8 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c)
        pm_runtime_put(dmadev->ddev.dev);
 
        vchan_free_chan_resources(to_virt_chan(c));
+       stm32_dma_clear_reg(&chan->chan_reg);
+       chan->threshold = 0;
 }
 
 static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
index a10ccd964376f53362fb3c1764fceabc13bc404d..ef0d0555103d9f6a7f0cb1a74d53e33869ab142a 100644 (file)
@@ -168,7 +168,7 @@ error_chan_id:
        return ERR_PTR(ret);
 }
 
-static const struct of_device_id stm32_stm32dma_master_match[] = {
+static const struct of_device_id stm32_stm32dma_master_match[] __maybe_unused = {
        { .compatible = "st,stm32-dma", },
        {},
 };
index 08cfbfab837bb29b697a2eb46ec234e9e6aae811..e4637ec786d396fb58907567c616e5603fe05a1a 100644 (file)
@@ -339,7 +339,7 @@ static struct stm32_mdma_desc *stm32_mdma_alloc_desc(
        struct stm32_mdma_desc *desc;
        int i;
 
-       desc = kzalloc(offsetof(typeof(*desc), node[count]), GFP_NOWAIT);
+       desc = kzalloc(struct_size(desc, node, count), GFP_NOWAIT);
        if (!desc)
                return NULL;
 
@@ -1346,7 +1346,7 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid)
 {
        struct stm32_mdma_device *dmadev = devid;
        struct stm32_mdma_chan *chan = devid;
-       u32 reg, id, ien, status, flag;
+       u32 reg, id, ccr, ien, status;
 
        /* Find out which channel generates the interrupt */
        status = readl_relaxed(dmadev->base + STM32_MDMA_GISR0);
@@ -1368,67 +1368,71 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid)
 
        chan = &dmadev->chan[id];
        if (!chan) {
-               dev_dbg(mdma2dev(dmadev), "MDMA channel not initialized\n");
-               goto exit;
+               dev_warn(mdma2dev(dmadev), "MDMA channel not initialized\n");
+               return IRQ_NONE;
        }
 
        /* Handle interrupt for the channel */
        spin_lock(&chan->vchan.lock);
-       status = stm32_mdma_read(dmadev, STM32_MDMA_CISR(chan->id));
-       ien = stm32_mdma_read(dmadev, STM32_MDMA_CCR(chan->id));
-       ien &= STM32_MDMA_CCR_IRQ_MASK;
-       ien >>= 1;
+       status = stm32_mdma_read(dmadev, STM32_MDMA_CISR(id));
+       /* Mask Channel ReQuest Active bit which can be set in case of MEM2MEM */
+       status &= ~STM32_MDMA_CISR_CRQA;
+       ccr = stm32_mdma_read(dmadev, STM32_MDMA_CCR(id));
+       ien = (ccr & STM32_MDMA_CCR_IRQ_MASK) >> 1;
 
        if (!(status & ien)) {
                spin_unlock(&chan->vchan.lock);
-               dev_dbg(chan2dev(chan),
-                       "spurious it (status=0x%04x, ien=0x%04x)\n",
-                       status, ien);
+               dev_warn(chan2dev(chan),
+                        "spurious it (status=0x%04x, ien=0x%04x)\n",
+                        status, ien);
                return IRQ_NONE;
        }
 
-       flag = __ffs(status & ien);
-       reg = STM32_MDMA_CIFCR(chan->id);
+       reg = STM32_MDMA_CIFCR(id);
 
-       switch (1 << flag) {
-       case STM32_MDMA_CISR_TEIF:
-               id = chan->id;
-               status = readl_relaxed(dmadev->base + STM32_MDMA_CESR(id));
-               dev_err(chan2dev(chan), "Transfer Err: stat=0x%08x\n", status);
+       if (status & STM32_MDMA_CISR_TEIF) {
+               dev_err(chan2dev(chan), "Transfer Err: stat=0x%08x\n",
+                       readl_relaxed(dmadev->base + STM32_MDMA_CESR(id)));
                stm32_mdma_set_bits(dmadev, reg, STM32_MDMA_CIFCR_CTEIF);
-               break;
+               status &= ~STM32_MDMA_CISR_TEIF;
+       }
 
-       case STM32_MDMA_CISR_CTCIF:
+       if (status & STM32_MDMA_CISR_CTCIF) {
                stm32_mdma_set_bits(dmadev, reg, STM32_MDMA_CIFCR_CCTCIF);
+               status &= ~STM32_MDMA_CISR_CTCIF;
                stm32_mdma_xfer_end(chan);
-               break;
+       }
 
-       case STM32_MDMA_CISR_BRTIF:
+       if (status & STM32_MDMA_CISR_BRTIF) {
                stm32_mdma_set_bits(dmadev, reg, STM32_MDMA_CIFCR_CBRTIF);
-               break;
+               status &= ~STM32_MDMA_CISR_BRTIF;
+       }
 
-       case STM32_MDMA_CISR_BTIF:
+       if (status & STM32_MDMA_CISR_BTIF) {
                stm32_mdma_set_bits(dmadev, reg, STM32_MDMA_CIFCR_CBTIF);
+               status &= ~STM32_MDMA_CISR_BTIF;
                chan->curr_hwdesc++;
                if (chan->desc && chan->desc->cyclic) {
                        if (chan->curr_hwdesc == chan->desc->count)
                                chan->curr_hwdesc = 0;
                        vchan_cyclic_callback(&chan->desc->vdesc);
                }
-               break;
+       }
 
-       case STM32_MDMA_CISR_TCIF:
+       if (status & STM32_MDMA_CISR_TCIF) {
                stm32_mdma_set_bits(dmadev, reg, STM32_MDMA_CIFCR_CLTCIF);
-               break;
+               status &= ~STM32_MDMA_CISR_TCIF;
+       }
 
-       default:
-               dev_err(chan2dev(chan), "it %d unhandled (status=0x%04x)\n",
-                       1 << flag, status);
+       if (status) {
+               stm32_mdma_set_bits(dmadev, reg, status);
+               dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
+               if (!(ccr & STM32_MDMA_CCR_EN))
+                       dev_err(chan2dev(chan), "chan disabled by HW\n");
        }
 
        spin_unlock(&chan->vchan.lock);
 
-exit:
        return IRQ_HANDLED;
 }
 
index f5f9c86c50bc2a9dae17dd993f7edd0c52f5a68c..5cadd4d2b82460fe323ec3eb4a9f3ff021254cb5 100644 (file)
@@ -1173,6 +1173,30 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
                             BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
 };
 
+/*
+ * TODO: Add support for more than 4g physical addressing.
+ *
+ * The A100 binding uses the number of dma channels from the
+ * device tree node.
+ */
+static struct sun6i_dma_config sun50i_a100_dma_cfg = {
+       .clock_autogate_enable = sun6i_enable_clock_autogate_h3,
+       .set_burst_length = sun6i_set_burst_length_h3,
+       .set_drq          = sun6i_set_drq_h6,
+       .set_mode         = sun6i_set_mode_h6,
+       .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+       .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+       .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+                            BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+                            BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
+                            BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+       .dst_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+                            BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+                            BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
+                            BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+       .has_mbus_clk = true,
+};
+
 /*
  * The H6 binding uses the number of dma channels from the
  * device tree node.
@@ -1225,6 +1249,7 @@ static const struct of_device_id sun6i_dma_match[] = {
        { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg },
        { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg },
        { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
+       { .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
        { .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
        { /* sentinel */ }
 };
index c5fa2ef74abc7f0071c868d1ea776422641e7a58..4735742e826d209b56dc3cfee9b33e1459f40f31 100644 (file)
@@ -408,19 +408,18 @@ static irqreturn_t tegra_adma_isr(int irq, void *dev_id)
 {
        struct tegra_adma_chan *tdc = dev_id;
        unsigned long status;
-       unsigned long flags;
 
-       spin_lock_irqsave(&tdc->vc.lock, flags);
+       spin_lock(&tdc->vc.lock);
 
        status = tegra_adma_irq_clear(tdc);
        if (status == 0 || !tdc->desc) {
-               spin_unlock_irqrestore(&tdc->vc.lock, flags);
+               spin_unlock(&tdc->vc.lock);
                return IRQ_NONE;
        }
 
        vchan_cyclic_callback(&tdc->desc->vd);
 
-       spin_unlock_irqrestore(&tdc->vc.lock, flags);
+       spin_unlock(&tdc->vc.lock);
 
        return IRQ_HANDLED;
 }
index 0c67254caee6bef83f545fddf0d81fc6b5a06f9f..bd496efadff70fbf9506c78d6e894446340aff2e 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
 obj-$(CONFIG_TI_K3_PSIL) += k3-psil.o \
                            k3-psil-am654.o \
                            k3-psil-j721e.o \
-                           k3-psil-j7200.o
+                           k3-psil-j7200.o \
+                           k3-psil-am64.o
 obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o
index 4ba8fa5d9c367f73252a5e196182bd52966a5aeb..71d24fc07c0038cdf23e350d913902c8e9d576b6 100644 (file)
@@ -122,7 +122,7 @@ static void *ti_am335x_xbar_route_allocate(struct of_phandle_args *dma_spec,
        return map;
 }
 
-static const struct of_device_id ti_am335x_master_match[] = {
+static const struct of_device_id ti_am335x_master_match[] __maybe_unused = {
        { .compatible = "ti,edma3-tpcc", },
        {},
 };
@@ -292,7 +292,7 @@ static const u32 ti_dma_offset[] = {
        [TI_XBAR_SDMA_OFFSET] = 1,
 };
 
-static const struct of_device_id ti_dra7_master_match[] = {
+static const struct of_device_id ti_dra7_master_match[] __maybe_unused = {
        {
                .compatible = "ti,omap4430-sdma",
                .data = &ti_dma_offset[TI_XBAR_SDMA_OFFSET],
@@ -460,7 +460,7 @@ static int ti_dma_xbar_probe(struct platform_device *pdev)
 static struct platform_driver ti_dma_xbar_driver = {
        .driver = {
                .name = "ti-dma-crossbar",
-               .of_match_table = of_match_ptr(ti_dma_xbar_match),
+               .of_match_table = ti_dma_xbar_match,
        },
        .probe  = ti_dma_xbar_probe,
 };
diff --git a/drivers/dma/ti/k3-psil-am64.c b/drivers/dma/ti/k3-psil-am64.c
new file mode 100644 (file)
index 0000000..9fdeaa1
--- /dev/null
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/kernel.h>
+
+#include "k3-psil-priv.h"
+
+#define PSIL_PDMA_XY_TR(x)                                     \
+       {                                                       \
+               .thread_id = x,                                 \
+               .ep_config = {                                  \
+                       .ep_type = PSIL_EP_PDMA_XY,             \
+                       .mapped_channel_id = -1,                \
+                       .default_flow_id = -1,                  \
+               },                                              \
+       }
+
+#define PSIL_PDMA_XY_PKT(x)                                    \
+       {                                                       \
+               .thread_id = x,                                 \
+               .ep_config = {                                  \
+                       .ep_type = PSIL_EP_PDMA_XY,             \
+                       .mapped_channel_id = -1,                \
+                       .default_flow_id = -1,                  \
+                       .pkt_mode = 1,                          \
+               },                                              \
+       }
+
+#define PSIL_ETHERNET(x, ch, flow_base, flow_cnt)              \
+       {                                                       \
+               .thread_id = x,                                 \
+               .ep_config = {                                  \
+                       .ep_type = PSIL_EP_NATIVE,              \
+                       .pkt_mode = 1,                          \
+                       .needs_epib = 1,                        \
+                       .psd_size = 16,                         \
+                       .mapped_channel_id = ch,                \
+                       .flow_start = flow_base,                \
+                       .flow_num = flow_cnt,                   \
+                       .default_flow_id = flow_base,           \
+               },                                              \
+       }
+
+#define PSIL_SAUL(x, ch, flow_base, flow_cnt, default_flow, tx)        \
+       {                                                       \
+               .thread_id = x,                                 \
+               .ep_config = {                                  \
+                       .ep_type = PSIL_EP_NATIVE,              \
+                       .pkt_mode = 1,                          \
+                       .needs_epib = 1,                        \
+                       .psd_size = 64,                         \
+                       .mapped_channel_id = ch,                \
+                       .flow_start = flow_base,                \
+                       .flow_num = flow_cnt,                   \
+                       .default_flow_id = default_flow,        \
+                       .notdpkt = tx,                          \
+               },                                              \
+       }
+
+/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */
+static struct psil_ep am64_src_ep_map[] = {
+       /* SAUL */
+       PSIL_SAUL(0x4000, 17, 32, 8, 32, 0),
+       PSIL_SAUL(0x4001, 18, 32, 8, 33, 0),
+       PSIL_SAUL(0x4002, 19, 40, 8, 40, 0),
+       PSIL_SAUL(0x4003, 20, 40, 8, 41, 0),
+       /* ICSS_G0 */
+       PSIL_ETHERNET(0x4100, 21, 48, 16),
+       PSIL_ETHERNET(0x4101, 22, 64, 16),
+       PSIL_ETHERNET(0x4102, 23, 80, 16),
+       PSIL_ETHERNET(0x4103, 24, 96, 16),
+       /* ICSS_G1 */
+       PSIL_ETHERNET(0x4200, 25, 112, 16),
+       PSIL_ETHERNET(0x4201, 26, 128, 16),
+       PSIL_ETHERNET(0x4202, 27, 144, 16),
+       PSIL_ETHERNET(0x4203, 28, 160, 16),
+       /* PDMA_MAIN0 - SPI0-3 */
+       PSIL_PDMA_XY_PKT(0x4300),
+       PSIL_PDMA_XY_PKT(0x4301),
+       PSIL_PDMA_XY_PKT(0x4302),
+       PSIL_PDMA_XY_PKT(0x4303),
+       PSIL_PDMA_XY_PKT(0x4304),
+       PSIL_PDMA_XY_PKT(0x4305),
+       PSIL_PDMA_XY_PKT(0x4306),
+       PSIL_PDMA_XY_PKT(0x4307),
+       PSIL_PDMA_XY_PKT(0x4308),
+       PSIL_PDMA_XY_PKT(0x4309),
+       PSIL_PDMA_XY_PKT(0x430a),
+       PSIL_PDMA_XY_PKT(0x430b),
+       PSIL_PDMA_XY_PKT(0x430c),
+       PSIL_PDMA_XY_PKT(0x430d),
+       PSIL_PDMA_XY_PKT(0x430e),
+       PSIL_PDMA_XY_PKT(0x430f),
+       /* PDMA_MAIN0 - USART0-1 */
+       PSIL_PDMA_XY_PKT(0x4310),
+       PSIL_PDMA_XY_PKT(0x4311),
+       /* PDMA_MAIN1 - SPI4 */
+       PSIL_PDMA_XY_PKT(0x4400),
+       PSIL_PDMA_XY_PKT(0x4401),
+       PSIL_PDMA_XY_PKT(0x4402),
+       PSIL_PDMA_XY_PKT(0x4403),
+       /* PDMA_MAIN1 - USART2-6 */
+       PSIL_PDMA_XY_PKT(0x4404),
+       PSIL_PDMA_XY_PKT(0x4405),
+       PSIL_PDMA_XY_PKT(0x4406),
+       PSIL_PDMA_XY_PKT(0x4407),
+       PSIL_PDMA_XY_PKT(0x4408),
+       /* PDMA_MAIN1 - ADCs */
+       PSIL_PDMA_XY_TR(0x440f),
+       PSIL_PDMA_XY_TR(0x4410),
+       /* CPSW2 */
+       PSIL_ETHERNET(0x4500, 16, 16, 16),
+};
+
+/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */
+static struct psil_ep am64_dst_ep_map[] = {
+       /* SAUL */
+       PSIL_SAUL(0xc000, 24, 80, 8, 80, 1),
+       PSIL_SAUL(0xc001, 25, 88, 8, 88, 1),
+       /* ICSS_G0 */
+       PSIL_ETHERNET(0xc100, 26, 96, 1),
+       PSIL_ETHERNET(0xc101, 27, 97, 1),
+       PSIL_ETHERNET(0xc102, 28, 98, 1),
+       PSIL_ETHERNET(0xc103, 29, 99, 1),
+       PSIL_ETHERNET(0xc104, 30, 100, 1),
+       PSIL_ETHERNET(0xc105, 31, 101, 1),
+       PSIL_ETHERNET(0xc106, 32, 102, 1),
+       PSIL_ETHERNET(0xc107, 33, 103, 1),
+       /* ICSS_G1 */
+       PSIL_ETHERNET(0xc200, 34, 104, 1),
+       PSIL_ETHERNET(0xc201, 35, 105, 1),
+       PSIL_ETHERNET(0xc202, 36, 106, 1),
+       PSIL_ETHERNET(0xc203, 37, 107, 1),
+       PSIL_ETHERNET(0xc204, 38, 108, 1),
+       PSIL_ETHERNET(0xc205, 39, 109, 1),
+       PSIL_ETHERNET(0xc206, 40, 110, 1),
+       PSIL_ETHERNET(0xc207, 41, 111, 1),
+       /* CPSW2 */
+       PSIL_ETHERNET(0xc500, 16, 16, 8),
+       PSIL_ETHERNET(0xc501, 17, 24, 8),
+       PSIL_ETHERNET(0xc502, 18, 32, 8),
+       PSIL_ETHERNET(0xc503, 19, 40, 8),
+       PSIL_ETHERNET(0xc504, 20, 48, 8),
+       PSIL_ETHERNET(0xc505, 21, 56, 8),
+       PSIL_ETHERNET(0xc506, 22, 64, 8),
+       PSIL_ETHERNET(0xc507, 23, 72, 8),
+};
+
+struct psil_ep_map am64_ep_map = {
+       .name = "am64",
+       .src = am64_src_ep_map,
+       .src_count = ARRAY_SIZE(am64_src_ep_map),
+       .dst = am64_dst_ep_map,
+       .dst_count = ARRAY_SIZE(am64_dst_ep_map),
+};
index b4b0fb359eff06a36efa9cb647db608946411846..b74e192e3c2d18f461e08ad84d612a405953f31c 100644 (file)
@@ -40,5 +40,6 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id);
 extern struct psil_ep_map am654_ep_map;
 extern struct psil_ep_map j721e_ep_map;
 extern struct psil_ep_map j7200_ep_map;
+extern struct psil_ep_map am64_ep_map;
 
 #endif /* K3_PSIL_PRIV_H_ */
index 837853aab95ac02ffbf42a6a8c1f53c36abbae2d..13ce7367d870c0bed71d3563f8049a6cd3d4697f 100644 (file)
@@ -20,6 +20,7 @@ static const struct soc_device_attribute k3_soc_devices[] = {
        { .family = "AM65X", .data = &am654_ep_map },
        { .family = "J721E", .data = &j721e_ep_map },
        { .family = "J7200", .data = &j7200_ep_map },
+       { .family = "AM64X", .data = &am64_ep_map },
        { /* sentinel */ }
 };
 
index a367584f0d7b3add40a8b4fae712f94bdf5e6f34..4fdd9f06b72358298f6168043e3a7720628cc89e 100644 (file)
@@ -22,6 +22,7 @@
 
 struct k3_udma_glue_common {
        struct device *dev;
+       struct device chan_dev;
        struct udma_dev *udmax;
        const struct udma_tisci_rm *tisci_rm;
        struct k3_ringacc *ringacc;
@@ -32,7 +33,8 @@ struct k3_udma_glue_common {
        bool epib;
        u32  psdata_size;
        u32  swdata_size;
-       u32  atype;
+       u32  atype_asel;
+       struct psil_endpoint_config *ep_config;
 };
 
 struct k3_udma_glue_tx_channel {
@@ -53,6 +55,8 @@ struct k3_udma_glue_tx_channel {
        bool tx_filt_einfo;
        bool tx_filt_pswords;
        bool tx_supr_tdpkt;
+
+       int udma_tflow_id;
 };
 
 struct k3_udma_glue_rx_flow {
@@ -81,20 +85,26 @@ struct k3_udma_glue_rx_channel {
        u32 flows_ready;
 };
 
+static void k3_udma_chan_dev_release(struct device *dev)
+{
+       /* The struct containing the device is devm managed */
+}
+
+static struct class k3_udma_glue_devclass = {
+       .name           = "k3_udma_glue_chan",
+       .dev_release    = k3_udma_chan_dev_release,
+};
+
 #define K3_UDMAX_TDOWN_TIMEOUT_US 1000
 
 static int of_k3_udma_glue_parse(struct device_node *udmax_np,
                                 struct k3_udma_glue_common *common)
 {
-       common->ringacc = of_k3_ringacc_get_by_phandle(udmax_np,
-                                                      "ti,ringacc");
-       if (IS_ERR(common->ringacc))
-               return PTR_ERR(common->ringacc);
-
        common->udmax = of_xudma_dev_get(udmax_np, NULL);
        if (IS_ERR(common->udmax))
                return PTR_ERR(common->udmax);
 
+       common->ringacc = xudma_get_ringacc(common->udmax);
        common->tisci_rm = xudma_dev_get_tisci_rm(common->udmax);
 
        return 0;
@@ -104,7 +114,6 @@ static int of_k3_udma_glue_parse_chn(struct device_node *chn_np,
                const char *name, struct k3_udma_glue_common *common,
                bool tx_chn)
 {
-       struct psil_endpoint_config *ep_config;
        struct of_phandle_args dma_spec;
        u32 thread_id;
        int ret = 0;
@@ -121,15 +130,26 @@ static int of_k3_udma_glue_parse_chn(struct device_node *chn_np,
                                       &dma_spec))
                return -ENOENT;
 
+       ret = of_k3_udma_glue_parse(dma_spec.np, common);
+       if (ret)
+               goto out_put_spec;
+
        thread_id = dma_spec.args[0];
        if (dma_spec.args_count == 2) {
-               if (dma_spec.args[1] > 2) {
+               if (dma_spec.args[1] > 2 && !xudma_is_pktdma(common->udmax)) {
                        dev_err(common->dev, "Invalid channel atype: %u\n",
                                dma_spec.args[1]);
                        ret = -EINVAL;
                        goto out_put_spec;
                }
-               common->atype = dma_spec.args[1];
+               if (dma_spec.args[1] > 15 && xudma_is_pktdma(common->udmax)) {
+                       dev_err(common->dev, "Invalid channel asel: %u\n",
+                               dma_spec.args[1]);
+                       ret = -EINVAL;
+                       goto out_put_spec;
+               }
+
+               common->atype_asel = dma_spec.args[1];
        }
 
        if (tx_chn && !(thread_id & K3_PSIL_DST_THREAD_ID_OFFSET)) {
@@ -143,25 +163,23 @@ static int of_k3_udma_glue_parse_chn(struct device_node *chn_np,
        }
 
        /* get psil endpoint config */
-       ep_config = psil_get_ep_config(thread_id);
-       if (IS_ERR(ep_config)) {
+       common->ep_config = psil_get_ep_config(thread_id);
+       if (IS_ERR(common->ep_config)) {
                dev_err(common->dev,
                        "No configuration for psi-l thread 0x%04x\n",
                        thread_id);
-               ret = PTR_ERR(ep_config);
+               ret = PTR_ERR(common->ep_config);
                goto out_put_spec;
        }
 
-       common->epib = ep_config->needs_epib;
-       common->psdata_size = ep_config->psd_size;
+       common->epib = common->ep_config->needs_epib;
+       common->psdata_size = common->ep_config->psd_size;
 
        if (tx_chn)
                common->dst_thread = thread_id;
        else
                common->src_thread = thread_id;
 
-       ret = of_k3_udma_glue_parse(dma_spec.np, common);
-
 out_put_spec:
        of_node_put(dma_spec.np);
        return ret;
@@ -227,7 +245,7 @@ static int k3_udma_glue_cfg_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
                req.tx_supr_tdpkt = 1;
        req.tx_fetch_size = tx_chn->common.hdesc_size >> 2;
        req.txcq_qnum = k3_ringacc_get_ring_id(tx_chn->ringtxcq);
-       req.tx_atype = tx_chn->common.atype;
+       req.tx_atype = tx_chn->common.atype_asel;
 
        return tisci_rm->tisci_udmap_ops->tx_ch_cfg(tisci_rm->tisci, &req);
 }
@@ -259,8 +277,14 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
                                                tx_chn->common.psdata_size,
                                                tx_chn->common.swdata_size);
 
+       if (xudma_is_pktdma(tx_chn->common.udmax))
+               tx_chn->udma_tchan_id = tx_chn->common.ep_config->mapped_channel_id;
+       else
+               tx_chn->udma_tchan_id = -1;
+
        /* request and cfg UDMAP TX channel */
-       tx_chn->udma_tchanx = xudma_tchan_get(tx_chn->common.udmax, -1);
+       tx_chn->udma_tchanx = xudma_tchan_get(tx_chn->common.udmax,
+                                             tx_chn->udma_tchan_id);
        if (IS_ERR(tx_chn->udma_tchanx)) {
                ret = PTR_ERR(tx_chn->udma_tchanx);
                dev_err(dev, "UDMAX tchanx get err %d\n", ret);
@@ -268,11 +292,34 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
        }
        tx_chn->udma_tchan_id = xudma_tchan_get_id(tx_chn->udma_tchanx);
 
+       tx_chn->common.chan_dev.class = &k3_udma_glue_devclass;
+       tx_chn->common.chan_dev.parent = xudma_get_device(tx_chn->common.udmax);
+       dev_set_name(&tx_chn->common.chan_dev, "tchan%d-0x%04x",
+                    tx_chn->udma_tchan_id, tx_chn->common.dst_thread);
+       ret = device_register(&tx_chn->common.chan_dev);
+       if (ret) {
+               dev_err(dev, "Channel Device registration failed %d\n", ret);
+               tx_chn->common.chan_dev.parent = NULL;
+               goto err;
+       }
+
+       if (xudma_is_pktdma(tx_chn->common.udmax)) {
+               /* prepare the channel device as coherent */
+               tx_chn->common.chan_dev.dma_coherent = true;
+               dma_coerce_mask_and_coherent(&tx_chn->common.chan_dev,
+                                            DMA_BIT_MASK(48));
+       }
+
        atomic_set(&tx_chn->free_pkts, cfg->txcq_cfg.size);
 
+       if (xudma_is_pktdma(tx_chn->common.udmax))
+               tx_chn->udma_tflow_id = tx_chn->common.ep_config->default_flow_id;
+       else
+               tx_chn->udma_tflow_id = tx_chn->udma_tchan_id;
+
        /* request and cfg rings */
        ret =  k3_ringacc_request_rings_pair(tx_chn->common.ringacc,
-                                            tx_chn->udma_tchan_id, -1,
+                                            tx_chn->udma_tflow_id, -1,
                                             &tx_chn->ringtx,
                                             &tx_chn->ringtxcq);
        if (ret) {
@@ -280,6 +327,16 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
                goto err;
        }
 
+       /* Set the dma_dev for the rings to be configured */
+       cfg->tx_cfg.dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn);
+       cfg->txcq_cfg.dma_dev = cfg->tx_cfg.dma_dev;
+
+       /* Set the ASEL value for DMA rings of PKTDMA */
+       if (xudma_is_pktdma(tx_chn->common.udmax)) {
+               cfg->tx_cfg.asel = tx_chn->common.atype_asel;
+               cfg->txcq_cfg.asel = tx_chn->common.atype_asel;
+       }
+
        ret = k3_ringacc_ring_cfg(tx_chn->ringtx, &cfg->tx_cfg);
        if (ret) {
                dev_err(dev, "Failed to cfg ringtx %d\n", ret);
@@ -303,19 +360,6 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
                goto err;
        }
 
-       ret = xudma_navss_psil_pair(tx_chn->common.udmax,
-                                   tx_chn->common.src_thread,
-                                   tx_chn->common.dst_thread);
-       if (ret) {
-               dev_err(dev, "PSI-L request err %d\n", ret);
-               goto err;
-       }
-
-       tx_chn->psil_paired = true;
-
-       /* reset TX RT registers */
-       k3_udma_glue_disable_tx_chn(tx_chn);
-
        k3_udma_glue_dump_tx_chn(tx_chn);
 
        return tx_chn;
@@ -344,6 +388,11 @@ void k3_udma_glue_release_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
 
        if (tx_chn->ringtx)
                k3_ringacc_ring_free(tx_chn->ringtx);
+
+       if (tx_chn->common.chan_dev.parent) {
+               device_unregister(&tx_chn->common.chan_dev);
+               tx_chn->common.chan_dev.parent = NULL;
+       }
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_release_tx_chn);
 
@@ -378,6 +427,18 @@ EXPORT_SYMBOL_GPL(k3_udma_glue_pop_tx_chn);
 
 int k3_udma_glue_enable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
 {
+       int ret;
+
+       ret = xudma_navss_psil_pair(tx_chn->common.udmax,
+                                   tx_chn->common.src_thread,
+                                   tx_chn->common.dst_thread);
+       if (ret) {
+               dev_err(tx_chn->common.dev, "PSI-L request err %d\n", ret);
+               return ret;
+       }
+
+       tx_chn->psil_paired = true;
+
        xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG,
                            UDMA_PEER_RT_EN_ENABLE);
 
@@ -398,6 +459,13 @@ void k3_udma_glue_disable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
        xudma_tchanrt_write(tx_chn->udma_tchanx,
                            UDMA_CHAN_RT_PEER_RT_EN_REG, 0);
        k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn dis2");
+
+       if (tx_chn->psil_paired) {
+               xudma_navss_psil_unpair(tx_chn->common.udmax,
+                                       tx_chn->common.src_thread,
+                                       tx_chn->common.dst_thread);
+               tx_chn->psil_paired = false;
+       }
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_disable_tx_chn);
 
@@ -437,13 +505,10 @@ void k3_udma_glue_reset_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
                               void *data,
                               void (*cleanup)(void *data, dma_addr_t desc_dma))
 {
+       struct device *dev = tx_chn->common.dev;
        dma_addr_t desc_dma;
        int occ_tx, i, ret;
 
-       /* reset TXCQ as it is not input for udma - expected to be empty */
-       if (tx_chn->ringtxcq)
-               k3_ringacc_ring_reset(tx_chn->ringtxcq);
-
        /*
         * TXQ reset need to be special way as it is input for udma and its
         * state cached by udma, so:
@@ -452,17 +517,20 @@ void k3_udma_glue_reset_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
         * 3) reset TXQ in a special way
         */
        occ_tx = k3_ringacc_ring_get_occ(tx_chn->ringtx);
-       dev_dbg(tx_chn->common.dev, "TX reset occ_tx %u\n", occ_tx);
+       dev_dbg(dev, "TX reset occ_tx %u\n", occ_tx);
 
        for (i = 0; i < occ_tx; i++) {
                ret = k3_ringacc_ring_pop(tx_chn->ringtx, &desc_dma);
                if (ret) {
-                       dev_err(tx_chn->common.dev, "TX reset pop %d\n", ret);
+                       if (ret != -ENODATA)
+                               dev_err(dev, "TX reset pop %d\n", ret);
                        break;
                }
                cleanup(data, desc_dma);
        }
 
+       /* reset TXCQ as it is not input for udma - expected to be empty */
+       k3_ringacc_ring_reset(tx_chn->ringtxcq);
        k3_ringacc_ring_reset_dma(tx_chn->ringtx, occ_tx);
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_reset_tx_chn);
@@ -481,12 +549,50 @@ EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_txcq_id);
 
 int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn)
 {
-       tx_chn->virq = k3_ringacc_get_ring_irq_num(tx_chn->ringtxcq);
+       if (xudma_is_pktdma(tx_chn->common.udmax)) {
+               tx_chn->virq = xudma_pktdma_tflow_get_irq(tx_chn->common.udmax,
+                                                         tx_chn->udma_tflow_id);
+       } else {
+               tx_chn->virq = k3_ringacc_get_ring_irq_num(tx_chn->ringtxcq);
+       }
 
        return tx_chn->virq;
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_irq);
 
+struct device *
+       k3_udma_glue_tx_get_dma_device(struct k3_udma_glue_tx_channel *tx_chn)
+{
+       if (xudma_is_pktdma(tx_chn->common.udmax) &&
+           (tx_chn->common.atype_asel == 14 || tx_chn->common.atype_asel == 15))
+               return &tx_chn->common.chan_dev;
+
+       return xudma_get_device(tx_chn->common.udmax);
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_dma_device);
+
+void k3_udma_glue_tx_dma_to_cppi5_addr(struct k3_udma_glue_tx_channel *tx_chn,
+                                      dma_addr_t *addr)
+{
+       if (!xudma_is_pktdma(tx_chn->common.udmax) ||
+           !tx_chn->common.atype_asel)
+               return;
+
+       *addr |= (u64)tx_chn->common.atype_asel << K3_ADDRESS_ASEL_SHIFT;
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_tx_dma_to_cppi5_addr);
+
+void k3_udma_glue_tx_cppi5_to_dma_addr(struct k3_udma_glue_tx_channel *tx_chn,
+                                      dma_addr_t *addr)
+{
+       if (!xudma_is_pktdma(tx_chn->common.udmax) ||
+           !tx_chn->common.atype_asel)
+               return;
+
+       *addr &= (u64)GENMASK(K3_ADDRESS_ASEL_SHIFT - 1, 0);
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_tx_cppi5_to_dma_addr);
+
 static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
 {
        const struct udma_tisci_rm *tisci_rm = rx_chn->common.tisci_rm;
@@ -498,8 +604,6 @@ static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
        req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |
                           TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |
                           TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID |
-                          TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID |
-                          TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID |
                           TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID;
 
        req.nav_id = tisci_rm->tisci_dev_id;
@@ -511,13 +615,16 @@ static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
         * req.rxcq_qnum = k3_ringacc_get_ring_id(rx_chn->flows[0].ringrx);
         */
        req.rxcq_qnum = 0xFFFF;
-       if (rx_chn->flow_num && rx_chn->flow_id_base != rx_chn->udma_rchan_id) {
+       if (!xudma_is_pktdma(rx_chn->common.udmax) && rx_chn->flow_num &&
+           rx_chn->flow_id_base != rx_chn->udma_rchan_id) {
                /* Default flow + extra ones */
+               req.valid_params |= TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID |
+                                   TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID;
                req.flowid_start = rx_chn->flow_id_base;
                req.flowid_cnt = rx_chn->flow_num;
        }
        req.rx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR;
-       req.rx_atype = rx_chn->common.atype;
+       req.rx_atype = rx_chn->common.atype_asel;
 
        ret = tisci_rm->tisci_udmap_ops->rx_ch_cfg(tisci_rm->tisci, &req);
        if (ret)
@@ -571,10 +678,18 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
                goto err_rflow_put;
        }
 
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               rx_ringfdq_id = flow->udma_rflow_id +
+                               xudma_get_rflow_ring_offset(rx_chn->common.udmax);
+               rx_ring_id = 0;
+       } else {
+               rx_ring_id = flow_cfg->ring_rxq_id;
+               rx_ringfdq_id = flow_cfg->ring_rxfdq0_id;
+       }
+
        /* request and cfg rings */
        ret =  k3_ringacc_request_rings_pair(rx_chn->common.ringacc,
-                                            flow_cfg->ring_rxfdq0_id,
-                                            flow_cfg->ring_rxq_id,
+                                            rx_ringfdq_id, rx_ring_id,
                                             &flow->ringrxfdq,
                                             &flow->ringrx);
        if (ret) {
@@ -582,6 +697,16 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
                goto err_rflow_put;
        }
 
+       /* Set the dma_dev for the rings to be configured */
+       flow_cfg->rx_cfg.dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn);
+       flow_cfg->rxfdq_cfg.dma_dev = flow_cfg->rx_cfg.dma_dev;
+
+       /* Set the ASEL value for DMA rings of PKTDMA */
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               flow_cfg->rx_cfg.asel = rx_chn->common.atype_asel;
+               flow_cfg->rxfdq_cfg.asel = rx_chn->common.atype_asel;
+       }
+
        ret = k3_ringacc_ring_cfg(flow->ringrx, &flow_cfg->rx_cfg);
        if (ret) {
                dev_err(dev, "Failed to cfg ringrx %d\n", ret);
@@ -740,6 +865,7 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name,
                                 struct k3_udma_glue_rx_channel_cfg *cfg)
 {
        struct k3_udma_glue_rx_channel *rx_chn;
+       struct psil_endpoint_config *ep_cfg;
        int ret, i;
 
        if (cfg->flow_id_num <= 0)
@@ -767,8 +893,16 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name,
                                                rx_chn->common.psdata_size,
                                                rx_chn->common.swdata_size);
 
+       ep_cfg = rx_chn->common.ep_config;
+
+       if (xudma_is_pktdma(rx_chn->common.udmax))
+               rx_chn->udma_rchan_id = ep_cfg->mapped_channel_id;
+       else
+               rx_chn->udma_rchan_id = -1;
+
        /* request and cfg UDMAP RX channel */
-       rx_chn->udma_rchanx = xudma_rchan_get(rx_chn->common.udmax, -1);
+       rx_chn->udma_rchanx = xudma_rchan_get(rx_chn->common.udmax,
+                                             rx_chn->udma_rchan_id);
        if (IS_ERR(rx_chn->udma_rchanx)) {
                ret = PTR_ERR(rx_chn->udma_rchanx);
                dev_err(dev, "UDMAX rchanx get err %d\n", ret);
@@ -776,12 +910,48 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name,
        }
        rx_chn->udma_rchan_id = xudma_rchan_get_id(rx_chn->udma_rchanx);
 
-       rx_chn->flow_num = cfg->flow_id_num;
-       rx_chn->flow_id_base = cfg->flow_id_base;
+       rx_chn->common.chan_dev.class = &k3_udma_glue_devclass;
+       rx_chn->common.chan_dev.parent = xudma_get_device(rx_chn->common.udmax);
+       dev_set_name(&rx_chn->common.chan_dev, "rchan%d-0x%04x",
+                    rx_chn->udma_rchan_id, rx_chn->common.src_thread);
+       ret = device_register(&rx_chn->common.chan_dev);
+       if (ret) {
+               dev_err(dev, "Channel Device registration failed %d\n", ret);
+               rx_chn->common.chan_dev.parent = NULL;
+               goto err;
+       }
 
-       /* Use RX channel id as flow id: target dev can't generate flow_id */
-       if (cfg->flow_id_use_rxchan_id)
-               rx_chn->flow_id_base = rx_chn->udma_rchan_id;
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               /* prepare the channel device as coherent */
+               rx_chn->common.chan_dev.dma_coherent = true;
+               dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev,
+                                            DMA_BIT_MASK(48));
+       }
+
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               int flow_start = cfg->flow_id_base;
+               int flow_end;
+
+               if (flow_start == -1)
+                       flow_start = ep_cfg->flow_start;
+
+               flow_end = flow_start + cfg->flow_id_num - 1;
+               if (flow_start < ep_cfg->flow_start ||
+                   flow_end > (ep_cfg->flow_start + ep_cfg->flow_num - 1)) {
+                       dev_err(dev, "Invalid flow range requested\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               rx_chn->flow_id_base = flow_start;
+       } else {
+               rx_chn->flow_id_base = cfg->flow_id_base;
+
+               /* Use RX channel id as flow id: target dev can't generate flow_id */
+               if (cfg->flow_id_use_rxchan_id)
+                       rx_chn->flow_id_base = rx_chn->udma_rchan_id;
+       }
+
+       rx_chn->flow_num = cfg->flow_id_num;
 
        rx_chn->flows = devm_kcalloc(dev, rx_chn->flow_num,
                                     sizeof(*rx_chn->flows), GFP_KERNEL);
@@ -815,19 +985,6 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name,
                        goto err;
        }
 
-       ret = xudma_navss_psil_pair(rx_chn->common.udmax,
-                                   rx_chn->common.src_thread,
-                                   rx_chn->common.dst_thread);
-       if (ret) {
-               dev_err(dev, "PSI-L request err %d\n", ret);
-               goto err;
-       }
-
-       rx_chn->psil_paired = true;
-
-       /* reset RX RT registers */
-       k3_udma_glue_disable_rx_chn(rx_chn);
-
        k3_udma_glue_dump_rx_chn(rx_chn);
 
        return rx_chn;
@@ -884,6 +1041,24 @@ k3_udma_glue_request_remote_rx_chn(struct device *dev, const char *name,
                goto err;
        }
 
+       rx_chn->common.chan_dev.class = &k3_udma_glue_devclass;
+       rx_chn->common.chan_dev.parent = xudma_get_device(rx_chn->common.udmax);
+       dev_set_name(&rx_chn->common.chan_dev, "rchan_remote-0x%04x",
+                    rx_chn->common.src_thread);
+       ret = device_register(&rx_chn->common.chan_dev);
+       if (ret) {
+               dev_err(dev, "Channel Device registration failed %d\n", ret);
+               rx_chn->common.chan_dev.parent = NULL;
+               goto err;
+       }
+
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               /* prepare the channel device as coherent */
+               rx_chn->common.chan_dev.dma_coherent = true;
+               dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev,
+                                            DMA_BIT_MASK(48));
+       }
+
        ret = k3_udma_glue_allocate_rx_flows(rx_chn, cfg);
        if (ret)
                goto err;
@@ -936,6 +1111,11 @@ void k3_udma_glue_release_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
        if (!IS_ERR_OR_NULL(rx_chn->udma_rchanx))
                xudma_rchan_put(rx_chn->common.udmax,
                                rx_chn->udma_rchanx);
+
+       if (rx_chn->common.chan_dev.parent) {
+               device_unregister(&rx_chn->common.chan_dev);
+               rx_chn->common.chan_dev.parent = NULL;
+       }
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_release_rx_chn);
 
@@ -1052,12 +1232,24 @@ EXPORT_SYMBOL_GPL(k3_udma_glue_rx_flow_disable);
 
 int k3_udma_glue_enable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
 {
+       int ret;
+
        if (rx_chn->remote)
                return -EINVAL;
 
        if (rx_chn->flows_ready < rx_chn->flow_num)
                return -EINVAL;
 
+       ret = xudma_navss_psil_pair(rx_chn->common.udmax,
+                                   rx_chn->common.src_thread,
+                                   rx_chn->common.dst_thread);
+       if (ret) {
+               dev_err(rx_chn->common.dev, "PSI-L request err %d\n", ret);
+               return ret;
+       }
+
+       rx_chn->psil_paired = true;
+
        xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG,
                            UDMA_CHAN_RT_CTL_EN);
 
@@ -1078,6 +1270,13 @@ void k3_udma_glue_disable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
        xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG, 0);
 
        k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt dis2");
+
+       if (rx_chn->psil_paired) {
+               xudma_navss_psil_unpair(rx_chn->common.udmax,
+                                       rx_chn->common.src_thread,
+                                       rx_chn->common.dst_thread);
+               rx_chn->psil_paired = false;
+       }
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_disable_rx_chn);
 
@@ -1128,12 +1327,10 @@ void k3_udma_glue_reset_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
        /* reset RXCQ as it is not input for udma - expected to be empty */
        occ_rx = k3_ringacc_ring_get_occ(flow->ringrx);
        dev_dbg(dev, "RX reset flow %u occ_rx %u\n", flow_num, occ_rx);
-       if (flow->ringrx)
-               k3_ringacc_ring_reset(flow->ringrx);
 
        /* Skip RX FDQ in case one FDQ is used for the set of flows */
        if (skip_fdq)
-               return;
+               goto do_reset;
 
        /*
         * RX FDQ reset need to be special way as it is input for udma and its
@@ -1148,13 +1345,17 @@ void k3_udma_glue_reset_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
        for (i = 0; i < occ_rx; i++) {
                ret = k3_ringacc_ring_pop(flow->ringrxfdq, &desc_dma);
                if (ret) {
-                       dev_err(dev, "RX reset pop %d\n", ret);
+                       if (ret != -ENODATA)
+                               dev_err(dev, "RX reset pop %d\n", ret);
                        break;
                }
                cleanup(data, desc_dma);
        }
 
        k3_ringacc_ring_reset_dma(flow->ringrxfdq, occ_rx);
+
+do_reset:
+       k3_ringacc_ring_reset(flow->ringrx);
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_reset_rx_chn);
 
@@ -1184,8 +1385,52 @@ int k3_udma_glue_rx_get_irq(struct k3_udma_glue_rx_channel *rx_chn,
 
        flow = &rx_chn->flows[flow_num];
 
-       flow->virq = k3_ringacc_get_ring_irq_num(flow->ringrx);
+       if (xudma_is_pktdma(rx_chn->common.udmax)) {
+               flow->virq = xudma_pktdma_rflow_get_irq(rx_chn->common.udmax,
+                                                       flow->udma_rflow_id);
+       } else {
+               flow->virq = k3_ringacc_get_ring_irq_num(flow->ringrx);
+       }
 
        return flow->virq;
 }
 EXPORT_SYMBOL_GPL(k3_udma_glue_rx_get_irq);
+
+struct device *
+       k3_udma_glue_rx_get_dma_device(struct k3_udma_glue_rx_channel *rx_chn)
+{
+       if (xudma_is_pktdma(rx_chn->common.udmax) &&
+           (rx_chn->common.atype_asel == 14 || rx_chn->common.atype_asel == 15))
+               return &rx_chn->common.chan_dev;
+
+       return xudma_get_device(rx_chn->common.udmax);
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_rx_get_dma_device);
+
+void k3_udma_glue_rx_dma_to_cppi5_addr(struct k3_udma_glue_rx_channel *rx_chn,
+                                      dma_addr_t *addr)
+{
+       if (!xudma_is_pktdma(rx_chn->common.udmax) ||
+           !rx_chn->common.atype_asel)
+               return;
+
+       *addr |= (u64)rx_chn->common.atype_asel << K3_ADDRESS_ASEL_SHIFT;
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_rx_dma_to_cppi5_addr);
+
+void k3_udma_glue_rx_cppi5_to_dma_addr(struct k3_udma_glue_rx_channel *rx_chn,
+                                      dma_addr_t *addr)
+{
+       if (!xudma_is_pktdma(rx_chn->common.udmax) ||
+           !rx_chn->common.atype_asel)
+               return;
+
+       *addr &= (u64)GENMASK(K3_ADDRESS_ASEL_SHIFT - 1, 0);
+}
+EXPORT_SYMBOL_GPL(k3_udma_glue_rx_cppi5_to_dma_addr);
+
+static int __init k3_udma_glue_class_init(void)
+{
+       return class_register(&k3_udma_glue_devclass);
+}
+arch_initcall(k3_udma_glue_class_init);
index 8563a392f30bfcfdbef84869736f64ed26c46808..aada84f40723c3328ffacc08422961c17eb2dc75 100644 (file)
@@ -50,6 +50,18 @@ struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property)
 }
 EXPORT_SYMBOL(of_xudma_dev_get);
 
+struct device *xudma_get_device(struct udma_dev *ud)
+{
+       return ud->dev;
+}
+EXPORT_SYMBOL(xudma_get_device);
+
+struct k3_ringacc *xudma_get_ringacc(struct udma_dev *ud)
+{
+       return ud->ringacc;
+}
+EXPORT_SYMBOL(xudma_get_ringacc);
+
 u32 xudma_dev_get_psil_base(struct udma_dev *ud)
 {
        return ud->psil_base;
@@ -76,6 +88,9 @@ EXPORT_SYMBOL(xudma_free_gp_rflow_range);
 
 bool xudma_rflow_is_gp(struct udma_dev *ud, int id)
 {
+       if (!ud->rflow_gp_map)
+               return false;
+
        return !test_bit(id, ud->rflow_gp_map);
 }
 EXPORT_SYMBOL(xudma_rflow_is_gp);
@@ -107,6 +122,12 @@ void xudma_rflow_put(struct udma_dev *ud, struct udma_rflow *p)
 }
 EXPORT_SYMBOL(xudma_rflow_put);
 
+int xudma_get_rflow_ring_offset(struct udma_dev *ud)
+{
+       return ud->tflow_cnt;
+}
+EXPORT_SYMBOL(xudma_get_rflow_ring_offset);
+
 #define XUDMA_GET_RESOURCE_ID(res)                                     \
 int xudma_##res##_get_id(struct udma_##res *p)                         \
 {                                                                      \
@@ -136,3 +157,27 @@ void xudma_##res##rt_write(struct udma_##res *p, int reg, u32 val) \
 EXPORT_SYMBOL(xudma_##res##rt_write)
 XUDMA_RT_IO_FUNCTIONS(tchan);
 XUDMA_RT_IO_FUNCTIONS(rchan);
+
+int xudma_is_pktdma(struct udma_dev *ud)
+{
+       return ud->match_data->type == DMA_TYPE_PKTDMA;
+}
+EXPORT_SYMBOL(xudma_is_pktdma);
+
+int xudma_pktdma_tflow_get_irq(struct udma_dev *ud, int udma_tflow_id)
+{
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+
+       return ti_sci_inta_msi_get_virq(ud->dev, udma_tflow_id +
+                                       oes->pktdma_tchan_flow);
+}
+EXPORT_SYMBOL(xudma_pktdma_tflow_get_irq);
+
+int xudma_pktdma_rflow_get_irq(struct udma_dev *ud, int udma_rflow_id)
+{
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+
+       return ti_sci_inta_msi_get_virq(ud->dev, udma_rflow_id +
+                                       oes->pktdma_rchan_flow);
+}
+EXPORT_SYMBOL(xudma_pktdma_rflow_get_irq);
index 82cf6c77f5c935b615cf3144fe4e7880a7553091..87157cbae1b8e782b414fd768f330b878b5b016d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/soc/ti/k3-ringacc.h>
 #include <linux/soc/ti/ti_sci_protocol.h>
 #include <linux/soc/ti/ti_sci_inta_msi.h>
+#include <linux/dma/k3-event-router.h>
 #include <linux/dma/ti-cppi5.h>
 
 #include "../virt-dma.h"
@@ -55,14 +56,26 @@ struct udma_static_tr {
 
 struct udma_chan;
 
+enum k3_dma_type {
+       DMA_TYPE_UDMA = 0,
+       DMA_TYPE_BCDMA,
+       DMA_TYPE_PKTDMA,
+};
+
 enum udma_mmr {
        MMR_GCFG = 0,
+       MMR_BCHANRT,
        MMR_RCHANRT,
        MMR_TCHANRT,
        MMR_LAST,
 };
 
-static const char * const mmr_names[] = { "gcfg", "rchanrt", "tchanrt" };
+static const char * const mmr_names[] = {
+       [MMR_GCFG] = "gcfg",
+       [MMR_BCHANRT] = "bchanrt",
+       [MMR_RCHANRT] = "rchanrt",
+       [MMR_TCHANRT] = "tchanrt",
+};
 
 struct udma_tchan {
        void __iomem *reg_rt;
@@ -70,8 +83,12 @@ struct udma_tchan {
        int id;
        struct k3_ring *t_ring; /* Transmit ring */
        struct k3_ring *tc_ring; /* Transmit Completion ring */
+       int tflow_id; /* applicable only for PKTDMA */
+
 };
 
+#define udma_bchan udma_tchan
+
 struct udma_rflow {
        int id;
        struct k3_ring *fd_ring; /* Free Descriptor ring */
@@ -84,10 +101,29 @@ struct udma_rchan {
        int id;
 };
 
+struct udma_oes_offsets {
+       /* K3 UDMA Output Event Offset */
+       u32 udma_rchan;
+
+       /* BCDMA Output Event Offsets */
+       u32 bcdma_bchan_data;
+       u32 bcdma_bchan_ring;
+       u32 bcdma_tchan_data;
+       u32 bcdma_tchan_ring;
+       u32 bcdma_rchan_data;
+       u32 bcdma_rchan_ring;
+
+       /* PKTDMA Output Event Offsets */
+       u32 pktdma_tchan_flow;
+       u32 pktdma_rchan_flow;
+};
+
 #define UDMA_FLAG_PDMA_ACC32           BIT(0)
 #define UDMA_FLAG_PDMA_BURST           BIT(1)
+#define UDMA_FLAG_TDTYPE               BIT(2)
 
 struct udma_match_data {
+       enum k3_dma_type type;
        u32 psil_base;
        bool enable_memcpy_support;
        u32 flags;
@@ -95,7 +131,8 @@ struct udma_match_data {
 };
 
 struct udma_soc_data {
-       u32 rchan_oes_offset;
+       struct udma_oes_offsets oes;
+       u32 bcdma_trigger_event_offset;
 };
 
 struct udma_hwdesc {
@@ -116,6 +153,11 @@ struct udma_rx_flush {
        dma_addr_t buffer_paddr;
 };
 
+struct udma_tpl {
+       u8 levels;
+       u32 start_idx[3];
+};
+
 struct udma_dev {
        struct dma_device ddev;
        struct device *dev;
@@ -123,8 +165,9 @@ struct udma_dev {
        const struct udma_match_data *match_data;
        const struct udma_soc_data *soc_data;
 
-       u8 tpl_levels;
-       u32 tpl_start_idx[3];
+       struct udma_tpl bchan_tpl;
+       struct udma_tpl tchan_tpl;
+       struct udma_tpl rchan_tpl;
 
        size_t desc_align; /* alignment to use for descriptors */
 
@@ -138,16 +181,21 @@ struct udma_dev {
 
        struct udma_rx_flush rx_flush;
 
+       int bchan_cnt;
        int tchan_cnt;
        int echan_cnt;
        int rchan_cnt;
        int rflow_cnt;
+       int tflow_cnt;
+       unsigned long *bchan_map;
        unsigned long *tchan_map;
        unsigned long *rchan_map;
        unsigned long *rflow_gp_map;
        unsigned long *rflow_gp_map_allocated;
        unsigned long *rflow_in_use;
+       unsigned long *tflow_map;
 
+       struct udma_bchan *bchans;
        struct udma_tchan *tchans;
        struct udma_rchan *rchans;
        struct udma_rflow *rflows;
@@ -155,6 +203,7 @@ struct udma_dev {
        struct udma_chan *channels;
        u32 psil_base;
        u32 atype;
+       u32 asel;
 };
 
 struct udma_desc {
@@ -199,6 +248,7 @@ struct udma_chan_config {
        bool notdpkt; /* Suppress sending TDC packet */
        int remote_thread_id;
        u32 atype;
+       u32 asel;
        u32 src_thread;
        u32 dst_thread;
        enum psil_endpoint_type ep_type;
@@ -206,6 +256,13 @@ struct udma_chan_config {
        bool enable_burst;
        enum udma_tp_level channel_tpl; /* Channel Throughput Level */
 
+       u32 tr_trigger_type;
+
+       /* PKDMA mapped channel */
+       int mapped_channel_id;
+       /* PKTDMA default tflow or rflow for mapped channel */
+       int default_flow_id;
+
        enum dma_transfer_direction dir;
 };
 
@@ -213,11 +270,13 @@ struct udma_chan {
        struct virt_dma_chan vc;
        struct dma_slave_config cfg;
        struct udma_dev *ud;
+       struct device *dma_dev;
        struct udma_desc *desc;
        struct udma_desc *terminated_desc;
        struct udma_static_tr static_tr;
        char *name;
 
+       struct udma_bchan *bchan;
        struct udma_tchan *tchan;
        struct udma_rchan *rchan;
        struct udma_rflow *rflow;
@@ -353,10 +412,36 @@ static int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
                                                src_thread, dst_thread);
 }
 
+static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel)
+{
+       struct device *chan_dev = &chan->dev->device;
+
+       if (asel == 0) {
+               /* No special handling for the channel */
+               chan->dev->chan_dma_dev = false;
+
+               chan_dev->dma_coherent = false;
+               chan_dev->dma_parms = NULL;
+       } else if (asel == 14 || asel == 15) {
+               chan->dev->chan_dma_dev = true;
+
+               chan_dev->dma_coherent = true;
+               dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48));
+               chan_dev->dma_parms = chan_dev->parent->dma_parms;
+       } else {
+               dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel);
+
+               chan_dev->dma_coherent = false;
+               chan_dev->dma_parms = NULL;
+       }
+}
+
 static void udma_reset_uchan(struct udma_chan *uc)
 {
        memset(&uc->config, 0, sizeof(uc->config));
        uc->config.remote_thread_id = -1;
+       uc->config.mapped_channel_id = -1;
+       uc->config.default_flow_id = -1;
        uc->state = UDMA_CHAN_IS_IDLE;
 }
 
@@ -439,9 +524,7 @@ static void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
                        d->hwdesc[i].cppi5_desc_vaddr = NULL;
                }
        } else if (d->hwdesc[0].cppi5_desc_vaddr) {
-               struct udma_dev *ud = uc->ud;
-
-               dma_free_coherent(ud->dev, d->hwdesc[0].cppi5_desc_size,
+               dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
                                  d->hwdesc[0].cppi5_desc_vaddr,
                                  d->hwdesc[0].cppi5_desc_paddr);
 
@@ -670,8 +753,10 @@ static void udma_reset_counters(struct udma_chan *uc)
                val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PCNT_REG);
                udma_tchanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val);
 
-               val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
-               udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
+               if (!uc->bchan) {
+                       val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
+                       udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
+               }
        }
 
        if (uc->rchan) {
@@ -746,10 +831,16 @@ static void udma_start_desc(struct udma_chan *uc)
 {
        struct udma_chan_config *ucc = &uc->config;
 
-       if (ucc->pkt_mode && (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
+           (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
                int i;
 
-               /* Push all descriptors to ring for packet mode cyclic or RX */
+               /*
+                * UDMA only: Push all descriptors to ring for packet mode
+                * cyclic or RX
+                * PKTDMA supports pre-linked descriptor and cyclic is not
+                * supported
+                */
                for (i = 0; i < uc->desc->sglen; i++)
                        udma_push_to_ring(uc, i);
        } else {
@@ -1020,13 +1111,12 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
 {
        struct udma_chan *uc = data;
        struct udma_desc *d;
-       unsigned long flags;
        dma_addr_t paddr = 0;
 
        if (udma_pop_from_ring(uc, &paddr) || !paddr)
                return IRQ_HANDLED;
 
-       spin_lock_irqsave(&uc->vc.lock, flags);
+       spin_lock(&uc->vc.lock);
 
        /* Teardown completion message */
        if (cppi5_desc_is_tdcm(paddr)) {
@@ -1077,7 +1167,7 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
                }
        }
 out:
-       spin_unlock_irqrestore(&uc->vc.lock, flags);
+       spin_unlock(&uc->vc.lock);
 
        return IRQ_HANDLED;
 }
@@ -1086,9 +1176,8 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
 {
        struct udma_chan *uc = data;
        struct udma_desc *d;
-       unsigned long flags;
 
-       spin_lock_irqsave(&uc->vc.lock, flags);
+       spin_lock(&uc->vc.lock);
        d = uc->desc;
        if (d) {
                d->tr_idx = (d->tr_idx + 1) % d->sglen;
@@ -1103,7 +1192,7 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
                }
        }
 
-       spin_unlock_irqrestore(&uc->vc.lock, flags);
+       spin_unlock(&uc->vc.lock);
 
        return IRQ_HANDLED;
 }
@@ -1181,10 +1270,12 @@ static struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id)
        if (test_bit(id, ud->rflow_in_use))
                return ERR_PTR(-ENOENT);
 
-       /* GP rflow has to be allocated first */
-       if (!test_bit(id, ud->rflow_gp_map) &&
-           !test_bit(id, ud->rflow_gp_map_allocated))
-               return ERR_PTR(-EINVAL);
+       if (ud->rflow_gp_map) {
+               /* GP rflow has to be allocated first */
+               if (!test_bit(id, ud->rflow_gp_map) &&
+                   !test_bit(id, ud->rflow_gp_map_allocated))
+                       return ERR_PTR(-EINVAL);
+       }
 
        dev_dbg(ud->dev, "get rflow%d\n", id);
        set_bit(id, ud->rflow_in_use);
@@ -1215,10 +1306,10 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud,     \
        } else {                                                        \
                int start;                                              \
                                                                        \
-               if (tpl >= ud->tpl_levels)                              \
-                       tpl = ud->tpl_levels - 1;                       \
+               if (tpl >= ud->res##_tpl.levels)                        \
+                       tpl = ud->res##_tpl.levels - 1;                 \
                                                                        \
-               start = ud->tpl_start_idx[tpl];                         \
+               start = ud->res##_tpl.start_idx[tpl];                   \
                                                                        \
                id = find_next_zero_bit(ud->res##_map, ud->res##_cnt,   \
                                        start);                         \
@@ -1231,9 +1322,39 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud,      \
        return &ud->res##s[id];                                         \
 }
 
+UDMA_RESERVE_RESOURCE(bchan);
 UDMA_RESERVE_RESOURCE(tchan);
 UDMA_RESERVE_RESOURCE(rchan);
 
+static int bcdma_get_bchan(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       enum udma_tp_level tpl;
+
+       if (uc->bchan) {
+               dev_dbg(ud->dev, "chan%d: already have bchan%d allocated\n",
+                       uc->id, uc->bchan->id);
+               return 0;
+       }
+
+       /*
+        * Use normal channels for peripherals, and highest TPL channel for
+        * mem2mem
+        */
+       if (uc->config.tr_trigger_type)
+               tpl = 0;
+       else
+               tpl = ud->bchan_tpl.levels - 1;
+
+       uc->bchan = __udma_reserve_bchan(ud, tpl, -1);
+       if (IS_ERR(uc->bchan))
+               return PTR_ERR(uc->bchan);
+
+       uc->tchan = uc->bchan;
+
+       return 0;
+}
+
 static int udma_get_tchan(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1244,9 +1365,39 @@ static int udma_get_tchan(struct udma_chan *uc)
                return 0;
        }
 
-       uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl, -1);
+       /*
+        * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels.
+        * For PKTDMA mapped channels it is configured to a channel which must
+        * be used to service the peripheral.
+        */
+       uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl,
+                                        uc->config.mapped_channel_id);
+       if (IS_ERR(uc->tchan))
+               return PTR_ERR(uc->tchan);
+
+       if (ud->tflow_cnt) {
+               int tflow_id;
+
+               /* Only PKTDMA have support for tx flows */
+               if (uc->config.default_flow_id >= 0)
+                       tflow_id = uc->config.default_flow_id;
+               else
+                       tflow_id = uc->tchan->id;
+
+               if (test_bit(tflow_id, ud->tflow_map)) {
+                       dev_err(ud->dev, "tflow%d is in use\n", tflow_id);
+                       clear_bit(uc->tchan->id, ud->tchan_map);
+                       uc->tchan = NULL;
+                       return -ENOENT;
+               }
+
+               uc->tchan->tflow_id = tflow_id;
+               set_bit(tflow_id, ud->tflow_map);
+       } else {
+               uc->tchan->tflow_id = -1;
+       }
 
-       return PTR_ERR_OR_ZERO(uc->tchan);
+       return 0;
 }
 
 static int udma_get_rchan(struct udma_chan *uc)
@@ -1259,7 +1410,13 @@ static int udma_get_rchan(struct udma_chan *uc)
                return 0;
        }
 
-       uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl, -1);
+       /*
+        * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels.
+        * For PKTDMA mapped channels it is configured to a channel which must
+        * be used to service the peripheral.
+        */
+       uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl,
+                                        uc->config.mapped_channel_id);
 
        return PTR_ERR_OR_ZERO(uc->rchan);
 }
@@ -1287,8 +1444,11 @@ static int udma_get_chan_pair(struct udma_chan *uc)
 
        /* Can be optimized, but let's have it like this for now */
        end = min(ud->tchan_cnt, ud->rchan_cnt);
-       /* Try to use the highest TPL channel pair for MEM_TO_MEM channels */
-       chan_id = ud->tpl_start_idx[ud->tpl_levels - 1];
+       /*
+        * Try to use the highest TPL channel pair for MEM_TO_MEM channels
+        * Note: in UDMAP the channel TPL is symmetric between tchan and rchan
+        */
+       chan_id = ud->tchan_tpl.start_idx[ud->tchan_tpl.levels - 1];
        for (; chan_id < end; chan_id++) {
                if (!test_bit(chan_id, ud->tchan_map) &&
                    !test_bit(chan_id, ud->rchan_map))
@@ -1303,6 +1463,9 @@ static int udma_get_chan_pair(struct udma_chan *uc)
        uc->tchan = &ud->tchans[chan_id];
        uc->rchan = &ud->rchans[chan_id];
 
+       /* UDMA does not use tx flows */
+       uc->tchan->tflow_id = -1;
+
        return 0;
 }
 
@@ -1326,6 +1489,19 @@ static int udma_get_rflow(struct udma_chan *uc, int flow_id)
        return PTR_ERR_OR_ZERO(uc->rflow);
 }
 
+static void bcdma_put_bchan(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+
+       if (uc->bchan) {
+               dev_dbg(ud->dev, "chan%d: put bchan%d\n", uc->id,
+                       uc->bchan->id);
+               clear_bit(uc->bchan->id, ud->bchan_map);
+               uc->bchan = NULL;
+               uc->tchan = NULL;
+       }
+}
+
 static void udma_put_rchan(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1346,6 +1522,10 @@ static void udma_put_tchan(struct udma_chan *uc)
                dev_dbg(ud->dev, "chan%d: put tchan%d\n", uc->id,
                        uc->tchan->id);
                clear_bit(uc->tchan->id, ud->tchan_map);
+
+               if (uc->tchan->tflow_id >= 0)
+                       clear_bit(uc->tchan->tflow_id, ud->tflow_map);
+
                uc->tchan = NULL;
        }
 }
@@ -1362,6 +1542,65 @@ static void udma_put_rflow(struct udma_chan *uc)
        }
 }
 
+static void bcdma_free_bchan_resources(struct udma_chan *uc)
+{
+       if (!uc->bchan)
+               return;
+
+       k3_ringacc_ring_free(uc->bchan->tc_ring);
+       k3_ringacc_ring_free(uc->bchan->t_ring);
+       uc->bchan->tc_ring = NULL;
+       uc->bchan->t_ring = NULL;
+       k3_configure_chan_coherency(&uc->vc.chan, 0);
+
+       bcdma_put_bchan(uc);
+}
+
+static int bcdma_alloc_bchan_resources(struct udma_chan *uc)
+{
+       struct k3_ring_cfg ring_cfg;
+       struct udma_dev *ud = uc->ud;
+       int ret;
+
+       ret = bcdma_get_bchan(uc);
+       if (ret)
+               return ret;
+
+       ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->bchan->id, -1,
+                                           &uc->bchan->t_ring,
+                                           &uc->bchan->tc_ring);
+       if (ret) {
+               ret = -EBUSY;
+               goto err_ring;
+       }
+
+       memset(&ring_cfg, 0, sizeof(ring_cfg));
+       ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
+       ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
+
+       k3_configure_chan_coherency(&uc->vc.chan, ud->asel);
+       ring_cfg.asel = ud->asel;
+       ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+
+       ret = k3_ringacc_ring_cfg(uc->bchan->t_ring, &ring_cfg);
+       if (ret)
+               goto err_ringcfg;
+
+       return 0;
+
+err_ringcfg:
+       k3_ringacc_ring_free(uc->bchan->tc_ring);
+       uc->bchan->tc_ring = NULL;
+       k3_ringacc_ring_free(uc->bchan->t_ring);
+       uc->bchan->t_ring = NULL;
+       k3_configure_chan_coherency(&uc->vc.chan, 0);
+err_ring:
+       bcdma_put_bchan(uc);
+
+       return ret;
+}
+
 static void udma_free_tx_resources(struct udma_chan *uc)
 {
        if (!uc->tchan)
@@ -1379,15 +1618,22 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
 {
        struct k3_ring_cfg ring_cfg;
        struct udma_dev *ud = uc->ud;
-       int ret;
+       struct udma_tchan *tchan;
+       int ring_idx, ret;
 
        ret = udma_get_tchan(uc);
        if (ret)
                return ret;
 
-       ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->tchan->id, -1,
-                                           &uc->tchan->t_ring,
-                                           &uc->tchan->tc_ring);
+       tchan = uc->tchan;
+       if (tchan->tflow_id >= 0)
+               ring_idx = tchan->tflow_id;
+       else
+               ring_idx = ud->bchan_cnt + tchan->id;
+
+       ret = k3_ringacc_request_rings_pair(ud->ringacc, ring_idx, -1,
+                                           &tchan->t_ring,
+                                           &tchan->tc_ring);
        if (ret) {
                ret = -EBUSY;
                goto err_ring;
@@ -1396,10 +1642,18 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
        memset(&ring_cfg, 0, sizeof(ring_cfg));
        ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
        ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
-       ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       } else {
+               ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
 
-       ret = k3_ringacc_ring_cfg(uc->tchan->t_ring, &ring_cfg);
-       ret |= k3_ringacc_ring_cfg(uc->tchan->tc_ring, &ring_cfg);
+               k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel);
+               ring_cfg.asel = uc->config.asel;
+               ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+       }
+
+       ret = k3_ringacc_ring_cfg(tchan->t_ring, &ring_cfg);
+       ret |= k3_ringacc_ring_cfg(tchan->tc_ring, &ring_cfg);
 
        if (ret)
                goto err_ringcfg;
@@ -1452,14 +1706,23 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
        if (uc->config.dir == DMA_MEM_TO_MEM)
                return 0;
 
-       ret = udma_get_rflow(uc, uc->rchan->id);
+       if (uc->config.default_flow_id >= 0)
+               ret = udma_get_rflow(uc, uc->config.default_flow_id);
+       else
+               ret = udma_get_rflow(uc, uc->rchan->id);
+
        if (ret) {
                ret = -EBUSY;
                goto err_rflow;
        }
 
        rflow = uc->rflow;
-       fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id;
+       if (ud->tflow_cnt)
+               fd_ring_id = ud->tflow_cnt + rflow->id;
+       else
+               fd_ring_id = ud->bchan_cnt + ud->tchan_cnt + ud->echan_cnt +
+                            uc->rchan->id;
+
        ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1,
                                            &rflow->fd_ring, &rflow->r_ring);
        if (ret) {
@@ -1469,15 +1732,25 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
 
        memset(&ring_cfg, 0, sizeof(ring_cfg));
 
-       if (uc->config.pkt_mode)
-               ring_cfg.size = SG_MAX_SEGMENTS;
-       else
+       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               if (uc->config.pkt_mode)
+                       ring_cfg.size = SG_MAX_SEGMENTS;
+               else
+                       ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+
+               ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       } else {
                ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+               ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
 
-       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
-       ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+               k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel);
+               ring_cfg.asel = uc->config.asel;
+               ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+       }
 
        ret = k3_ringacc_ring_cfg(rflow->fd_ring, &ring_cfg);
+
        ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
        ret |= k3_ringacc_ring_cfg(rflow->r_ring, &ring_cfg);
 
@@ -1499,7 +1772,18 @@ err_rflow:
        return ret;
 }
 
-#define TISCI_TCHAN_VALID_PARAMS (                             \
+#define TISCI_BCDMA_BCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_EXTENDED_CH_TYPE_VALID)
+
+#define TISCI_BCDMA_TCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID)
+
+#define TISCI_BCDMA_RCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID)
+
+#define TISCI_UDMA_TCHAN_VALID_PARAMS (                                \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_EINFO_VALID |      \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_PSWORDS_VALID |    \
@@ -1509,7 +1793,7 @@ err_rflow:
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |            \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
 
-#define TISCI_RCHAN_VALID_PARAMS (                             \
+#define TISCI_UDMA_RCHAN_VALID_PARAMS (                                \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |         \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |            \
@@ -1534,7 +1818,7 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
        struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
        struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
 
-       req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS;
+       req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS;
        req_tx.nav_id = tisci_rm->tisci_dev_id;
        req_tx.index = tchan->id;
        req_tx.tx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR;
@@ -1548,7 +1832,7 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
                return ret;
        }
 
-       req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS;
+       req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS;
        req_rx.nav_id = tisci_rm->tisci_dev_id;
        req_rx.index = rchan->id;
        req_rx.rx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2;
@@ -1563,6 +1847,27 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
        return ret;
 }
 
+static int bcdma_tisci_m2m_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
+       struct udma_bchan *bchan = uc->bchan;
+       int ret = 0;
+
+       req_tx.valid_params = TISCI_BCDMA_BCHAN_VALID_PARAMS;
+       req_tx.nav_id = tisci_rm->tisci_dev_id;
+       req_tx.extended_ch_type = TI_SCI_RM_BCDMA_EXTENDED_CH_TYPE_BCHAN;
+       req_tx.index = bchan->id;
+
+       ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
+       if (ret)
+               dev_err(ud->dev, "bchan%d cfg failed %d\n", bchan->id, ret);
+
+       return ret;
+}
+
 static int udma_tisci_tx_channel_config(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1583,7 +1888,7 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
                fetch_size = sizeof(struct cppi5_desc_hdr_t);
        }
 
-       req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS;
+       req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS;
        req_tx.nav_id = tisci_rm->tisci_dev_id;
        req_tx.index = tchan->id;
        req_tx.tx_chan_type = mode;
@@ -1591,6 +1896,40 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
        req_tx.tx_fetch_size = fetch_size >> 2;
        req_tx.txcq_qnum = tc_ring;
        req_tx.tx_atype = uc->config.atype;
+       if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
+           ud->match_data->flags & UDMA_FLAG_TDTYPE) {
+               /* wait for peer to complete the teardown for PDMAs */
+               req_tx.valid_params |=
+                               TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID;
+               req_tx.tx_tdtype = 1;
+       }
+
+       ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
+       if (ret)
+               dev_err(ud->dev, "tchan%d cfg failed %d\n", tchan->id, ret);
+
+       return ret;
+}
+
+static int bcdma_tisci_tx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct udma_tchan *tchan = uc->tchan;
+       struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
+       int ret = 0;
+
+       req_tx.valid_params = TISCI_BCDMA_TCHAN_VALID_PARAMS;
+       req_tx.nav_id = tisci_rm->tisci_dev_id;
+       req_tx.index = tchan->id;
+       req_tx.tx_supr_tdpkt = uc->config.notdpkt;
+       if (ud->match_data->flags & UDMA_FLAG_TDTYPE) {
+               /* wait for peer to complete the teardown for PDMAs */
+               req_tx.valid_params |=
+                               TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID;
+               req_tx.tx_tdtype = 1;
+       }
 
        ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
        if (ret)
@@ -1599,6 +1938,8 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
        return ret;
 }
 
+#define pktdma_tisci_tx_channel_config bcdma_tisci_tx_channel_config
+
 static int udma_tisci_rx_channel_config(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1621,7 +1962,7 @@ static int udma_tisci_rx_channel_config(struct udma_chan *uc)
                fetch_size = sizeof(struct cppi5_desc_hdr_t);
        }
 
-       req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS;
+       req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS;
        req_rx.nav_id = tisci_rm->tisci_dev_id;
        req_rx.index = rchan->id;
        req_rx.rx_fetch_size =  fetch_size >> 2;
@@ -1680,6 +2021,72 @@ static int udma_tisci_rx_channel_config(struct udma_chan *uc)
        return 0;
 }
 
+static int bcdma_tisci_rx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct udma_rchan *rchan = uc->rchan;
+       struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
+       int ret = 0;
+
+       req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS;
+       req_rx.nav_id = tisci_rm->tisci_dev_id;
+       req_rx.index = rchan->id;
+
+       ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx);
+       if (ret)
+               dev_err(ud->dev, "rchan%d cfg failed %d\n", rchan->id, ret);
+
+       return ret;
+}
+
+static int pktdma_tisci_rx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
+       struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 };
+       int ret = 0;
+
+       req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS;
+       req_rx.nav_id = tisci_rm->tisci_dev_id;
+       req_rx.index = uc->rchan->id;
+
+       ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx);
+       if (ret) {
+               dev_err(ud->dev, "rchan%d cfg failed %d\n", uc->rchan->id, ret);
+               return ret;
+       }
+
+       flow_req.valid_params =
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_EINFO_PRESENT_VALID |
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_PSINFO_PRESENT_VALID |
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_ERROR_HANDLING_VALID;
+
+       flow_req.nav_id = tisci_rm->tisci_dev_id;
+       flow_req.flow_index = uc->rflow->id;
+
+       if (uc->config.needs_epib)
+               flow_req.rx_einfo_present = 1;
+       else
+               flow_req.rx_einfo_present = 0;
+       if (uc->config.psd_size)
+               flow_req.rx_psinfo_present = 1;
+       else
+               flow_req.rx_psinfo_present = 0;
+       flow_req.rx_error_handling = 1;
+
+       ret = tisci_ops->rx_flow_cfg(tisci_rm->tisci, &flow_req);
+
+       if (ret)
+               dev_err(ud->dev, "flow%d config failed: %d\n", uc->rflow->id,
+                       ret);
+
+       return ret;
+}
+
 static int udma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct udma_chan *uc = to_udma_chan(chan);
@@ -1689,6 +2096,8 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
        u32 irq_udma_idx;
        int ret;
 
+       uc->dma_dev = ud->dev;
+
        if (uc->config.pkt_mode || uc->config.dir == DMA_MEM_TO_MEM) {
                uc->use_dma_pool = true;
                /* in case of MEM_TO_MEM we have maximum of two TRs */
@@ -1784,7 +2193,7 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
                                        K3_PSIL_DST_THREAD_ID_OFFSET;
 
                irq_ring = uc->rflow->r_ring;
-               irq_udma_idx = soc_data->rchan_oes_offset + uc->rchan->id;
+               irq_udma_idx = soc_data->oes.udma_rchan + uc->rchan->id;
 
                ret = udma_tisci_rx_channel_config(uc);
                break;
@@ -1884,28 +2293,391 @@ err_cleanup:
        return ret;
 }
 
-static int udma_slave_config(struct dma_chan *chan,
-                            struct dma_slave_config *cfg)
+static int bcdma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct udma_chan *uc = to_udma_chan(chan);
+       struct udma_dev *ud = to_udma_dev(chan->device);
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 irq_udma_idx, irq_ring_idx;
+       int ret;
 
-       memcpy(&uc->cfg, cfg, sizeof(uc->cfg));
+       /* Only TR mode is supported */
+       uc->config.pkt_mode = false;
 
-       return 0;
-}
+       /*
+        * Make sure that the completion is in a known state:
+        * No teardown, the channel is idle
+        */
+       reinit_completion(&uc->teardown_completed);
+       complete_all(&uc->teardown_completed);
+       uc->state = UDMA_CHAN_IS_IDLE;
 
-static struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
-                                           size_t tr_size, int tr_count,
-                                           enum dma_transfer_direction dir)
-{
-       struct udma_hwdesc *hwdesc;
-       struct cppi5_desc_hdr_t *tr_desc;
-       struct udma_desc *d;
-       u32 reload_count = 0;
-       u32 ring_id;
+       switch (uc->config.dir) {
+       case DMA_MEM_TO_MEM:
+               /* Non synchronized - mem to mem type of transfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-MEM\n", __func__,
+                       uc->id);
 
-       switch (tr_size) {
-       case 16:
+               ret = bcdma_alloc_bchan_resources(uc);
+               if (ret)
+                       return ret;
+
+               irq_ring_idx = uc->bchan->id + oes->bcdma_bchan_ring;
+               irq_udma_idx = uc->bchan->id + oes->bcdma_bchan_data;
+
+               ret = bcdma_tisci_m2m_channel_config(uc);
+               break;
+       case DMA_MEM_TO_DEV:
+               /* Slave transfer synchronized - mem to dev (TX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_tx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = ud->psil_base + uc->tchan->id;
+               uc->config.dst_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->tchan->id + oes->bcdma_tchan_ring;
+               irq_udma_idx = uc->tchan->id + oes->bcdma_tchan_data;
+
+               ret = bcdma_tisci_tx_channel_config(uc);
+               break;
+       case DMA_DEV_TO_MEM:
+               /* Slave transfer synchronized - dev to mem (RX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_rx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread = (ud->psil_base + uc->rchan->id) |
+                                       K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->rchan->id + oes->bcdma_rchan_ring;
+               irq_udma_idx = uc->rchan->id + oes->bcdma_rchan_data;
+
+               ret = bcdma_tisci_rx_channel_config(uc);
+               break;
+       default:
+               /* Can not happen */
+               dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n",
+                       __func__, uc->id, uc->config.dir);
+               return -EINVAL;
+       }
+
+       /* check if the channel configuration was successful */
+       if (ret)
+               goto err_res_free;
+
+       if (udma_is_chan_running(uc)) {
+               dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
+               udma_reset_chan(uc, false);
+               if (udma_is_chan_running(uc)) {
+                       dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+                       ret = -EBUSY;
+                       goto err_res_free;
+               }
+       }
+
+       uc->dma_dev = dmaengine_get_dma_device(chan);
+       if (uc->config.dir == DMA_MEM_TO_MEM  && !uc->config.tr_trigger_type) {
+               uc->config.hdesc_size = cppi5_trdesc_calc_size(
+                                       sizeof(struct cppi5_tr_type15_t), 2);
+
+               uc->hdesc_pool = dma_pool_create(uc->name, ud->ddev.dev,
+                                                uc->config.hdesc_size,
+                                                ud->desc_align,
+                                                0);
+               if (!uc->hdesc_pool) {
+                       dev_err(ud->ddev.dev,
+                               "Descriptor pool allocation failed\n");
+                       uc->use_dma_pool = false;
+                       return -ENOMEM;
+               }
+
+               uc->use_dma_pool = true;
+       } else if (uc->config.dir != DMA_MEM_TO_MEM) {
+               /* PSI-L pairing */
+               ret = navss_psil_pair(ud, uc->config.src_thread,
+                                     uc->config.dst_thread);
+               if (ret) {
+                       dev_err(ud->dev,
+                               "PSI-L pairing failed: 0x%04x -> 0x%04x\n",
+                               uc->config.src_thread, uc->config.dst_thread);
+                       goto err_res_free;
+               }
+
+               uc->psil_paired = true;
+       }
+
+       uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx);
+       if (uc->irq_num_ring <= 0) {
+               dev_err(ud->dev, "Failed to get ring irq (index: %u)\n",
+                       irq_ring_idx);
+               ret = -EINVAL;
+               goto err_psi_free;
+       }
+
+       ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler,
+                         IRQF_TRIGGER_HIGH, uc->name, uc);
+       if (ret) {
+               dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id);
+               goto err_irq_free;
+       }
+
+       /* Event from BCDMA (TR events) only needed for slave channels */
+       if (is_slave_direction(uc->config.dir)) {
+               uc->irq_num_udma = ti_sci_inta_msi_get_virq(ud->dev,
+                                                           irq_udma_idx);
+               if (uc->irq_num_udma <= 0) {
+                       dev_err(ud->dev, "Failed to get bcdma irq (index: %u)\n",
+                               irq_udma_idx);
+                       free_irq(uc->irq_num_ring, uc);
+                       ret = -EINVAL;
+                       goto err_irq_free;
+               }
+
+               ret = request_irq(uc->irq_num_udma, udma_udma_irq_handler, 0,
+                                 uc->name, uc);
+               if (ret) {
+                       dev_err(ud->dev, "chan%d: BCDMA irq request failed\n",
+                               uc->id);
+                       free_irq(uc->irq_num_ring, uc);
+                       goto err_irq_free;
+               }
+       } else {
+               uc->irq_num_udma = 0;
+       }
+
+       udma_reset_rings(uc);
+
+       INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work,
+                                 udma_check_tx_completion);
+       return 0;
+
+err_irq_free:
+       uc->irq_num_ring = 0;
+       uc->irq_num_udma = 0;
+err_psi_free:
+       if (uc->psil_paired)
+               navss_psil_unpair(ud, uc->config.src_thread,
+                                 uc->config.dst_thread);
+       uc->psil_paired = false;
+err_res_free:
+       bcdma_free_bchan_resources(uc);
+       udma_free_tx_resources(uc);
+       udma_free_rx_resources(uc);
+
+       udma_reset_uchan(uc);
+
+       if (uc->use_dma_pool) {
+               dma_pool_destroy(uc->hdesc_pool);
+               uc->use_dma_pool = false;
+       }
+
+       return ret;
+}
+
+static int bcdma_router_config(struct dma_chan *chan)
+{
+       struct k3_event_route_data *router_data = chan->route_data;
+       struct udma_chan *uc = to_udma_chan(chan);
+       u32 trigger_event;
+
+       if (!uc->bchan)
+               return -EINVAL;
+
+       if (uc->config.tr_trigger_type != 1 && uc->config.tr_trigger_type != 2)
+               return -EINVAL;
+
+       trigger_event = uc->ud->soc_data->bcdma_trigger_event_offset;
+       trigger_event += (uc->bchan->id * 2) + uc->config.tr_trigger_type - 1;
+
+       return router_data->set_event(router_data->priv, trigger_event);
+}
+
+static int pktdma_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct udma_chan *uc = to_udma_chan(chan);
+       struct udma_dev *ud = to_udma_dev(chan->device);
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 irq_ring_idx;
+       int ret;
+
+       /*
+        * Make sure that the completion is in a known state:
+        * No teardown, the channel is idle
+        */
+       reinit_completion(&uc->teardown_completed);
+       complete_all(&uc->teardown_completed);
+       uc->state = UDMA_CHAN_IS_IDLE;
+
+       switch (uc->config.dir) {
+       case DMA_MEM_TO_DEV:
+               /* Slave transfer synchronized - mem to dev (TX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_tx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = ud->psil_base + uc->tchan->id;
+               uc->config.dst_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->tchan->tflow_id + oes->pktdma_tchan_flow;
+
+               ret = pktdma_tisci_tx_channel_config(uc);
+               break;
+       case DMA_DEV_TO_MEM:
+               /* Slave transfer synchronized - dev to mem (RX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_rx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread = (ud->psil_base + uc->rchan->id) |
+                                       K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->rflow->id + oes->pktdma_rchan_flow;
+
+               ret = pktdma_tisci_rx_channel_config(uc);
+               break;
+       default:
+               /* Can not happen */
+               dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n",
+                       __func__, uc->id, uc->config.dir);
+               return -EINVAL;
+       }
+
+       /* check if the channel configuration was successful */
+       if (ret)
+               goto err_res_free;
+
+       if (udma_is_chan_running(uc)) {
+               dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
+               udma_reset_chan(uc, false);
+               if (udma_is_chan_running(uc)) {
+                       dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+                       ret = -EBUSY;
+                       goto err_res_free;
+               }
+       }
+
+       uc->dma_dev = dmaengine_get_dma_device(chan);
+       uc->hdesc_pool = dma_pool_create(uc->name, uc->dma_dev,
+                                        uc->config.hdesc_size, ud->desc_align,
+                                        0);
+       if (!uc->hdesc_pool) {
+               dev_err(ud->ddev.dev,
+                       "Descriptor pool allocation failed\n");
+               uc->use_dma_pool = false;
+               ret = -ENOMEM;
+               goto err_res_free;
+       }
+
+       uc->use_dma_pool = true;
+
+       /* PSI-L pairing */
+       ret = navss_psil_pair(ud, uc->config.src_thread, uc->config.dst_thread);
+       if (ret) {
+               dev_err(ud->dev, "PSI-L pairing failed: 0x%04x -> 0x%04x\n",
+                       uc->config.src_thread, uc->config.dst_thread);
+               goto err_res_free;
+       }
+
+       uc->psil_paired = true;
+
+       uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx);
+       if (uc->irq_num_ring <= 0) {
+               dev_err(ud->dev, "Failed to get ring irq (index: %u)\n",
+                       irq_ring_idx);
+               ret = -EINVAL;
+               goto err_psi_free;
+       }
+
+       ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler,
+                         IRQF_TRIGGER_HIGH, uc->name, uc);
+       if (ret) {
+               dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id);
+               goto err_irq_free;
+       }
+
+       uc->irq_num_udma = 0;
+
+       udma_reset_rings(uc);
+
+       INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work,
+                                 udma_check_tx_completion);
+
+       if (uc->tchan)
+               dev_dbg(ud->dev,
+                       "chan%d: tchan%d, tflow%d, Remote thread: 0x%04x\n",
+                       uc->id, uc->tchan->id, uc->tchan->tflow_id,
+                       uc->config.remote_thread_id);
+       else if (uc->rchan)
+               dev_dbg(ud->dev,
+                       "chan%d: rchan%d, rflow%d, Remote thread: 0x%04x\n",
+                       uc->id, uc->rchan->id, uc->rflow->id,
+                       uc->config.remote_thread_id);
+       return 0;
+
+err_irq_free:
+       uc->irq_num_ring = 0;
+err_psi_free:
+       navss_psil_unpair(ud, uc->config.src_thread, uc->config.dst_thread);
+       uc->psil_paired = false;
+err_res_free:
+       udma_free_tx_resources(uc);
+       udma_free_rx_resources(uc);
+
+       udma_reset_uchan(uc);
+
+       dma_pool_destroy(uc->hdesc_pool);
+       uc->use_dma_pool = false;
+
+       return ret;
+}
+
+static int udma_slave_config(struct dma_chan *chan,
+                            struct dma_slave_config *cfg)
+{
+       struct udma_chan *uc = to_udma_chan(chan);
+
+       memcpy(&uc->cfg, cfg, sizeof(uc->cfg));
+
+       return 0;
+}
+
+static struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
+                                           size_t tr_size, int tr_count,
+                                           enum dma_transfer_direction dir)
+{
+       struct udma_hwdesc *hwdesc;
+       struct cppi5_desc_hdr_t *tr_desc;
+       struct udma_desc *d;
+       u32 reload_count = 0;
+       u32 ring_id;
+
+       switch (tr_size) {
+       case 16:
        case 32:
        case 64:
        case 128:
@@ -2028,6 +2800,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
        size_t tr_size;
        int num_tr = 0;
        int tr_idx = 0;
+       u64 asel;
 
        /* estimate the number of TRs we will need */
        for_each_sg(sgl, sgent, sglen, i) {
@@ -2045,6 +2818,11 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
 
        d->sglen = sglen;
 
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               asel = 0;
+       else
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        tr_req = d->hwdesc[0].tr_req_base;
        for_each_sg(sgl, sgent, sglen, i) {
                dma_addr_t sg_addr = sg_dma_address(sgent);
@@ -2063,6 +2841,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
                              false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
                cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
 
+               sg_addr |= asel;
                tr_req[tr_idx].addr = sg_addr;
                tr_req[tr_idx].icnt0 = tr0_cnt0;
                tr_req[tr_idx].icnt1 = tr0_cnt1;
@@ -2092,6 +2871,205 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
        return d;
 }
 
+static struct udma_desc *
+udma_prep_slave_sg_triggered_tr(struct udma_chan *uc, struct scatterlist *sgl,
+                               unsigned int sglen,
+                               enum dma_transfer_direction dir,
+                               unsigned long tx_flags, void *context)
+{
+       struct scatterlist *sgent;
+       struct cppi5_tr_type15_t *tr_req = NULL;
+       enum dma_slave_buswidth dev_width;
+       u16 tr_cnt0, tr_cnt1;
+       dma_addr_t dev_addr;
+       struct udma_desc *d;
+       unsigned int i;
+       size_t tr_size, sg_len;
+       int num_tr = 0;
+       int tr_idx = 0;
+       u32 burst, trigger_size, port_window;
+       u64 asel;
+
+       if (dir == DMA_DEV_TO_MEM) {
+               dev_addr = uc->cfg.src_addr;
+               dev_width = uc->cfg.src_addr_width;
+               burst = uc->cfg.src_maxburst;
+               port_window = uc->cfg.src_port_window_size;
+       } else if (dir == DMA_MEM_TO_DEV) {
+               dev_addr = uc->cfg.dst_addr;
+               dev_width = uc->cfg.dst_addr_width;
+               burst = uc->cfg.dst_maxburst;
+               port_window = uc->cfg.dst_port_window_size;
+       } else {
+               dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       if (!burst)
+               burst = 1;
+
+       if (port_window) {
+               if (port_window != burst) {
+                       dev_err(uc->ud->dev,
+                               "The burst must be equal to port_window\n");
+                       return NULL;
+               }
+
+               tr_cnt0 = dev_width * port_window;
+               tr_cnt1 = 1;
+       } else {
+               tr_cnt0 = dev_width;
+               tr_cnt1 = burst;
+       }
+       trigger_size = tr_cnt0 * tr_cnt1;
+
+       /* estimate the number of TRs we will need */
+       for_each_sg(sgl, sgent, sglen, i) {
+               sg_len = sg_dma_len(sgent);
+
+               if (sg_len % trigger_size) {
+                       dev_err(uc->ud->dev,
+                               "Not aligned SG entry (%zu for %u)\n", sg_len,
+                               trigger_size);
+                       return NULL;
+               }
+
+               if (sg_len / trigger_size < SZ_64K)
+                       num_tr++;
+               else
+                       num_tr += 2;
+       }
+
+       /* Now allocate and setup the descriptor. */
+       tr_size = sizeof(struct cppi5_tr_type15_t);
+       d = udma_alloc_tr_desc(uc, tr_size, num_tr, dir);
+       if (!d)
+               return NULL;
+
+       d->sglen = sglen;
+
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA) {
+               asel = 0;
+       } else {
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+               dev_addr |= asel;
+       }
+
+       tr_req = d->hwdesc[0].tr_req_base;
+       for_each_sg(sgl, sgent, sglen, i) {
+               u16 tr0_cnt2, tr0_cnt3, tr1_cnt2;
+               dma_addr_t sg_addr = sg_dma_address(sgent);
+
+               sg_len = sg_dma_len(sgent);
+               num_tr = udma_get_tr_counters(sg_len / trigger_size, 0,
+                                             &tr0_cnt2, &tr0_cnt3, &tr1_cnt2);
+               if (num_tr < 0) {
+                       dev_err(uc->ud->dev, "size %zu is not supported\n",
+                               sg_len);
+                       udma_free_hwdesc(uc, d);
+                       kfree(d);
+                       return NULL;
+               }
+
+               cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15, false,
+                             true, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+               cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
+               cppi5_tr_set_trigger(&tr_req[tr_idx].flags,
+                                    uc->config.tr_trigger_type,
+                                    CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC, 0, 0);
+
+               sg_addr |= asel;
+               if (dir == DMA_DEV_TO_MEM) {
+                       tr_req[tr_idx].addr = dev_addr;
+                       tr_req[tr_idx].icnt0 = tr_cnt0;
+                       tr_req[tr_idx].icnt1 = tr_cnt1;
+                       tr_req[tr_idx].icnt2 = tr0_cnt2;
+                       tr_req[tr_idx].icnt3 = tr0_cnt3;
+                       tr_req[tr_idx].dim1 = (-1) * tr_cnt0;
+
+                       tr_req[tr_idx].daddr = sg_addr;
+                       tr_req[tr_idx].dicnt0 = tr_cnt0;
+                       tr_req[tr_idx].dicnt1 = tr_cnt1;
+                       tr_req[tr_idx].dicnt2 = tr0_cnt2;
+                       tr_req[tr_idx].dicnt3 = tr0_cnt3;
+                       tr_req[tr_idx].ddim1 = tr_cnt0;
+                       tr_req[tr_idx].ddim2 = trigger_size;
+                       tr_req[tr_idx].ddim3 = trigger_size * tr0_cnt2;
+               } else {
+                       tr_req[tr_idx].addr = sg_addr;
+                       tr_req[tr_idx].icnt0 = tr_cnt0;
+                       tr_req[tr_idx].icnt1 = tr_cnt1;
+                       tr_req[tr_idx].icnt2 = tr0_cnt2;
+                       tr_req[tr_idx].icnt3 = tr0_cnt3;
+                       tr_req[tr_idx].dim1 = tr_cnt0;
+                       tr_req[tr_idx].dim2 = trigger_size;
+                       tr_req[tr_idx].dim3 = trigger_size * tr0_cnt2;
+
+                       tr_req[tr_idx].daddr = dev_addr;
+                       tr_req[tr_idx].dicnt0 = tr_cnt0;
+                       tr_req[tr_idx].dicnt1 = tr_cnt1;
+                       tr_req[tr_idx].dicnt2 = tr0_cnt2;
+                       tr_req[tr_idx].dicnt3 = tr0_cnt3;
+                       tr_req[tr_idx].ddim1 = (-1) * tr_cnt0;
+               }
+
+               tr_idx++;
+
+               if (num_tr == 2) {
+                       cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15,
+                                     false, true,
+                                     CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+                       cppi5_tr_csf_set(&tr_req[tr_idx].flags,
+                                        CPPI5_TR_CSF_SUPR_EVT);
+                       cppi5_tr_set_trigger(&tr_req[tr_idx].flags,
+                                            uc->config.tr_trigger_type,
+                                            CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC,
+                                            0, 0);
+
+                       sg_addr += trigger_size * tr0_cnt2 * tr0_cnt3;
+                       if (dir == DMA_DEV_TO_MEM) {
+                               tr_req[tr_idx].addr = dev_addr;
+                               tr_req[tr_idx].icnt0 = tr_cnt0;
+                               tr_req[tr_idx].icnt1 = tr_cnt1;
+                               tr_req[tr_idx].icnt2 = tr1_cnt2;
+                               tr_req[tr_idx].icnt3 = 1;
+                               tr_req[tr_idx].dim1 = (-1) * tr_cnt0;
+
+                               tr_req[tr_idx].daddr = sg_addr;
+                               tr_req[tr_idx].dicnt0 = tr_cnt0;
+                               tr_req[tr_idx].dicnt1 = tr_cnt1;
+                               tr_req[tr_idx].dicnt2 = tr1_cnt2;
+                               tr_req[tr_idx].dicnt3 = 1;
+                               tr_req[tr_idx].ddim1 = tr_cnt0;
+                               tr_req[tr_idx].ddim2 = trigger_size;
+                       } else {
+                               tr_req[tr_idx].addr = sg_addr;
+                               tr_req[tr_idx].icnt0 = tr_cnt0;
+                               tr_req[tr_idx].icnt1 = tr_cnt1;
+                               tr_req[tr_idx].icnt2 = tr1_cnt2;
+                               tr_req[tr_idx].icnt3 = 1;
+                               tr_req[tr_idx].dim1 = tr_cnt0;
+                               tr_req[tr_idx].dim2 = trigger_size;
+
+                               tr_req[tr_idx].daddr = dev_addr;
+                               tr_req[tr_idx].dicnt0 = tr_cnt0;
+                               tr_req[tr_idx].dicnt1 = tr_cnt1;
+                               tr_req[tr_idx].dicnt2 = tr1_cnt2;
+                               tr_req[tr_idx].dicnt3 = 1;
+                               tr_req[tr_idx].ddim1 = (-1) * tr_cnt0;
+                       }
+                       tr_idx++;
+               }
+
+               d->residue += sg_len;
+       }
+
+       cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
+                        CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
+
+       return d;
+}
+
 static int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
                                   enum dma_slave_buswidth dev_width,
                                   u16 elcnt)
@@ -2156,6 +3134,7 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
        struct udma_desc *d;
        u32 ring_id;
        unsigned int i;
+       u64 asel;
 
        d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
        if (!d)
@@ -2169,6 +3148,11 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
        else
                ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
 
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               asel = 0;
+       else
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        for_each_sg(sgl, sgent, sglen, i) {
                struct udma_hwdesc *hwdesc = &d->hwdesc[i];
                dma_addr_t sg_addr = sg_dma_address(sgent);
@@ -2203,14 +3187,16 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
                }
 
                /* attach the sg buffer to the descriptor */
+               sg_addr |= asel;
                cppi5_hdesc_attach_buf(desc, sg_addr, sg_len, sg_addr, sg_len);
 
                /* Attach link as host buffer descriptor */
                if (h_desc)
                        cppi5_hdesc_link_hbdesc(h_desc,
-                                               hwdesc->cppi5_desc_paddr);
+                                               hwdesc->cppi5_desc_paddr | asel);
 
-               if (dir == DMA_MEM_TO_DEV)
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
+                   dir == DMA_MEM_TO_DEV)
                        h_desc = desc;
        }
 
@@ -2333,7 +3319,8 @@ udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        struct udma_desc *d;
        u32 burst;
 
-       if (dir != uc->config.dir) {
+       if (dir != uc->config.dir &&
+           (uc->config.dir == DMA_MEM_TO_MEM && !uc->config.tr_trigger_type)) {
                dev_err(chan->device->dev,
                        "%s: chan%d is for %s, not supporting %s\n",
                        __func__, uc->id,
@@ -2359,9 +3346,12 @@ udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (uc->config.pkt_mode)
                d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
                                           context);
-       else
+       else if (is_slave_direction(uc->config.dir))
                d = udma_prep_slave_sg_tr(uc, sgl, sglen, dir, tx_flags,
                                          context);
+       else
+               d = udma_prep_slave_sg_triggered_tr(uc, sgl, sglen, dir,
+                                                   tx_flags, context);
 
        if (!d)
                return NULL;
@@ -2415,7 +3405,12 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
                return NULL;
 
        tr_req = d->hwdesc[0].tr_req_base;
-       period_addr = buf_addr;
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               period_addr = buf_addr;
+       else
+               period_addr = buf_addr |
+                       ((u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT);
+
        for (i = 0; i < periods; i++) {
                int tr_idx = i * num_tr;
 
@@ -2480,6 +3475,9 @@ udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
        else
                ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
 
+       if (uc->ud->match_data->type != DMA_TYPE_UDMA)
+               buf_addr |= (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        for (i = 0; i < periods; i++) {
                struct udma_hwdesc *hwdesc = &d->hwdesc[i];
                dma_addr_t period_addr = buf_addr + (period_len * i);
@@ -2621,6 +3619,11 @@ udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
        d->tr_idx = 0;
        d->residue = len;
 
+       if (uc->ud->match_data->type != DMA_TYPE_UDMA) {
+               src |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT;
+               dest |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT;
+       }
+
        tr_req = d->hwdesc[0].tr_req_base;
 
        cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
@@ -2978,6 +3981,7 @@ static void udma_free_chan_resources(struct dma_chan *chan)
        vchan_free_chan_resources(&uc->vc);
        tasklet_kill(&uc->vc.task);
 
+       bcdma_free_bchan_resources(uc);
        udma_free_tx_resources(uc);
        udma_free_rx_resources(uc);
        udma_reset_uchan(uc);
@@ -2989,10 +3993,14 @@ static void udma_free_chan_resources(struct dma_chan *chan)
 }
 
 static struct platform_driver udma_driver;
+static struct platform_driver bcdma_driver;
+static struct platform_driver pktdma_driver;
 
 struct udma_filter_param {
        int remote_thread_id;
        u32 atype;
+       u32 asel;
+       u32 tr_trigger_type;
 };
 
 static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
@@ -3003,7 +4011,9 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
        struct udma_chan *uc;
        struct udma_dev *ud;
 
-       if (chan->device->dev->driver != &udma_driver.driver)
+       if (chan->device->dev->driver != &udma_driver.driver &&
+           chan->device->dev->driver != &bcdma_driver.driver &&
+           chan->device->dev->driver != &pktdma_driver.driver)
                return false;
 
        uc = to_udma_chan(chan);
@@ -3017,13 +4027,25 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                return false;
        }
 
+       if (filter_param->asel > 15) {
+               dev_err(ud->dev, "Invalid channel asel: %u\n",
+                       filter_param->asel);
+               return false;
+       }
+
        ucc->remote_thread_id = filter_param->remote_thread_id;
        ucc->atype = filter_param->atype;
+       ucc->asel = filter_param->asel;
+       ucc->tr_trigger_type = filter_param->tr_trigger_type;
 
-       if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET)
+       if (ucc->tr_trigger_type) {
+               ucc->dir = DMA_MEM_TO_MEM;
+               goto triggered_bchan;
+       } else if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET) {
                ucc->dir = DMA_MEM_TO_DEV;
-       else
+       } else {
                ucc->dir = DMA_DEV_TO_MEM;
+       }
 
        ep_config = psil_get_ep_config(ucc->remote_thread_id);
        if (IS_ERR(ep_config)) {
@@ -3032,6 +4054,19 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                ucc->dir = DMA_MEM_TO_MEM;
                ucc->remote_thread_id = -1;
                ucc->atype = 0;
+               ucc->asel = 0;
+               return false;
+       }
+
+       if (ud->match_data->type == DMA_TYPE_BCDMA &&
+           ep_config->pkt_mode) {
+               dev_err(ud->dev,
+                       "Only TR mode is supported (psi-l thread 0x%04x)\n",
+                       ucc->remote_thread_id);
+               ucc->dir = DMA_MEM_TO_MEM;
+               ucc->remote_thread_id = -1;
+               ucc->atype = 0;
+               ucc->asel = 0;
                return false;
        }
 
@@ -3040,6 +4075,15 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
        ucc->notdpkt = ep_config->notdpkt;
        ucc->ep_type = ep_config->ep_type;
 
+       if (ud->match_data->type == DMA_TYPE_PKTDMA &&
+           ep_config->mapped_channel_id >= 0) {
+               ucc->mapped_channel_id = ep_config->mapped_channel_id;
+               ucc->default_flow_id = ep_config->default_flow_id;
+       } else {
+               ucc->mapped_channel_id = -1;
+               ucc->default_flow_id = -1;
+       }
+
        if (ucc->ep_type != PSIL_EP_NATIVE) {
                const struct udma_match_data *match_data = ud->match_data;
 
@@ -3063,6 +4107,13 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                ucc->remote_thread_id, dmaengine_get_direction_text(ucc->dir));
 
        return true;
+
+triggered_bchan:
+       dev_dbg(ud->dev, "chan%d: triggered channel (type: %u)\n", uc->id,
+               ucc->tr_trigger_type);
+
+       return true;
+
 }
 
 static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
@@ -3073,14 +4124,33 @@ static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
        struct udma_filter_param filter_param;
        struct dma_chan *chan;
 
-       if (dma_spec->args_count != 1 && dma_spec->args_count != 2)
-               return NULL;
+       if (ud->match_data->type == DMA_TYPE_BCDMA) {
+               if (dma_spec->args_count != 3)
+                       return NULL;
 
-       filter_param.remote_thread_id = dma_spec->args[0];
-       if (dma_spec->args_count == 2)
-               filter_param.atype = dma_spec->args[1];
-       else
+               filter_param.tr_trigger_type = dma_spec->args[0];
+               filter_param.remote_thread_id = dma_spec->args[1];
+               filter_param.asel = dma_spec->args[2];
                filter_param.atype = 0;
+       } else {
+               if (dma_spec->args_count != 1 && dma_spec->args_count != 2)
+                       return NULL;
+
+               filter_param.remote_thread_id = dma_spec->args[0];
+               filter_param.tr_trigger_type = 0;
+               if (dma_spec->args_count == 2) {
+                       if (ud->match_data->type == DMA_TYPE_UDMA) {
+                               filter_param.atype = dma_spec->args[1];
+                               filter_param.asel = 0;
+                       } else {
+                               filter_param.atype = 0;
+                               filter_param.asel = dma_spec->args[1];
+                       }
+               } else {
+                       filter_param.atype = 0;
+                       filter_param.asel = 0;
+               }
+       }
 
        chan = __dma_request_channel(&mask, udma_dma_filter_fn, &filter_param,
                                     ofdma->of_node);
@@ -3093,28 +4163,48 @@ static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
 }
 
 static struct udma_match_data am654_main_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x1000,
        .enable_memcpy_support = true,
        .statictr_z_mask = GENMASK(11, 0),
 };
 
 static struct udma_match_data am654_mcu_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x6000,
        .enable_memcpy_support = false,
        .statictr_z_mask = GENMASK(11, 0),
 };
 
 static struct udma_match_data j721e_main_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x1000,
        .enable_memcpy_support = true,
-       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST,
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
        .statictr_z_mask = GENMASK(23, 0),
 };
 
 static struct udma_match_data j721e_mcu_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x6000,
        .enable_memcpy_support = false, /* MEM_TO_MEM is slow via MCU UDMA */
-       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST,
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
+       .statictr_z_mask = GENMASK(23, 0),
+};
+
+static struct udma_match_data am64_bcdma_data = {
+       .type = DMA_TYPE_BCDMA,
+       .psil_base = 0x2000, /* for tchan and rchan, not applicable to bchan */
+       .enable_memcpy_support = true, /* Supported via bchan */
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
+       .statictr_z_mask = GENMASK(23, 0),
+};
+
+static struct udma_match_data am64_pktdma_data = {
+       .type = DMA_TYPE_PKTDMA,
+       .psil_base = 0x1000,
+       .enable_memcpy_support = false, /* PKTDMA does not support MEM_TO_MEM */
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
        .statictr_z_mask = GENMASK(23, 0),
 };
 
@@ -3136,30 +4226,105 @@ static const struct of_device_id udma_of_match[] = {
        { /* Sentinel */ },
 };
 
+static const struct of_device_id bcdma_of_match[] = {
+       {
+               .compatible = "ti,am64-dmss-bcdma",
+               .data = &am64_bcdma_data,
+       },
+       { /* Sentinel */ },
+};
+
+static const struct of_device_id pktdma_of_match[] = {
+       {
+               .compatible = "ti,am64-dmss-pktdma",
+               .data = &am64_pktdma_data,
+       },
+       { /* Sentinel */ },
+};
+
 static struct udma_soc_data am654_soc_data = {
-       .rchan_oes_offset = 0x200,
+       .oes = {
+               .udma_rchan = 0x200,
+       },
 };
 
 static struct udma_soc_data j721e_soc_data = {
-       .rchan_oes_offset = 0x400,
+       .oes = {
+               .udma_rchan = 0x400,
+       },
 };
 
 static struct udma_soc_data j7200_soc_data = {
-       .rchan_oes_offset = 0x80,
+       .oes = {
+               .udma_rchan = 0x80,
+       },
+};
+
+static struct udma_soc_data am64_soc_data = {
+       .oes = {
+               .bcdma_bchan_data = 0x2200,
+               .bcdma_bchan_ring = 0x2400,
+               .bcdma_tchan_data = 0x2800,
+               .bcdma_tchan_ring = 0x2a00,
+               .bcdma_rchan_data = 0x2e00,
+               .bcdma_rchan_ring = 0x3000,
+               .pktdma_tchan_flow = 0x1200,
+               .pktdma_rchan_flow = 0x1600,
+       },
+       .bcdma_trigger_event_offset = 0xc400,
 };
 
 static const struct soc_device_attribute k3_soc_devices[] = {
        { .family = "AM65X", .data = &am654_soc_data },
        { .family = "J721E", .data = &j721e_soc_data },
        { .family = "J7200", .data = &j7200_soc_data },
+       { .family = "AM64X", .data = &am64_soc_data },
        { /* sentinel */ }
 };
 
 static int udma_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
 {
+       u32 cap2, cap3, cap4;
        int i;
 
-       for (i = 0; i < MMR_LAST; i++) {
+       ud->mmrs[MMR_GCFG] = devm_platform_ioremap_resource_byname(pdev, mmr_names[MMR_GCFG]);
+       if (IS_ERR(ud->mmrs[MMR_GCFG]))
+               return PTR_ERR(ud->mmrs[MMR_GCFG]);
+
+       cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28);
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
+               ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
+               ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2);
+               ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
+               break;
+       case DMA_TYPE_BCDMA:
+               ud->bchan_cnt = BCDMA_CAP2_BCHAN_CNT(cap2);
+               ud->tchan_cnt = BCDMA_CAP2_TCHAN_CNT(cap2);
+               ud->rchan_cnt = BCDMA_CAP2_RCHAN_CNT(cap2);
+               break;
+       case DMA_TYPE_PKTDMA:
+               cap4 = udma_read(ud->mmrs[MMR_GCFG], 0x30);
+               ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
+               ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
+               ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
+               ud->tflow_cnt = PKTDMA_CAP4_TFLOW_CNT(cap4);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 1; i < MMR_LAST; i++) {
+               if (i == MMR_BCHANRT && ud->bchan_cnt == 0)
+                       continue;
+               if (i == MMR_TCHANRT && ud->tchan_cnt == 0)
+                       continue;
+               if (i == MMR_RCHANRT && ud->rchan_cnt == 0)
+                       continue;
+
                ud->mmrs[i] = devm_platform_ioremap_resource_byname(pdev, mmr_names[i]);
                if (IS_ERR(ud->mmrs[i]))
                        return PTR_ERR(ud->mmrs[i]);
@@ -3168,48 +4333,58 @@ static int udma_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
        return 0;
 }
 
+static void udma_mark_resource_ranges(struct udma_dev *ud, unsigned long *map,
+                                     struct ti_sci_resource_desc *rm_desc,
+                                     char *name)
+{
+       bitmap_clear(map, rm_desc->start, rm_desc->num);
+       bitmap_clear(map, rm_desc->start_sec, rm_desc->num_sec);
+       dev_dbg(ud->dev, "ti_sci resource range for %s: %d:%d | %d:%d\n", name,
+               rm_desc->start, rm_desc->num, rm_desc->start_sec,
+               rm_desc->num_sec);
+}
+
+static const char * const range_names[] = {
+       [RM_RANGE_BCHAN] = "ti,sci-rm-range-bchan",
+       [RM_RANGE_TCHAN] = "ti,sci-rm-range-tchan",
+       [RM_RANGE_RCHAN] = "ti,sci-rm-range-rchan",
+       [RM_RANGE_RFLOW] = "ti,sci-rm-range-rflow",
+       [RM_RANGE_TFLOW] = "ti,sci-rm-range-tflow",
+};
+
 static int udma_setup_resources(struct udma_dev *ud)
 {
+       int ret, i, j;
        struct device *dev = ud->dev;
-       int ch_count, ret, i, j;
-       u32 cap2, cap3;
-       struct ti_sci_resource_desc *rm_desc;
        struct ti_sci_resource *rm_res, irq_res;
        struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
-       static const char * const range_names[] = { "ti,sci-rm-range-tchan",
-                                                   "ti,sci-rm-range-rchan",
-                                                   "ti,sci-rm-range-rflow" };
-
-       cap2 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(2));
-       cap3 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(3));
-
-       ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
-       ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
-       ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2);
-       ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
-       ch_count  = ud->tchan_cnt + ud->rchan_cnt;
+       u32 cap3;
 
        /* Set up the throughput level start indexes */
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
        if (of_device_is_compatible(dev->of_node,
                                    "ti,am654-navss-main-udmap")) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = 8;
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = 8;
        } else if (of_device_is_compatible(dev->of_node,
                                           "ti,am654-navss-mcu-udmap")) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = 2;
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = 2;
        } else if (UDMA_CAP3_UCHAN_CNT(cap3)) {
-               ud->tpl_levels = 3;
-               ud->tpl_start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
-               ud->tpl_start_idx[0] = ud->tpl_start_idx[1] +
-                                      UDMA_CAP3_HCHAN_CNT(cap3);
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
        } else if (UDMA_CAP3_HCHAN_CNT(cap3)) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
        } else {
-               ud->tpl_levels = 1;
+               ud->tchan_tpl.levels = 1;
        }
 
+       ud->rchan_tpl.levels = ud->tchan_tpl.levels;
+       ud->rchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
+       ud->rchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
+
        ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
                                           sizeof(unsigned long), GFP_KERNEL);
        ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
@@ -3247,11 +4422,15 @@ static int udma_setup_resources(struct udma_dev *ud)
        bitmap_set(ud->rflow_gp_map, 0, ud->rflow_cnt);
 
        /* Get resource ranges from tisci */
-       for (i = 0; i < RM_RANGE_LAST; i++)
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_BCHAN || i == RM_RANGE_TFLOW)
+                       continue;
+
                tisci_rm->rm_ranges[i] =
                        devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
                                                    tisci_rm->tisci_dev_id,
                                                    (char *)range_names[i]);
+       }
 
        /* tchan ranges */
        rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
@@ -3259,13 +4438,9 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_zero(ud->tchan_map, ud->tchan_cnt);
        } else {
                bitmap_fill(ud->tchan_map, ud->tchan_cnt);
-               for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->tchan_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: tchan: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
-               }
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                 &rm_res->desc[i], "tchan");
        }
        irq_res.sets = rm_res->sets;
 
@@ -3275,13 +4450,9 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_zero(ud->rchan_map, ud->rchan_cnt);
        } else {
                bitmap_fill(ud->rchan_map, ud->rchan_cnt);
-               for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->rchan_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: rchan: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
-               }
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                 &rm_res->desc[i], "rchan");
        }
 
        irq_res.sets += rm_res->sets;
@@ -3290,12 +4461,21 @@ static int udma_setup_resources(struct udma_dev *ud)
        for (i = 0; i < rm_res->sets; i++) {
                irq_res.desc[i].start = rm_res->desc[i].start;
                irq_res.desc[i].num = rm_res->desc[i].num;
+               irq_res.desc[i].start_sec = rm_res->desc[i].start_sec;
+               irq_res.desc[i].num_sec = rm_res->desc[i].num_sec;
        }
        rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
        for (j = 0; j < rm_res->sets; j++, i++) {
-               irq_res.desc[i].start = rm_res->desc[j].start +
-                                       ud->soc_data->rchan_oes_offset;
-               irq_res.desc[i].num = rm_res->desc[j].num;
+               if (rm_res->desc[j].num) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                       ud->soc_data->oes.udma_rchan;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+               }
+               if (rm_res->desc[j].num_sec) {
+                       irq_res.desc[i].start_sec = rm_res->desc[j].start_sec +
+                                       ud->soc_data->oes.udma_rchan;
+                       irq_res.desc[i].num_sec = rm_res->desc[j].num_sec;
+               }
        }
        ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
        kfree(irq_res.desc);
@@ -3311,15 +4491,344 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_clear(ud->rflow_gp_map, ud->rchan_cnt,
                             ud->rflow_cnt - ud->rchan_cnt);
        } else {
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rflow_gp_map,
+                                                 &rm_res->desc[i], "gp-rflow");
+       }
+
+       return 0;
+}
+
+static int bcdma_setup_resources(struct udma_dev *ud)
+{
+       int ret, i, j;
+       struct device *dev = ud->dev;
+       struct ti_sci_resource *rm_res, irq_res;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 cap;
+
+       /* Set up the throughput level start indexes */
+       cap = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+       if (BCDMA_CAP3_UBCHAN_CNT(cap)) {
+               ud->bchan_tpl.levels = 3;
+               ud->bchan_tpl.start_idx[1] = BCDMA_CAP3_UBCHAN_CNT(cap);
+               ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap);
+       } else if (BCDMA_CAP3_HBCHAN_CNT(cap)) {
+               ud->bchan_tpl.levels = 2;
+               ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap);
+       } else {
+               ud->bchan_tpl.levels = 1;
+       }
+
+       cap = udma_read(ud->mmrs[MMR_GCFG], 0x30);
+       if (BCDMA_CAP4_URCHAN_CNT(cap)) {
+               ud->rchan_tpl.levels = 3;
+               ud->rchan_tpl.start_idx[1] = BCDMA_CAP4_URCHAN_CNT(cap);
+               ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap);
+       } else if (BCDMA_CAP4_HRCHAN_CNT(cap)) {
+               ud->rchan_tpl.levels = 2;
+               ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap);
+       } else {
+               ud->rchan_tpl.levels = 1;
+       }
+
+       if (BCDMA_CAP4_UTCHAN_CNT(cap)) {
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = BCDMA_CAP4_UTCHAN_CNT(cap);
+               ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap);
+       } else if (BCDMA_CAP4_HTCHAN_CNT(cap)) {
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap);
+       } else {
+               ud->tchan_tpl.levels = 1;
+       }
+
+       ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->bchans = devm_kcalloc(dev, ud->bchan_cnt, sizeof(*ud->bchans),
+                                 GFP_KERNEL);
+       ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
+                                 GFP_KERNEL);
+       ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
+                                 GFP_KERNEL);
+       /* BCDMA do not really have flows, but the driver expect it */
+       ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                       sizeof(unsigned long),
+                                       GFP_KERNEL);
+       ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows),
+                                 GFP_KERNEL);
+
+       if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map ||
+           !ud->rflow_in_use || !ud->bchans || !ud->tchans || !ud->rchans ||
+           !ud->rflows)
+               return -ENOMEM;
+
+       /* Get resource ranges from tisci */
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_RFLOW || i == RM_RANGE_TFLOW)
+                       continue;
+               if (i == RM_RANGE_BCHAN && ud->bchan_cnt == 0)
+                       continue;
+               if (i == RM_RANGE_TCHAN && ud->tchan_cnt == 0)
+                       continue;
+               if (i == RM_RANGE_RCHAN && ud->rchan_cnt == 0)
+                       continue;
+
+               tisci_rm->rm_ranges[i] =
+                       devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
+                                                   tisci_rm->tisci_dev_id,
+                                                   (char *)range_names[i]);
+       }
+
+       irq_res.sets = 0;
+
+       /* bchan ranges */
+       if (ud->bchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->bchan_map, ud->bchan_cnt);
+               } else {
+                       bitmap_fill(ud->bchan_map, ud->bchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->bchan_map,
+                                                         &rm_res->desc[i],
+                                                         "bchan");
+               }
+               irq_res.sets += rm_res->sets;
+       }
+
+       /* tchan ranges */
+       if (ud->tchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->tchan_map, ud->tchan_cnt);
+               } else {
+                       bitmap_fill(ud->tchan_map, ud->tchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                         &rm_res->desc[i],
+                                                         "tchan");
+               }
+               irq_res.sets += rm_res->sets * 2;
+       }
+
+       /* rchan ranges */
+       if (ud->rchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+               } else {
+                       bitmap_fill(ud->rchan_map, ud->rchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                         &rm_res->desc[i],
+                                                         "rchan");
+               }
+               irq_res.sets += rm_res->sets * 2;
+       }
+
+       irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL);
+       if (ud->bchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN];
                for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->rflow_gp_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: rflow: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
+                       irq_res.desc[i].start = rm_res->desc[i].start +
+                                               oes->bcdma_bchan_ring;
+                       irq_res.desc[i].num = rm_res->desc[i].num;
+               }
+       }
+       if (ud->tchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+               for (j = 0; j < rm_res->sets; j++, i += 2) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                               oes->bcdma_tchan_data;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+
+                       irq_res.desc[i + 1].start = rm_res->desc[j].start +
+                                               oes->bcdma_tchan_ring;
+                       irq_res.desc[i + 1].num = rm_res->desc[j].num;
                }
        }
+       if (ud->rchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+               for (j = 0; j < rm_res->sets; j++, i += 2) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                               oes->bcdma_rchan_data;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+
+                       irq_res.desc[i + 1].start = rm_res->desc[j].start +
+                                               oes->bcdma_rchan_ring;
+                       irq_res.desc[i + 1].num = rm_res->desc[j].num;
+               }
+       }
+
+       ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
+       kfree(irq_res.desc);
+       if (ret) {
+               dev_err(ud->dev, "Failed to allocate MSI interrupts\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pktdma_setup_resources(struct udma_dev *ud)
+{
+       int ret, i, j;
+       struct device *dev = ud->dev;
+       struct ti_sci_resource *rm_res, irq_res;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 cap3;
+
+       /* Set up the throughput level start indexes */
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+       if (UDMA_CAP3_UCHAN_CNT(cap3)) {
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+       } else if (UDMA_CAP3_HCHAN_CNT(cap3)) {
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+       } else {
+               ud->tchan_tpl.levels = 1;
+       }
+
+       ud->tchan_tpl.levels = ud->tchan_tpl.levels;
+       ud->tchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
+       ud->tchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
+
+       ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
+                                 GFP_KERNEL);
+       ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
+                                 GFP_KERNEL);
+       ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rflow_cnt),
+                                       sizeof(unsigned long),
+                                       GFP_KERNEL);
+       ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows),
+                                 GFP_KERNEL);
+       ud->tflow_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tflow_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+
+       if (!ud->tchan_map || !ud->rchan_map || !ud->tflow_map || !ud->tchans ||
+           !ud->rchans || !ud->rflows || !ud->rflow_in_use)
+               return -ENOMEM;
+
+       /* Get resource ranges from tisci */
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_BCHAN)
+                       continue;
+
+               tisci_rm->rm_ranges[i] =
+                       devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
+                                                   tisci_rm->tisci_dev_id,
+                                                   (char *)range_names[i]);
+       }
+
+       /* tchan ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+       if (IS_ERR(rm_res)) {
+               bitmap_zero(ud->tchan_map, ud->tchan_cnt);
+       } else {
+               bitmap_fill(ud->tchan_map, ud->tchan_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                 &rm_res->desc[i], "tchan");
+       }
+
+       /* rchan ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+       if (IS_ERR(rm_res)) {
+               bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+       } else {
+               bitmap_fill(ud->rchan_map, ud->rchan_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                 &rm_res->desc[i], "rchan");
+       }
+
+       /* rflow ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW];
+       if (IS_ERR(rm_res)) {
+               /* all rflows are assigned exclusively to Linux */
+               bitmap_zero(ud->rflow_in_use, ud->rflow_cnt);
+       } else {
+               bitmap_fill(ud->rflow_in_use, ud->rflow_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rflow_in_use,
+                                                 &rm_res->desc[i], "rflow");
+       }
+       irq_res.sets = rm_res->sets;
+
+       /* tflow ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW];
+       if (IS_ERR(rm_res)) {
+               /* all tflows are assigned exclusively to Linux */
+               bitmap_zero(ud->tflow_map, ud->tflow_cnt);
+       } else {
+               bitmap_fill(ud->tflow_map, ud->tflow_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tflow_map,
+                                                 &rm_res->desc[i], "tflow");
+       }
+       irq_res.sets += rm_res->sets;
+
+       irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL);
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW];
+       for (i = 0; i < rm_res->sets; i++) {
+               irq_res.desc[i].start = rm_res->desc[i].start +
+                                       oes->pktdma_tchan_flow;
+               irq_res.desc[i].num = rm_res->desc[i].num;
+       }
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW];
+       for (j = 0; j < rm_res->sets; j++, i++) {
+               irq_res.desc[i].start = rm_res->desc[j].start +
+                                       oes->pktdma_rchan_flow;
+               irq_res.desc[i].num = rm_res->desc[j].num;
+       }
+       ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
+       kfree(irq_res.desc);
+       if (ret) {
+               dev_err(ud->dev, "Failed to allocate MSI interrupts\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int setup_resources(struct udma_dev *ud)
+{
+       struct device *dev = ud->dev;
+       int ch_count, ret;
+
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ret = udma_setup_resources(ud);
+               break;
+       case DMA_TYPE_BCDMA:
+               ret = bcdma_setup_resources(ud);
+               break;
+       case DMA_TYPE_PKTDMA:
+               ret = pktdma_setup_resources(ud);
+               break;
+       default:
+               return -EINVAL;
+       }
 
+       if (ret)
+               return ret;
+
+       ch_count  = ud->bchan_cnt + ud->tchan_cnt + ud->rchan_cnt;
+       if (ud->bchan_cnt)
+               ch_count -= bitmap_weight(ud->bchan_map, ud->bchan_cnt);
        ch_count -= bitmap_weight(ud->tchan_map, ud->tchan_cnt);
        ch_count -= bitmap_weight(ud->rchan_map, ud->rchan_cnt);
        if (!ch_count)
@@ -3330,12 +4839,40 @@ static int udma_setup_resources(struct udma_dev *ud)
        if (!ud->channels)
                return -ENOMEM;
 
-       dev_info(dev, "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n",
-                ch_count,
-                ud->tchan_cnt - bitmap_weight(ud->tchan_map, ud->tchan_cnt),
-                ud->rchan_cnt - bitmap_weight(ud->rchan_map, ud->rchan_cnt),
-                ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map,
-                                              ud->rflow_cnt));
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               dev_info(dev,
+                        "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n",
+                        ch_count,
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt),
+                        ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map,
+                                                      ud->rflow_cnt));
+               break;
+       case DMA_TYPE_BCDMA:
+               dev_info(dev,
+                        "Channels: %d (bchan: %u, tchan: %u, rchan: %u)\n",
+                        ch_count,
+                        ud->bchan_cnt - bitmap_weight(ud->bchan_map,
+                                                      ud->bchan_cnt),
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt));
+               break;
+       case DMA_TYPE_PKTDMA:
+               dev_info(dev,
+                        "Channels: %d (tchan: %u, rchan: %u)\n",
+                        ch_count,
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt));
+       default:
+               break;
+       }
 
        return ch_count;
 }
@@ -3444,20 +4981,33 @@ static void udma_dbg_summary_show_chan(struct seq_file *s,
 
        seq_printf(s, " %-13s| %s", dma_chan_name(chan),
                   chan->dbg_client_name ?: "in-use");
-       seq_printf(s, " (%s, ", dmaengine_get_direction_text(uc->config.dir));
+       if (ucc->tr_trigger_type)
+               seq_puts(s, " (triggered, ");
+       else
+               seq_printf(s, " (%s, ",
+                          dmaengine_get_direction_text(uc->config.dir));
 
        switch (uc->config.dir) {
        case DMA_MEM_TO_MEM:
+               if (uc->ud->match_data->type == DMA_TYPE_BCDMA) {
+                       seq_printf(s, "bchan%d)\n", uc->bchan->id);
+                       return;
+               }
+
                seq_printf(s, "chan%d pair [0x%04x -> 0x%04x], ", uc->tchan->id,
                           ucc->src_thread, ucc->dst_thread);
                break;
        case DMA_DEV_TO_MEM:
                seq_printf(s, "rchan%d [0x%04x -> 0x%04x], ", uc->rchan->id,
                           ucc->src_thread, ucc->dst_thread);
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA)
+                       seq_printf(s, "rflow%d, ", uc->rflow->id);
                break;
        case DMA_MEM_TO_DEV:
                seq_printf(s, "tchan%d [0x%04x -> 0x%04x], ", uc->tchan->id,
                           ucc->src_thread, ucc->dst_thread);
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA)
+                       seq_printf(s, "tflow%d, ", uc->tchan->tflow_id);
                break;
        default:
                seq_printf(s, ")\n");
@@ -3519,6 +5069,25 @@ static int udma_probe(struct platform_device *pdev)
        if (!ud)
                return -ENOMEM;
 
+       match = of_match_node(udma_of_match, dev->of_node);
+       if (!match)
+               match = of_match_node(bcdma_of_match, dev->of_node);
+       if (!match) {
+               match = of_match_node(pktdma_of_match, dev->of_node);
+               if (!match) {
+                       dev_err(dev, "No compatible match found\n");
+                       return -ENODEV;
+               }
+       }
+       ud->match_data = match->data;
+
+       soc = soc_device_match(k3_soc_devices);
+       if (!soc) {
+               dev_err(dev, "No compatible SoC found\n");
+               return -ENODEV;
+       }
+       ud->soc_data = soc->data;
+
        ret = udma_get_mmrs(pdev, ud);
        if (ret)
                return ret;
@@ -3542,16 +5111,44 @@ static int udma_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = of_property_read_u32(dev->of_node, "ti,udma-atype", &ud->atype);
-       if (!ret && ud->atype > 2) {
-               dev_err(dev, "Invalid atype: %u\n", ud->atype);
-               return -EINVAL;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ret = of_property_read_u32(dev->of_node, "ti,udma-atype",
+                                          &ud->atype);
+               if (!ret && ud->atype > 2) {
+                       dev_err(dev, "Invalid atype: %u\n", ud->atype);
+                       return -EINVAL;
+               }
+       } else {
+               ret = of_property_read_u32(dev->of_node, "ti,asel",
+                                          &ud->asel);
+               if (!ret && ud->asel > 15) {
+                       dev_err(dev, "Invalid asel: %u\n", ud->asel);
+                       return -EINVAL;
+               }
        }
 
        ud->tisci_rm.tisci_udmap_ops = &ud->tisci_rm.tisci->ops.rm_udmap_ops;
        ud->tisci_rm.tisci_psil_ops = &ud->tisci_rm.tisci->ops.rm_psil_ops;
 
-       ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc");
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc");
+       } else {
+               struct k3_ringacc_init_data ring_init_data;
+
+               ring_init_data.tisci = ud->tisci_rm.tisci;
+               ring_init_data.tisci_dev_id = ud->tisci_rm.tisci_dev_id;
+               if (ud->match_data->type == DMA_TYPE_BCDMA) {
+                       ring_init_data.num_rings = ud->bchan_cnt +
+                                                  ud->tchan_cnt +
+                                                  ud->rchan_cnt;
+               } else {
+                       ring_init_data.num_rings = ud->rflow_cnt +
+                                                  ud->tflow_cnt;
+               }
+
+               ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data);
+       }
+
        if (IS_ERR(ud->ringacc))
                return PTR_ERR(ud->ringacc);
 
@@ -3562,27 +5159,15 @@ static int udma_probe(struct platform_device *pdev)
                return -EPROBE_DEFER;
        }
 
-       match = of_match_node(udma_of_match, dev->of_node);
-       if (!match) {
-               dev_err(dev, "No compatible match found\n");
-               return -ENODEV;
-       }
-       ud->match_data = match->data;
-
-       soc = soc_device_match(k3_soc_devices);
-       if (!soc) {
-               dev_err(dev, "No compatible SoC found\n");
-               return -ENODEV;
-       }
-       ud->soc_data = soc->data;
-
        dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask);
-       dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+       /* cyclic operation is not supported via PKTDMA */
+       if (ud->match_data->type != DMA_TYPE_PKTDMA) {
+               dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+               ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
+       }
 
-       ud->ddev.device_alloc_chan_resources = udma_alloc_chan_resources;
        ud->ddev.device_config = udma_slave_config;
        ud->ddev.device_prep_slave_sg = udma_prep_slave_sg;
-       ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
        ud->ddev.device_issue_pending = udma_issue_pending;
        ud->ddev.device_tx_status = udma_tx_status;
        ud->ddev.device_pause = udma_pause;
@@ -3593,7 +5178,25 @@ static int udma_probe(struct platform_device *pdev)
        ud->ddev.dbg_summary_show = udma_dbg_summary_show;
 #endif
 
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       udma_alloc_chan_resources;
+               break;
+       case DMA_TYPE_BCDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       bcdma_alloc_chan_resources;
+               ud->ddev.device_router_config = bcdma_router_config;
+               break;
+       case DMA_TYPE_PKTDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       pktdma_alloc_chan_resources;
+               break;
+       default:
+               return -EINVAL;
+       }
        ud->ddev.device_free_chan_resources = udma_free_chan_resources;
+
        ud->ddev.src_addr_widths = TI_UDMAC_BUSWIDTHS;
        ud->ddev.dst_addr_widths = TI_UDMAC_BUSWIDTHS;
        ud->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
@@ -3601,7 +5204,8 @@ static int udma_probe(struct platform_device *pdev)
        ud->ddev.copy_align = DMAENGINE_ALIGN_8_BYTES;
        ud->ddev.desc_metadata_modes = DESC_METADATA_CLIENT |
                                       DESC_METADATA_ENGINE;
-       if (ud->match_data->enable_memcpy_support) {
+       if (ud->match_data->enable_memcpy_support &&
+           !(ud->match_data->type == DMA_TYPE_BCDMA && ud->bchan_cnt == 0)) {
                dma_cap_set(DMA_MEMCPY, ud->ddev.cap_mask);
                ud->ddev.device_prep_dma_memcpy = udma_prep_dma_memcpy;
                ud->ddev.directions |= BIT(DMA_MEM_TO_MEM);
@@ -3614,7 +5218,7 @@ static int udma_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&ud->ddev.channels);
        INIT_LIST_HEAD(&ud->desc_to_purge);
 
-       ch_count = udma_setup_resources(ud);
+       ch_count = setup_resources(ud);
        if (ch_count <= 0)
                return ch_count;
 
@@ -3629,6 +5233,13 @@ static int udma_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       for (i = 0; i < ud->bchan_cnt; i++) {
+               struct udma_bchan *bchan = &ud->bchans[i];
+
+               bchan->id = i;
+               bchan->reg_rt = ud->mmrs[MMR_BCHANRT] + i * 0x1000;
+       }
+
        for (i = 0; i < ud->tchan_cnt; i++) {
                struct udma_tchan *tchan = &ud->tchans[i];
 
@@ -3655,9 +5266,12 @@ static int udma_probe(struct platform_device *pdev)
                uc->ud = ud;
                uc->vc.desc_free = udma_desc_free;
                uc->id = i;
+               uc->bchan = NULL;
                uc->tchan = NULL;
                uc->rchan = NULL;
                uc->config.remote_thread_id = -1;
+               uc->config.mapped_channel_id = -1;
+               uc->config.default_flow_id = -1;
                uc->config.dir = DMA_MEM_TO_MEM;
                uc->name = devm_kasprintf(dev, GFP_KERNEL, "%s chan%d",
                                          dev_name(dev), i);
@@ -3696,5 +5310,25 @@ static struct platform_driver udma_driver = {
 };
 builtin_platform_driver(udma_driver);
 
+static struct platform_driver bcdma_driver = {
+       .driver = {
+               .name   = "ti-bcdma",
+               .of_match_table = bcdma_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe          = udma_probe,
+};
+builtin_platform_driver(bcdma_driver);
+
+static struct platform_driver pktdma_driver = {
+       .driver = {
+               .name   = "ti-pktdma",
+               .of_match_table = pktdma_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe          = udma_probe,
+};
+builtin_platform_driver(pktdma_driver);
+
 /* Private interfaces to UDMA */
 #include "k3-udma-private.c"
index 09c4529e013d750dc08e09c72d168b2ef0ff8dab..d349c6d482aee039c2049abcfa003da9bbd3a1a6 100644 (file)
@@ -18,7 +18,7 @@
 #define UDMA_RX_FLOW_ID_FW_OES_REG     0x80
 #define UDMA_RX_FLOW_ID_FW_STATUS_REG  0x88
 
-/* TCHANRT/RCHANRT registers */
+/* BCHANRT/TCHANRT/RCHANRT registers */
 #define UDMA_CHAN_RT_CTL_REG           0x0
 #define UDMA_CHAN_RT_SWTRIG_REG                0x8
 #define UDMA_CHAN_RT_STDATA_REG                0x80
 #define UDMA_CAP3_HCHAN_CNT(val)       (((val) >> 14) & 0x1ff)
 #define UDMA_CAP3_UCHAN_CNT(val)       (((val) >> 23) & 0x1ff)
 
+#define BCDMA_CAP2_BCHAN_CNT(val)      ((val) & 0x1ff)
+#define BCDMA_CAP2_TCHAN_CNT(val)      (((val) >> 9) & 0x1ff)
+#define BCDMA_CAP2_RCHAN_CNT(val)      (((val) >> 18) & 0x1ff)
+#define BCDMA_CAP3_HBCHAN_CNT(val)     (((val) >> 14) & 0x1ff)
+#define BCDMA_CAP3_UBCHAN_CNT(val)     (((val) >> 23) & 0x1ff)
+#define BCDMA_CAP4_HRCHAN_CNT(val)     ((val) & 0xff)
+#define BCDMA_CAP4_URCHAN_CNT(val)     (((val) >> 8) & 0xff)
+#define BCDMA_CAP4_HTCHAN_CNT(val)     (((val) >> 16) & 0xff)
+#define BCDMA_CAP4_UTCHAN_CNT(val)     (((val) >> 24) & 0xff)
+
+#define PKTDMA_CAP4_TFLOW_CNT(val)     ((val) & 0x3fff)
+
 /* UDMA_CHAN_RT_CTL_REG */
 #define UDMA_CHAN_RT_CTL_EN            BIT(31)
 #define UDMA_CHAN_RT_CTL_TDOWN         BIT(30)
  */
 #define PDMA_STATIC_TR_Z(x, mask)      ((x) & (mask))
 
+/* Address Space Select */
+#define K3_ADDRESS_ASEL_SHIFT          48
+
 struct udma_dev;
 struct udma_tchan;
 struct udma_rchan;
 struct udma_rflow;
 
 enum udma_rm_range {
-       RM_RANGE_TCHAN = 0,
+       RM_RANGE_BCHAN = 0,
+       RM_RANGE_TCHAN,
        RM_RANGE_RCHAN,
        RM_RANGE_RFLOW,
+       RM_RANGE_TFLOW,
        RM_RANGE_LAST,
 };
 
@@ -112,6 +129,8 @@ int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
                            u32 dst_thread);
 
 struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property);
+struct device *xudma_get_device(struct udma_dev *ud);
+struct k3_ringacc *xudma_get_ringacc(struct udma_dev *ud);
 void xudma_dev_put(struct udma_dev *ud);
 u32 xudma_dev_get_psil_base(struct udma_dev *ud);
 struct udma_tisci_rm *xudma_dev_get_tisci_rm(struct udma_dev *ud);
@@ -136,5 +155,10 @@ void xudma_tchanrt_write(struct udma_tchan *tchan, int reg, u32 val);
 u32 xudma_rchanrt_read(struct udma_rchan *rchan, int reg);
 void xudma_rchanrt_write(struct udma_rchan *rchan, int reg, u32 val);
 bool xudma_rflow_is_gp(struct udma_dev *ud, int id);
+int xudma_get_rflow_ring_offset(struct udma_dev *ud);
+
+int xudma_is_pktdma(struct udma_dev *ud);
 
+int xudma_pktdma_tflow_get_irq(struct udma_dev *ud, int udma_tflow_id);
+int xudma_pktdma_rflow_get_irq(struct udma_dev *ud, int udma_rflow_id);
 #endif /* K3_UDMA_H_ */
index 119164abcb41ac339a93ab98ecceb3ae4fefb004..b495b0d5d0fa5c653f53d68daac4552e4abd3acd 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/sys_soc.h>
+#include <linux/dma/ti-cppi5.h>
 #include <linux/soc/ti/k3-ringacc.h>
 #include <linux/soc/ti/ti_sci_protocol.h>
 #include <linux/soc/ti/ti_sci_inta_msi.h>
@@ -21,6 +22,7 @@ static LIST_HEAD(k3_ringacc_list);
 static DEFINE_MUTEX(k3_ringacc_list_lock);
 
 #define K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK            GENMASK(19, 0)
+#define K3_DMARING_CFG_RING_SIZE_ELCNT_MASK            GENMASK(15, 0)
 
 /**
  * struct k3_ring_rt_regs - The RA realtime Control/Status Registers region
@@ -43,7 +45,13 @@ struct k3_ring_rt_regs {
        u32     hwindx;
 };
 
-#define K3_RINGACC_RT_REGS_STEP        0x1000
+#define K3_RINGACC_RT_REGS_STEP                        0x1000
+#define K3_DMARING_RT_REGS_STEP                        0x2000
+#define K3_DMARING_RT_REGS_REVERSE_OFS         0x1000
+#define K3_RINGACC_RT_OCC_MASK                 GENMASK(20, 0)
+#define K3_DMARING_RT_OCC_TDOWN_COMPLETE       BIT(31)
+#define K3_DMARING_RT_DB_ENTRY_MASK            GENMASK(7, 0)
+#define K3_DMARING_RT_DB_TDOWN_ACK             BIT(31)
 
 /**
  * struct k3_ring_fifo_regs - The Ring Accelerator Queues Registers region
@@ -122,6 +130,7 @@ struct k3_ring_state {
        u32 occ;
        u32 windex;
        u32 rindex;
+       u32 tdown_complete:1;
 };
 
 /**
@@ -143,6 +152,7 @@ struct k3_ring_state {
  * @use_count: Use count for shared rings
  * @proxy_id: RA Ring Proxy Id (only if @K3_RINGACC_RING_USE_PROXY)
  * @dma_dev: device to be used for DMA API (allocation, mapping)
+ * @asel: Address Space Select value for physical addresses
  */
 struct k3_ring {
        struct k3_ring_rt_regs __iomem *rt;
@@ -157,12 +167,15 @@ struct k3_ring {
        u32             flags;
 #define K3_RING_FLAG_BUSY      BIT(1)
 #define K3_RING_FLAG_SHARED    BIT(2)
+#define K3_RING_FLAG_REVERSE   BIT(3)
        struct k3_ring_state state;
        u32             ring_id;
        struct k3_ringacc       *parent;
        u32             use_count;
        int             proxy_id;
        struct device   *dma_dev;
+       u32             asel;
+#define K3_ADDRESS_ASEL_SHIFT  48
 };
 
 struct k3_ringacc_ops {
@@ -188,6 +201,7 @@ struct k3_ringacc_ops {
  * @tisci_ring_ops: ti-sci rings ops
  * @tisci_dev_id: ti-sci device id
  * @ops: SoC specific ringacc operation
+ * @dma_rings: indicate DMA ring (dual ring within BCDMA/PKTDMA)
  */
 struct k3_ringacc {
        struct device *dev;
@@ -210,6 +224,7 @@ struct k3_ringacc {
        u32 tisci_dev_id;
 
        const struct k3_ringacc_ops *ops;
+       bool dma_rings;
 };
 
 /**
@@ -221,6 +236,21 @@ struct k3_ringacc_soc_data {
        unsigned dma_ring_reset_quirk:1;
 };
 
+static int k3_ringacc_ring_read_occ(struct k3_ring *ring)
+{
+       return readl(&ring->rt->occ) & K3_RINGACC_RT_OCC_MASK;
+}
+
+static void k3_ringacc_ring_update_occ(struct k3_ring *ring)
+{
+       u32 val;
+
+       val = readl(&ring->rt->occ);
+
+       ring->state.occ = val & K3_RINGACC_RT_OCC_MASK;
+       ring->state.tdown_complete = !!(val & K3_DMARING_RT_OCC_TDOWN_COMPLETE);
+}
+
 static long k3_ringacc_ring_get_fifo_pos(struct k3_ring *ring)
 {
        return K3_RINGACC_FIFO_WINDOW_SIZE_BYTES -
@@ -234,12 +264,24 @@ static void *k3_ringacc_get_elm_addr(struct k3_ring *ring, u32 idx)
 
 static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem);
 static int k3_ringacc_ring_pop_mem(struct k3_ring *ring, void *elem);
+static int k3_dmaring_fwd_pop(struct k3_ring *ring, void *elem);
+static int k3_dmaring_reverse_pop(struct k3_ring *ring, void *elem);
 
 static struct k3_ring_ops k3_ring_mode_ring_ops = {
                .push_tail = k3_ringacc_ring_push_mem,
                .pop_head = k3_ringacc_ring_pop_mem,
 };
 
+static struct k3_ring_ops k3_dmaring_fwd_ops = {
+               .push_tail = k3_ringacc_ring_push_mem,
+               .pop_head = k3_dmaring_fwd_pop,
+};
+
+static struct k3_ring_ops k3_dmaring_reverse_ops = {
+               /* Reverse side of the DMA ring can only be popped by SW */
+               .pop_head = k3_dmaring_reverse_pop,
+};
+
 static int k3_ringacc_ring_push_io(struct k3_ring *ring, void *elem);
 static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem);
 static int k3_ringacc_ring_push_head_io(struct k3_ring *ring, void *elem);
@@ -342,6 +384,40 @@ error:
 }
 EXPORT_SYMBOL_GPL(k3_ringacc_request_ring);
 
+static int k3_dmaring_request_dual_ring(struct k3_ringacc *ringacc, int fwd_id,
+                                       struct k3_ring **fwd_ring,
+                                       struct k3_ring **compl_ring)
+{
+       int ret = 0;
+
+       /*
+        * DMA rings must be requested by ID, completion ring is the reverse
+        * side of the forward ring
+        */
+       if (fwd_id < 0)
+               return -EINVAL;
+
+       mutex_lock(&ringacc->req_lock);
+
+       if (test_bit(fwd_id, ringacc->rings_inuse)) {
+               ret = -EBUSY;
+               goto error;
+       }
+
+       *fwd_ring = &ringacc->rings[fwd_id];
+       *compl_ring = &ringacc->rings[fwd_id + ringacc->num_rings];
+       set_bit(fwd_id, ringacc->rings_inuse);
+       ringacc->rings[fwd_id].use_count++;
+       dev_dbg(ringacc->dev, "Giving ring#%d\n", fwd_id);
+
+       mutex_unlock(&ringacc->req_lock);
+       return 0;
+
+error:
+       mutex_unlock(&ringacc->req_lock);
+       return ret;
+}
+
 int k3_ringacc_request_rings_pair(struct k3_ringacc *ringacc,
                                  int fwd_id, int compl_id,
                                  struct k3_ring **fwd_ring,
@@ -352,6 +428,10 @@ int k3_ringacc_request_rings_pair(struct k3_ringacc *ringacc,
        if (!fwd_ring || !compl_ring)
                return -EINVAL;
 
+       if (ringacc->dma_rings)
+               return k3_dmaring_request_dual_ring(ringacc, fwd_id,
+                                                   fwd_ring, compl_ring);
+
        *fwd_ring = k3_ringacc_request_ring(ringacc, fwd_id, 0);
        if (!(*fwd_ring))
                return -ENODEV;
@@ -421,7 +501,7 @@ void k3_ringacc_ring_reset_dma(struct k3_ring *ring, u32 occ)
                goto reset;
 
        if (!occ)
-               occ = readl(&ring->rt->occ);
+               occ = k3_ringacc_ring_read_occ(ring);
 
        if (occ) {
                u32 db_ring_cnt, db_ring_cnt_cur;
@@ -496,6 +576,13 @@ int k3_ringacc_ring_free(struct k3_ring *ring)
 
        ringacc = ring->parent;
 
+       /*
+        * DMA rings: rings shared memory and configuration, only forward ring
+        * is configured and reverse ring considered as slave.
+        */
+       if (ringacc->dma_rings && (ring->flags & K3_RING_FLAG_REVERSE))
+               return 0;
+
        dev_dbg(ring->parent->dev, "flags: 0x%08x\n", ring->flags);
 
        if (!test_bit(ring->ring_id, ringacc->rings_inuse))
@@ -517,6 +604,8 @@ int k3_ringacc_ring_free(struct k3_ring *ring)
        ring->flags = 0;
        ring->ops = NULL;
        ring->dma_dev = NULL;
+       ring->asel = 0;
+
        if (ring->proxy_id != K3_RINGACC_PROXY_NOT_USED) {
                clear_bit(ring->proxy_id, ringacc->proxy_inuse);
                ring->proxy = NULL;
@@ -581,6 +670,7 @@ static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring)
        ring_cfg.count = ring->size;
        ring_cfg.mode = ring->mode;
        ring_cfg.size = ring->elm_size;
+       ring_cfg.asel = ring->asel;
 
        ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
        if (ret)
@@ -590,6 +680,90 @@ static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring)
        return ret;
 }
 
+static int k3_dmaring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg)
+{
+       struct k3_ringacc *ringacc;
+       struct k3_ring *reverse_ring;
+       int ret = 0;
+
+       if (cfg->elm_size != K3_RINGACC_RING_ELSIZE_8 ||
+           cfg->mode != K3_RINGACC_RING_MODE_RING ||
+           cfg->size & ~K3_DMARING_CFG_RING_SIZE_ELCNT_MASK)
+               return -EINVAL;
+
+       ringacc = ring->parent;
+
+       /*
+        * DMA rings: rings shared memory and configuration, only forward ring
+        * is configured and reverse ring considered as slave.
+        */
+       if (ringacc->dma_rings && (ring->flags & K3_RING_FLAG_REVERSE))
+               return 0;
+
+       if (!test_bit(ring->ring_id, ringacc->rings_inuse))
+               return -EINVAL;
+
+       ring->size = cfg->size;
+       ring->elm_size = cfg->elm_size;
+       ring->mode = cfg->mode;
+       ring->asel = cfg->asel;
+       ring->dma_dev = cfg->dma_dev;
+       if (!ring->dma_dev) {
+               dev_warn(ringacc->dev, "dma_dev is not provided for ring%d\n",
+                        ring->ring_id);
+               ring->dma_dev = ringacc->dev;
+       }
+
+       memset(&ring->state, 0, sizeof(ring->state));
+
+       ring->ops = &k3_dmaring_fwd_ops;
+
+       ring->ring_mem_virt = dma_alloc_coherent(ring->dma_dev,
+                                                ring->size * (4 << ring->elm_size),
+                                                &ring->ring_mem_dma, GFP_KERNEL);
+       if (!ring->ring_mem_virt) {
+               dev_err(ringacc->dev, "Failed to alloc ring mem\n");
+               ret = -ENOMEM;
+               goto err_free_ops;
+       }
+
+       ret = k3_ringacc_ring_cfg_sci(ring);
+       if (ret)
+               goto err_free_mem;
+
+       ring->flags |= K3_RING_FLAG_BUSY;
+
+       k3_ringacc_ring_dump(ring);
+
+       /* DMA rings: configure reverse ring */
+       reverse_ring = &ringacc->rings[ring->ring_id + ringacc->num_rings];
+       reverse_ring->size = cfg->size;
+       reverse_ring->elm_size = cfg->elm_size;
+       reverse_ring->mode = cfg->mode;
+       reverse_ring->asel = cfg->asel;
+       memset(&reverse_ring->state, 0, sizeof(reverse_ring->state));
+       reverse_ring->ops = &k3_dmaring_reverse_ops;
+
+       reverse_ring->ring_mem_virt = ring->ring_mem_virt;
+       reverse_ring->ring_mem_dma = ring->ring_mem_dma;
+       reverse_ring->flags |= K3_RING_FLAG_BUSY;
+       k3_ringacc_ring_dump(reverse_ring);
+
+       return 0;
+
+err_free_mem:
+       dma_free_coherent(ring->dma_dev,
+                         ring->size * (4 << ring->elm_size),
+                         ring->ring_mem_virt,
+                         ring->ring_mem_dma);
+err_free_ops:
+       ring->ops = NULL;
+       ring->proxy = NULL;
+       ring->dma_dev = NULL;
+       ring->asel = 0;
+       return ret;
+}
+
 int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg)
 {
        struct k3_ringacc *ringacc;
@@ -597,8 +771,12 @@ int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg)
 
        if (!ring || !cfg)
                return -EINVAL;
+
        ringacc = ring->parent;
 
+       if (ringacc->dma_rings)
+               return k3_dmaring_cfg(ring, cfg);
+
        if (cfg->elm_size > K3_RINGACC_RING_ELSIZE_256 ||
            cfg->mode >= K3_RINGACC_RING_MODE_INVALID ||
            cfg->size & ~K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK ||
@@ -705,7 +883,7 @@ u32 k3_ringacc_ring_get_free(struct k3_ring *ring)
                return -EINVAL;
 
        if (!ring->state.free)
-               ring->state.free = ring->size - readl(&ring->rt->occ);
+               ring->state.free = ring->size - k3_ringacc_ring_read_occ(ring);
 
        return ring->state.free;
 }
@@ -716,7 +894,7 @@ u32 k3_ringacc_ring_get_occ(struct k3_ring *ring)
        if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))
                return -EINVAL;
 
-       return readl(&ring->rt->occ);
+       return k3_ringacc_ring_read_occ(ring);
 }
 EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_occ);
 
@@ -892,6 +1070,72 @@ static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem)
                                         K3_RINGACC_ACCESS_MODE_POP_HEAD);
 }
 
+/*
+ * The element is 48 bits of address + ASEL bits in the ring.
+ * ASEL is used by the DMAs and should be removed for the kernel as it is not
+ * part of the physical memory address.
+ */
+static void k3_dmaring_remove_asel_from_elem(u64 *elem)
+{
+       *elem &= GENMASK_ULL(K3_ADDRESS_ASEL_SHIFT - 1, 0);
+}
+
+static int k3_dmaring_fwd_pop(struct k3_ring *ring, void *elem)
+{
+       void *elem_ptr;
+       u32 elem_idx;
+
+       /*
+        * DMA rings: forward ring is always tied DMA channel and HW does not
+        * maintain any state data required for POP operation and its unknown
+        * how much elements were consumed by HW. So, to actually
+        * do POP, the read pointer has to be recalculated every time.
+        */
+       ring->state.occ = k3_ringacc_ring_read_occ(ring);
+       if (ring->state.windex >= ring->state.occ)
+               elem_idx = ring->state.windex - ring->state.occ;
+       else
+               elem_idx = ring->size - (ring->state.occ - ring->state.windex);
+
+       elem_ptr = k3_ringacc_get_elm_addr(ring, elem_idx);
+       memcpy(elem, elem_ptr, (4 << ring->elm_size));
+       k3_dmaring_remove_asel_from_elem(elem);
+
+       ring->state.occ--;
+       writel(-1, &ring->rt->db);
+
+       dev_dbg(ring->parent->dev, "%s: occ%d Windex%d Rindex%d pos_ptr%px\n",
+               __func__, ring->state.occ, ring->state.windex, elem_idx,
+               elem_ptr);
+       return 0;
+}
+
+static int k3_dmaring_reverse_pop(struct k3_ring *ring, void *elem)
+{
+       void *elem_ptr;
+
+       elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.rindex);
+
+       if (ring->state.occ) {
+               memcpy(elem, elem_ptr, (4 << ring->elm_size));
+               k3_dmaring_remove_asel_from_elem(elem);
+
+               ring->state.rindex = (ring->state.rindex + 1) % ring->size;
+               ring->state.occ--;
+               writel(-1 & K3_DMARING_RT_DB_ENTRY_MASK, &ring->rt->db);
+       } else if (ring->state.tdown_complete) {
+               dma_addr_t *value = elem;
+
+               *value = CPPI5_TDCM_MARKER;
+               writel(K3_DMARING_RT_DB_TDOWN_ACK, &ring->rt->db);
+               ring->state.tdown_complete = false;
+       }
+
+       dev_dbg(ring->parent->dev, "%s: occ%d index%d pos_ptr%px\n",
+               __func__, ring->state.occ, ring->state.rindex, elem_ptr);
+       return 0;
+}
+
 static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem)
 {
        void *elem_ptr;
@@ -899,6 +1143,11 @@ static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem)
        elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.windex);
 
        memcpy(elem_ptr, elem, (4 << ring->elm_size));
+       if (ring->parent->dma_rings) {
+               u64 *addr = elem_ptr;
+
+               *addr |= ((u64)ring->asel << K3_ADDRESS_ASEL_SHIFT);
+       }
 
        ring->state.windex = (ring->state.windex + 1) % ring->size;
        ring->state.free--;
@@ -975,12 +1224,12 @@ int k3_ringacc_ring_pop(struct k3_ring *ring, void *elem)
                return -EINVAL;
 
        if (!ring->state.occ)
-               ring->state.occ = k3_ringacc_ring_get_occ(ring);
+               k3_ringacc_ring_update_occ(ring);
 
        dev_dbg(ring->parent->dev, "ring_pop: occ%d index%d\n", ring->state.occ,
                ring->state.rindex);
 
-       if (!ring->state.occ)
+       if (!ring->state.occ && !ring->state.tdown_complete)
                return -ENODATA;
 
        if (ring->ops && ring->ops->pop_head)
@@ -998,7 +1247,7 @@ int k3_ringacc_ring_pop_tail(struct k3_ring *ring, void *elem)
                return -EINVAL;
 
        if (!ring->state.occ)
-               ring->state.occ = k3_ringacc_ring_get_occ(ring);
+               k3_ringacc_ring_update_occ(ring);
 
        dev_dbg(ring->parent->dev, "ring_pop_tail: occ%d index%d\n",
                ring->state.occ, ring->state.rindex);
@@ -1203,6 +1452,68 @@ static const struct of_device_id k3_ringacc_of_match[] = {
        {},
 };
 
+struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev,
+                                           struct k3_ringacc_init_data *data)
+{
+       struct device *dev = &pdev->dev;
+       struct k3_ringacc *ringacc;
+       void __iomem *base_rt;
+       struct resource *res;
+       int i;
+
+       ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL);
+       if (!ringacc)
+               return ERR_PTR(-ENOMEM);
+
+       ringacc->dev = dev;
+       ringacc->dma_rings = true;
+       ringacc->num_rings = data->num_rings;
+       ringacc->tisci = data->tisci;
+       ringacc->tisci_dev_id = data->tisci_dev_id;
+
+       mutex_init(&ringacc->req_lock);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ringrt");
+       base_rt = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base_rt))
+               return ERR_CAST(base_rt);
+
+       ringacc->rings = devm_kzalloc(dev,
+                                     sizeof(*ringacc->rings) *
+                                     ringacc->num_rings * 2,
+                                     GFP_KERNEL);
+       ringacc->rings_inuse = devm_kcalloc(dev,
+                                           BITS_TO_LONGS(ringacc->num_rings),
+                                           sizeof(unsigned long), GFP_KERNEL);
+
+       if (!ringacc->rings || !ringacc->rings_inuse)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < ringacc->num_rings; i++) {
+               struct k3_ring *ring = &ringacc->rings[i];
+
+               ring->rt = base_rt + K3_DMARING_RT_REGS_STEP * i;
+               ring->parent = ringacc;
+               ring->ring_id = i;
+               ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;
+
+               ring = &ringacc->rings[ringacc->num_rings + i];
+               ring->rt = base_rt + K3_DMARING_RT_REGS_STEP * i +
+                          K3_DMARING_RT_REGS_REVERSE_OFS;
+               ring->parent = ringacc;
+               ring->ring_id = i;
+               ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;
+               ring->flags = K3_RING_FLAG_REVERSE;
+       }
+
+       ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops;
+
+       dev_info(dev, "Number of rings: %u\n", ringacc->num_rings);
+
+       return ringacc;
+}
+EXPORT_SYMBOL_GPL(k3_ringacc_dmarings_init);
+
 static int k3_ringacc_probe(struct platform_device *pdev)
 {
        const struct ringacc_match_data *match_data;
diff --git a/include/dt-bindings/dma/jz4775-dma.h b/include/dt-bindings/dma/jz4775-dma.h
new file mode 100644 (file)
index 0000000..8d27e2c
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This header provides macros for JZ4775 DMA bindings.
+ *
+ * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+ */
+
+#ifndef __DT_BINDINGS_DMA_JZ4775_DMA_H__
+#define __DT_BINDINGS_DMA_JZ4775_DMA_H__
+
+/*
+ * Request type numbers for the JZ4775 DMA controller (written to the DRTn
+ * register for the channel).
+ */
+#define JZ4775_DMA_I2S0_TX     0x6
+#define JZ4775_DMA_I2S0_RX     0x7
+#define JZ4775_DMA_AUTO                0x8
+#define JZ4775_DMA_SADC_RX     0x9
+#define JZ4775_DMA_UART3_TX    0x0e
+#define JZ4775_DMA_UART3_RX    0x0f
+#define JZ4775_DMA_UART2_TX    0x10
+#define JZ4775_DMA_UART2_RX    0x11
+#define JZ4775_DMA_UART1_TX    0x12
+#define JZ4775_DMA_UART1_RX    0x13
+#define JZ4775_DMA_UART0_TX    0x14
+#define JZ4775_DMA_UART0_RX    0x15
+#define JZ4775_DMA_SSI0_TX     0x16
+#define JZ4775_DMA_SSI0_RX     0x17
+#define JZ4775_DMA_MSC0_TX     0x1a
+#define JZ4775_DMA_MSC0_RX     0x1b
+#define JZ4775_DMA_MSC1_TX     0x1c
+#define JZ4775_DMA_MSC1_RX     0x1d
+#define JZ4775_DMA_MSC2_TX     0x1e
+#define JZ4775_DMA_MSC2_RX     0x1f
+#define JZ4775_DMA_PCM0_TX     0x20
+#define JZ4775_DMA_PCM0_RX     0x21
+#define JZ4775_DMA_SMB0_TX     0x24
+#define JZ4775_DMA_SMB0_RX     0x25
+#define JZ4775_DMA_SMB1_TX     0x26
+#define JZ4775_DMA_SMB1_RX     0x27
+#define JZ4775_DMA_SMB2_TX     0x28
+#define JZ4775_DMA_SMB2_RX     0x29
+
+#endif /* __DT_BINDINGS_DMA_JZ4775_DMA_H__ */
diff --git a/include/dt-bindings/dma/qcom-gpi.h b/include/dt-bindings/dma/qcom-gpi.h
new file mode 100644 (file)
index 0000000..ebda2a3
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/* Copyright (c) 2020, Linaro Ltd.  */
+
+#ifndef __DT_BINDINGS_DMA_QCOM_GPI_H__
+#define __DT_BINDINGS_DMA_QCOM_GPI_H__
+
+#define QCOM_GPI_SPI           1
+#define QCOM_GPI_UART          2
+#define QCOM_GPI_I2C           3
+
+#endif /* __DT_BINDINGS_DMA_QCOM_GPI_H__ */
diff --git a/include/dt-bindings/dma/x2000-dma.h b/include/dt-bindings/dma/x2000-dma.h
new file mode 100644 (file)
index 0000000..db2cd48
--- /dev/null
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This header provides macros for X2000 DMA bindings.
+ *
+ * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+ */
+
+#ifndef __DT_BINDINGS_DMA_X2000_DMA_H__
+#define __DT_BINDINGS_DMA_X2000_DMA_H__
+
+/*
+ * Request type numbers for the X2000 DMA controller (written to the DRTn
+ * register for the channel).
+ */
+#define X2000_DMA_AUTO         0x8
+#define X2000_DMA_UART5_TX     0xa
+#define X2000_DMA_UART5_RX     0xb
+#define X2000_DMA_UART4_TX     0xc
+#define X2000_DMA_UART4_RX     0xd
+#define X2000_DMA_UART3_TX     0xe
+#define X2000_DMA_UART3_RX     0xf
+#define X2000_DMA_UART2_TX     0x10
+#define X2000_DMA_UART2_RX     0x11
+#define X2000_DMA_UART1_TX     0x12
+#define X2000_DMA_UART1_RX     0x13
+#define X2000_DMA_UART0_TX     0x14
+#define X2000_DMA_UART0_RX     0x15
+#define X2000_DMA_SSI0_TX      0x16
+#define X2000_DMA_SSI0_RX      0x17
+#define X2000_DMA_SSI1_TX      0x18
+#define X2000_DMA_SSI1_RX      0x19
+#define X2000_DMA_I2C0_TX      0x24
+#define X2000_DMA_I2C0_RX      0x25
+#define X2000_DMA_I2C1_TX      0x26
+#define X2000_DMA_I2C1_RX      0x27
+#define X2000_DMA_I2C2_TX      0x28
+#define X2000_DMA_I2C2_RX      0x29
+#define X2000_DMA_I2C3_TX      0x2a
+#define X2000_DMA_I2C3_RX      0x2b
+#define X2000_DMA_I2C4_TX      0x2c
+#define X2000_DMA_I2C4_RX      0x2d
+#define X2000_DMA_I2C5_TX      0x2e
+#define X2000_DMA_I2C5_RX      0x2f
+#define X2000_DMA_UART6_TX     0x30
+#define X2000_DMA_UART6_RX     0x31
+#define X2000_DMA_UART7_TX     0x32
+#define X2000_DMA_UART7_RX     0x33
+#define X2000_DMA_UART8_TX     0x34
+#define X2000_DMA_UART8_RX     0x35
+#define X2000_DMA_UART9_TX     0x36
+#define X2000_DMA_UART9_RX     0x37
+#define X2000_DMA_SADC_RX      0x38
+
+#endif /* __DT_BINDINGS_DMA_X2000_DMA_H__ */
diff --git a/include/linux/dma/k3-event-router.h b/include/linux/dma/k3-event-router.h
new file mode 100644 (file)
index 0000000..e3f88b2
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#ifndef K3_EVENT_ROUTER_
+#define K3_EVENT_ROUTER_
+
+#include <linux/types.h>
+
+struct k3_event_route_data {
+       void *priv;
+       int (*set_event)(void *priv, u32 event);
+};
+
+#endif /* K3_EVENT_ROUTER_ */
index 1962f75fa2d37176ceeed44d0337a689d4dd0484..36e22c5a0f29fd09e7c7650201ff5da84e69e45d 100644 (file)
@@ -50,6 +50,15 @@ enum psil_endpoint_type {
  * @channel_tpl:       Desired throughput level for the channel
  * @pdma_acc32:                ACC32 must be enabled on the PDMA side
  * @pdma_burst:                BURST must be enabled on the PDMA side
+ * @mapped_channel_id: PKTDMA thread to channel mapping for mapped channels.
+ *                     The thread must be serviced by the specified channel if
+ *                     mapped_channel_id is >= 0 in case of PKTDMA
+ * @flow_start:                PKDMA flow range start of mapped channel. Unmapped
+ *                     channels use flow_id == chan_id
+ * @flow_num:          PKDMA flow count of mapped channel. Unmapped channels
+ *                     use flow_id == chan_id
+ * @default_flow_id:   PKDMA default (r)flow index of mapped channel.
+ *                     Must be within the flow range of the mapped channel.
  */
 struct psil_endpoint_config {
        enum psil_endpoint_type ep_type;
@@ -63,6 +72,13 @@ struct psil_endpoint_config {
        /* PDMA properties, valid for PSIL_EP_PDMA_* */
        unsigned pdma_acc32:1;
        unsigned pdma_burst:1;
+
+       /* PKDMA mapped channel */
+       int mapped_channel_id;
+       /* PKTDMA tflow and rflow ranges for mapped channel */
+       u16 flow_start;
+       u16 flow_num;
+       u16 default_flow_id;
 };
 
 int psil_set_new_ep_config(struct device *dev, const char *name,
index 5eb34ad973a7ab6c81fdf4d3ad00acdf9345bb59..e443be4d3b4b7e04bc350aff0b9d3a9e2fd4acbf 100644 (file)
@@ -41,6 +41,12 @@ void k3_udma_glue_reset_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
 u32 k3_udma_glue_tx_get_hdesc_size(struct k3_udma_glue_tx_channel *tx_chn);
 u32 k3_udma_glue_tx_get_txcq_id(struct k3_udma_glue_tx_channel *tx_chn);
 int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn);
+struct device *
+       k3_udma_glue_tx_get_dma_device(struct k3_udma_glue_tx_channel *tx_chn);
+void k3_udma_glue_tx_dma_to_cppi5_addr(struct k3_udma_glue_tx_channel *tx_chn,
+                                      dma_addr_t *addr);
+void k3_udma_glue_tx_cppi5_to_dma_addr(struct k3_udma_glue_tx_channel *tx_chn,
+                                      dma_addr_t *addr);
 
 enum {
        K3_UDMA_GLUE_SRC_TAG_LO_KEEP = 0,
@@ -130,5 +136,11 @@ int k3_udma_glue_rx_flow_enable(struct k3_udma_glue_rx_channel *rx_chn,
                                u32 flow_idx);
 int k3_udma_glue_rx_flow_disable(struct k3_udma_glue_rx_channel *rx_chn,
                                 u32 flow_idx);
+struct device *
+       k3_udma_glue_rx_get_dma_device(struct k3_udma_glue_rx_channel *rx_chn);
+void k3_udma_glue_rx_dma_to_cppi5_addr(struct k3_udma_glue_rx_channel *rx_chn,
+                                      dma_addr_t *addr);
+void k3_udma_glue_rx_cppi5_to_dma_addr(struct k3_udma_glue_rx_channel *rx_chn,
+                                      dma_addr_t *addr);
 
 #endif /* K3_UDMA_GLUE_H_ */
diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h
new file mode 100644 (file)
index 0000000..f46dc33
--- /dev/null
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#ifndef QCOM_GPI_DMA_H
+#define QCOM_GPI_DMA_H
+
+/**
+ * enum spi_transfer_cmd - spi transfer commands
+ */
+enum spi_transfer_cmd {
+       SPI_TX = 1,
+       SPI_RX,
+       SPI_DUPLEX,
+};
+
+/**
+ * struct gpi_spi_config - spi config for peripheral
+ *
+ * @loopback_en: spi loopback enable when set
+ * @clock_pol_high: clock polarity
+ * @data_pol_high: data polarity
+ * @pack_en: process tx/rx buffers as packed
+ * @word_len: spi word length
+ * @clk_div: source clock divider
+ * @clk_src: serial clock
+ * @cmd: spi cmd
+ * @fragmentation: keep CS assserted at end of sequence
+ * @cs: chip select toggle
+ * @set_config: set peripheral config
+ * @rx_len: receive length for buffer
+ */
+struct gpi_spi_config {
+       u8 set_config;
+       u8 loopback_en;
+       u8 clock_pol_high;
+       u8 data_pol_high;
+       u8 pack_en;
+       u8 word_len;
+       u8 fragmentation;
+       u8 cs;
+       u32 clk_div;
+       u32 clk_src;
+       enum spi_transfer_cmd cmd;
+       u32 rx_len;
+};
+
+enum i2c_op {
+       I2C_WRITE = 1,
+       I2C_READ,
+};
+
+/**
+ * struct gpi_i2c_config - i2c config for peripheral
+ *
+ * @pack_enable: process tx/rx buffers as packed
+ * @cycle_count: clock cycles to be sent
+ * @high_count: high period of clock
+ * @low_count: low period of clock
+ * @clk_div: source clock divider
+ * @addr: i2c bus address
+ * @stretch: stretch the clock at eot
+ * @set_config: set peripheral config
+ * @rx_len: receive length for buffer
+ * @op: i2c cmd
+ * @muli-msg: is part of multi i2c r-w msgs
+ */
+struct gpi_i2c_config {
+       u8 set_config;
+       u8 pack_enable;
+       u8 cycle_count;
+       u8 high_count;
+       u8 low_count;
+       u8 addr;
+       u8 stretch;
+       u16 clk_div;
+       u32 rx_len;
+       enum i2c_op op;
+       bool multi_msg;
+};
+
+#endif /* QCOM_GPI_DMA_H */
index dd357a747780fc63017ea2d07db8c5c80f177e66..68130f5f599e1146e56b3f7fe4ef376443a8ca79 100644 (file)
@@ -357,11 +357,14 @@ struct dma_chan {
  * @chan: driver channel device
  * @device: sysfs device
  * @dev_id: parent dma_device dev_id
+ * @chan_dma_dev: The channel is using custom/different dma-mapping
+ * compared to the parent dma_device
  */
 struct dma_chan_dev {
        struct dma_chan *chan;
        struct device device;
        int dev_id;
+       bool chan_dma_dev;
 };
 
 /**
@@ -418,6 +421,9 @@ enum dma_slave_buswidth {
  * @slave_id: Slave requester id. Only valid for slave channels. The dma
  * slave peripheral will have unique id as dma requester which need to be
  * pass as slave config.
+ * @peripheral_config: peripheral configuration for programming peripheral
+ * for dmaengine transfer
+ * @peripheral_size: peripheral configuration buffer size
  *
  * This struct is passed in as configuration data to a DMA engine
  * in order to set up a certain channel for DMA transport at runtime.
@@ -443,6 +449,8 @@ struct dma_slave_config {
        u32 dst_port_window_size;
        bool device_fc;
        unsigned int slave_id;
+       void *peripheral_config;
+       size_t peripheral_size;
 };
 
 /**
@@ -800,6 +808,7 @@ struct dma_filter {
  *     by tx_status
  * @device_alloc_chan_resources: allocate resources and return the
  *     number of allocated descriptors
+ * @device_router_config: optional callback for DMA router configuration
  * @device_free_chan_resources: release DMA channel's resources
  * @device_prep_dma_memcpy: prepares a memcpy operation
  * @device_prep_dma_xor: prepares a xor operation
@@ -874,6 +883,7 @@ struct dma_device {
        enum dma_residue_granularity residue_granularity;
 
        int (*device_alloc_chan_resources)(struct dma_chan *chan);
+       int (*device_router_config)(struct dma_chan *chan);
        void (*device_free_chan_resources)(struct dma_chan *chan);
 
        struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
@@ -1611,4 +1621,13 @@ dmaengine_get_direction_text(enum dma_transfer_direction dir)
                return "invalid";
        }
 }
+
+static inline struct device *dmaengine_get_dma_device(struct dma_chan *chan)
+{
+       if (chan->dev->chan_dma_dev)
+               return &chan->dev->device;
+
+       return chan->device->dev;
+}
+
 #endif /* DMAENGINE_H */
index 658dc71d2901e043f696c990981ace6b46be1638..39b022b9259867415633229c39f32cdb20870c93 100644 (file)
@@ -70,6 +70,7 @@ struct k3_ring;
  * @dma_dev: Master device which is using and accessing to the ring
  *     memory when the mode is K3_RINGACC_RING_MODE_RING. Memory allocations
  *     should be done using this device.
+ * @asel: Address Space Select value for physical addresses
  */
 struct k3_ring_cfg {
        u32 size;
@@ -79,6 +80,7 @@ struct k3_ring_cfg {
        u32 flags;
 
        struct device *dma_dev;
+       u32 asel;
 };
 
 #define K3_RINGACC_RING_ID_ANY (-1)
@@ -250,4 +252,19 @@ int k3_ringacc_ring_pop_tail(struct k3_ring *ring, void *elem);
 
 u32 k3_ringacc_get_tisci_dev_id(struct k3_ring *ring);
 
+/* DMA ring support */
+struct ti_sci_handle;
+
+/**
+ * struct struct k3_ringacc_init_data - Initialization data for DMA rings
+ */
+struct k3_ringacc_init_data {
+       const struct ti_sci_handle *tisci;
+       u32 tisci_dev_id;
+       u32 num_rings;
+};
+
+struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev,
+                                           struct k3_ringacc_init_data *data);
+
 #endif /* __SOC_TI_K3_RINGACC_API_H_ */
index fdcdfe414223e4aeec5f64622acc82ddad994f06..236d437947bc9d6d70bd0386ba90571755d31908 100644 (file)
@@ -26,6 +26,9 @@
 #define IDXD_OP_FLAG_DRDBK     0x4000
 #define IDXD_OP_FLAG_DSTS      0x8000
 
+/* IAX */
+#define IDXD_OP_FLAG_RD_SRC2_AECS      0x010000
+
 /* Opcode */
 enum dsa_opcode {
        DSA_OPCODE_NOOP = 0,
@@ -47,6 +50,14 @@ enum dsa_opcode {
        DSA_OPCODE_CFLUSH = 0x20,
 };
 
+enum iax_opcode {
+       IAX_OPCODE_NOOP = 0,
+       IAX_OPCODE_DRAIN = 2,
+       IAX_OPCODE_MEMMOVE,
+       IAX_OPCODE_DECOMPRESS = 0x42,
+       IAX_OPCODE_COMPRESS,
+};
+
 /* Completion record status */
 enum dsa_completion_status {
        DSA_COMP_NONE = 0,
@@ -80,6 +91,33 @@ enum dsa_completion_status {
        DSA_COMP_TRANSLATION_FAIL,
 };
 
+enum iax_completion_status {
+       IAX_COMP_NONE = 0,
+       IAX_COMP_SUCCESS,
+       IAX_COMP_PAGE_FAULT_IR = 0x04,
+       IAX_COMP_OUTBUF_OVERFLOW,
+       IAX_COMP_BAD_OPCODE = 0x10,
+       IAX_COMP_INVALID_FLAGS,
+       IAX_COMP_NOZERO_RESERVE,
+       IAX_COMP_INVALID_SIZE,
+       IAX_COMP_OVERLAP_BUFFERS = 0x16,
+       IAX_COMP_INT_HANDLE_INVAL = 0x19,
+       IAX_COMP_CRA_XLAT,
+       IAX_COMP_CRA_ALIGN,
+       IAX_COMP_ADDR_ALIGN,
+       IAX_COMP_PRIV_BAD,
+       IAX_COMP_TRAFFIC_CLASS_CONF,
+       IAX_COMP_PFAULT_RDBA,
+       IAX_COMP_HW_ERR1,
+       IAX_COMP_HW_ERR_DRB,
+       IAX_COMP_TRANSLATION_FAIL,
+       IAX_COMP_PRS_TIMEOUT,
+       IAX_COMP_WATCHDOG,
+       IAX_COMP_INVALID_COMP_FLAG = 0x30,
+       IAX_COMP_INVALID_FILTER_FLAG,
+       IAX_COMP_INVALID_NUM_ELEMS = 0x33,
+};
+
 #define DSA_COMP_STATUS_MASK           0x7f
 #define DSA_COMP_STATUS_WRITE          0x80
 
@@ -163,6 +201,28 @@ struct dsa_hw_desc {
        };
 } __attribute__((packed));
 
+struct iax_hw_desc {
+       uint32_t        pasid:20;
+       uint32_t        rsvd:11;
+       uint32_t        priv:1;
+       uint32_t        flags:24;
+       uint32_t        opcode:8;
+       uint64_t        completion_addr;
+       uint64_t        src1_addr;
+       uint64_t        dst_addr;
+       uint32_t        src1_size;
+       uint16_t        int_handle;
+       union {
+               uint16_t        compr_flags;
+               uint16_t        decompr_flags;
+       };
+       uint64_t        src2_addr;
+       uint32_t        max_dst_size;
+       uint32_t        src2_size;
+       uint32_t        filter_flags;
+       uint32_t        num_inputs;
+} __attribute__((packed));
+
 struct dsa_raw_desc {
        uint64_t        field[8];
 } __attribute__((packed));
@@ -223,4 +283,23 @@ struct dsa_raw_completion_record {
        uint64_t        field[4];
 } __attribute__((packed));
 
+struct iax_completion_record {
+       volatile uint8_t        status;
+       uint8_t                 error_code;
+       uint16_t                rsvd;
+       uint32_t                bytes_completed;
+       uint64_t                fault_addr;
+       uint32_t                invalid_flags;
+       uint32_t                rsvd2;
+       uint32_t                output_size;
+       uint8_t                 output_bits;
+       uint8_t                 rsvd3;
+       uint16_t                rsvd4;
+       uint64_t                rsvd5[4];
+} __attribute__((packed));
+
+struct iax_raw_completion_record {
+       uint64_t        field[8];
+} __attribute__((packed));
+
 #endif