Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git

This commit is contained in:
Stephen Rothwell 2025-01-14 14:56:49 +11:00
commit 41363b1d4f
47 changed files with 2059 additions and 375 deletions

View File

@ -1,61 +0,0 @@
Analog Devices AXI-DMAC DMA controller
Required properties:
- compatible: Must be "adi,axi-dmac-1.00.a".
- reg: Specification for the controllers memory mapped register map.
- interrupts: Specification for the controllers interrupt.
- clocks: Phandle and specifier to the controllers AXI interface clock
- #dma-cells: Must be 1.
Required sub-nodes:
- adi,channels: This sub-node must contain a sub-node for each DMA channel. For
the channel sub-nodes the following bindings apply. They must match the
configuration options of the peripheral as it was instantiated.
Required properties for adi,channels sub-node:
- #size-cells: Must be 0
- #address-cells: Must be 1
Required channel sub-node properties:
- reg: Which channel this node refers to.
- adi,source-bus-width,
adi,destination-bus-width: Width of the source or destination bus in bits.
- adi,source-bus-type,
adi,destination-bus-type: Type of the source or destination bus. Must be one
of the following:
0 (AXI_DMAC_TYPE_AXI_MM): Memory mapped AXI interface
1 (AXI_DMAC_TYPE_AXI_STREAM): Streaming AXI interface
2 (AXI_DMAC_TYPE_AXI_FIFO): FIFO interface
Deprecated optional channel properties:
- adi,length-width: Width of the DMA transfer length register.
- adi,cyclic: Must be set if the channel supports hardware cyclic DMA
transfers.
- adi,2d: Must be set if the channel supports hardware 2D DMA transfers.
DMA clients connected to the AXI-DMAC DMA controller must use the format
described in the dma.txt file using a one-cell specifier. The value of the
specifier refers to the DMA channel index.
Example:
dma: dma@7c420000 {
compatible = "adi,axi-dmac-1.00.a";
reg = <0x7c420000 0x10000>;
interrupts = <0 57 0>;
clocks = <&clkc 16>;
#dma-cells = <1>;
adi,channels {
#size-cells = <0>;
#address-cells = <1>;
dma-channel@0 {
reg = <0>;
adi,source-bus-width = <32>;
adi,source-bus-type = <ADI_AXI_DMAC_TYPE_MM_AXI>;
adi,destination-bus-width = <64>;
adi,destination-bus-type = <ADI_AXI_DMAC_TYPE_FIFO>;
};
};
};

View File

@ -0,0 +1,129 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dma/adi,axi-dmac.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AXI-DMAC DMA controller
description: |
FPGA-based DMA controller designed for use with high-speed converter hardware.
http://analogdevicesinc.github.io/hdl/library/axi_dmac/index.html
maintainers:
- Nuno Sa <nuno.sa@analog.com>
additionalProperties: false
properties:
compatible:
const: adi,axi-dmac-1.00.a
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
"#dma-cells":
const: 1
adi,channels:
deprecated: true
type: object
description:
This sub-node must contain a sub-node for each DMA channel. This node is
only required for IP versions older than 4.3.a and should otherwise be
omitted.
additionalProperties: false
properties:
"#size-cells":
const: 0
"#address-cells":
const: 1
patternProperties:
"^dma-channel@[0-9a-f]+$":
type: object
description:
DMA channel properties based on HDL compile-time configuration.
additionalProperties: false
properties:
reg:
maxItems: 1
adi,source-bus-width:
$ref: /schemas/types.yaml#/definitions/uint32
description: Width of the source bus in bits.
enum: [8, 16, 32, 64, 128]
adi,destination-bus-width:
$ref: /schemas/types.yaml#/definitions/uint32
description: Width of the destination bus in bits.
enum: [8, 16, 32, 64, 128]
adi,source-bus-type:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Type of the source bus.
0: Memory mapped AXI interface
1: Streaming AXI interface
2: FIFO interface
enum: [0, 1, 2]
adi,destination-bus-type:
$ref: /schemas/types.yaml#/definitions/uint32
description: Type of the destination bus (see adi,source-bus-type).
enum: [0, 1, 2]
adi,length-width:
deprecated: true
$ref: /schemas/types.yaml#/definitions/uint32
description: Width of the DMA transfer length register.
adi,cyclic:
deprecated: true
type: boolean
description:
Must be set if the channel supports hardware cyclic DMA transfers.
adi,2d:
deprecated: true
type: boolean
description:
Must be set if the channel supports hardware 2D DMA transfers.
required:
- reg
- adi,source-bus-width
- adi,destination-bus-width
- adi,source-bus-type
- adi,destination-bus-type
required:
- "#size-cells"
- "#address-cells"
required:
- compatible
- reg
- interrupts
- clocks
- "#dma-cells"
examples:
- |
dma-controller@7c420000 {
compatible = "adi,axi-dmac-1.00.a";
reg = <0x7c420000 0x10000>;
interrupts = <0 57 0>;
clocks = <&clkc 16>;
#dma-cells = <1>;
};

View File

@ -22,7 +22,9 @@ properties:
number.
compatible:
const: allwinner,sun4i-a10-dma
enum:
- allwinner,sun4i-a10-dma
- allwinner,suniv-f1c100s-dma
reg:
maxItems: 1

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dma/atmel,sama5d4-dma.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip AT91 Extensible Direct Memory Access Controller
maintainers:
- Nicolas Ferre <nicolas.ferre@microchip.com>
- Charan Pedumuru <charan.pedumuru@microchip.com>
description:
The DMA Controller (XDMAC) is a AHB-protocol central direct memory access
controller. It performs peripheral data transfer and memory move operations
over one or two bus ports through the unidirectional communication
channel. Each channel is fully programmable and provides both peripheral
or memory-to-memory transfers. The channel features are configurable at
implementation.
allOf:
- $ref: dma-controller.yaml#
properties:
compatible:
oneOf:
- enum:
- atmel,sama5d4-dma
- microchip,sama7g5-dma
- items:
- enum:
- microchip,sam9x60-dma
- microchip,sam9x7-dma
- const: atmel,sama5d4-dma
"#dma-cells":
description: |
Represents the number of integer cells in the `dmas` property of client
devices. The single cell specifies the channel configuration register:
- bit 13: SIF (Source Interface Identifier) for memory interface.
- bit 14: DIF (Destination Interface Identifier) for peripheral interface.
- bit 30-24: PERID (Peripheral Identifier).
const: 1
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: dma_clk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- "#dma-cells"
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/dma/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
dma-controller@f0008000 {
compatible = "atmel,sama5d4-dma";
reg = <0xf0008000 0x1000>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH 0>;
#dma-cells = <1>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 20>;
clock-names = "dma_clk";
};

View File

@ -1,54 +0,0 @@
* Atmel Extensible Direct Memory Access Controller (XDMAC)
* XDMA Controller
Required properties:
- compatible: Should be "atmel,sama5d4-dma", "microchip,sam9x60-dma" or
"microchip,sama7g5-dma" or
"microchip,sam9x7-dma", "atmel,sama5d4-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
the dmas property of client devices.
- The 1st cell specifies the channel configuration register:
- bit 13: SIF, source interface identifier, used to get the memory
interface identifier,
- bit 14: DIF, destination interface identifier, used to get the peripheral
interface identifier,
- bit 30-24: PERID, peripheral identifier.
Example:
dma1: dma-controller@f0004000 {
compatible = "atmel,sama5d4-dma";
reg = <0xf0004000 0x200>;
interrupts = <50 4 0>;
#dma-cells = <1>;
};
* DMA clients
DMA clients connected to the Atmel XDMA controller must use the format
described in the dma.txt file, using a one-cell specifier for each channel.
The two cells in order are:
1. A phandle pointing to the DMA controller.
2. Channel configuration register. Configurable fields are:
- bit 13: SIF, source interface identifier, used to get the memory
interface identifier,
- bit 14: DIF, destination interface identifier, used to get the peripheral
interface identifier,
- bit 30-24: PERID, peripheral identifier.
Example:
i2c2: i2c@f8024000 {
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8024000 0x4000>;
interrupts = <34 4 6>;
dmas = <&dma1
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
| AT91_XDMAC_DT_PERID(6))>,
<&dma1
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
| AT91_XDMAC_DT_PERID(7))>;
dma-names = "tx", "rx";
};

View File

@ -26,9 +26,13 @@ properties:
- fsl,imx93-edma3
- fsl,imx93-edma4
- fsl,imx95-edma5
- nxp,s32g2-edma
- items:
- const: fsl,ls1028a-edma
- const: fsl,vf610-edma
- items:
- const: nxp,s32g3-edma
- const: nxp,s32g2-edma
reg:
minItems: 1
@ -221,6 +225,36 @@ allOf:
properties:
power-domains: false
- if:
properties:
compatible:
contains:
const: nxp,s32g2-edma
then:
properties:
clocks:
minItems: 2
maxItems: 2
clock-names:
items:
- const: dmamux0
- const: dmamux1
interrupts:
minItems: 3
maxItems: 3
interrupt-names:
items:
- const: tx-0-15
- const: tx-16-31
- const: err
reg:
minItems: 3
maxItems: 3
"#dma-cells":
const: 2
dma-channels:
const: 32
unevaluatedProperties: false
examples:

View File

@ -13,9 +13,6 @@ description: |
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
allOf:
- $ref: dma-controller.yaml#
properties:
compatible:
oneOf:
@ -29,7 +26,19 @@ properties:
- const: nvidia,tegra186-adma
reg:
maxItems: 1
description:
The 'page' region describes the address space of the page
used for accessing the DMA channel registers. The 'global'
region describes the address space of the global DMA registers.
In the absence of the 'reg-names' property, there must be a
single entry that covers the address space of the global DMA
registers and the DMA channel registers.
minItems: 1
maxItems: 2
reg-names:
minItems: 1
maxItems: 2
interrupts:
description: |
@ -63,6 +72,49 @@ required:
- clocks
- clock-names
allOf:
- $ref: dma-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra210-adma
then:
properties:
reg:
items:
- description: Full address space range of DMA registers.
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra186-adma
then:
anyOf:
- properties:
reg:
items:
- description: Full address space range of DMA registers.
- properties:
reg:
items:
- description: Channel Page address space range of DMA registers.
reg-names:
items:
- const: page
- properties:
reg:
items:
- description: Channel Page address space range of DMA registers.
- description: Global Page address space range of DMA registers.
reg-names:
items:
- const: page
- const: global
additionalProperties: false
examples:

View File

@ -25,7 +25,9 @@ properties:
- items:
- enum:
- qcom,qcm2290-gpi-dma
- qcom,qcs8300-gpi-dma
- qcom,qdu1000-gpi-dma
- qcom,sa8775p-gpi-dma
- qcom,sar2130p-gpi-dma
- qcom,sc7280-gpi-dma
- qcom,sdx75-gpi-dma
@ -35,10 +37,12 @@ properties:
- qcom,sm8450-gpi-dma
- qcom,sm8550-gpi-dma
- qcom,sm8650-gpi-dma
- qcom,sm8750-gpi-dma
- qcom,x1e80100-gpi-dma
- const: qcom,sm6350-gpi-dma
- items:
- enum:
- qcom,qcs615-gpi-dma
- qcom,sdm670-gpi-dma
- qcom,sm6125-gpi-dma
- qcom,sm8150-gpi-dma

View File

@ -15,6 +15,16 @@ allOf:
properties:
"#dma-cells":
const: 3
description: |
Each cell represents the following:
1. The mux input number/line for the request
2. Bitfield representing DMA channel configuration that is passed
to the real DMA controller
3. Bitfield representing device dependent DMA features passed to
the real DMA controller
For bitfield definitions of cells 2 and 3, see the associated
bindings doc for the actual DMA controller in st,stm32-dma.yaml.
compatible:
const: st,stm32h7-dmamux

View File

@ -34,6 +34,7 @@ properties:
- ti,am62a-dmss-bcdma-csirx
- ti,am64-dmss-bcdma
- ti,j721s2-dmss-bcdma-csi
- ti,j722s-dmss-bcdma-csi
reg:
minItems: 3
@ -196,7 +197,9 @@ allOf:
properties:
compatible:
contains:
const: ti,j721s2-dmss-bcdma-csi
enum:
- ti,j721s2-dmss-bcdma-csi
- ti,j722s-dmss-bcdma-csi
then:
properties:
ti,sci-rm-range-bchan: false

View File

@ -987,6 +987,12 @@ L: linux-edac@vger.kernel.org
S: Supported
F: drivers/ras/amd/atl/*
AMD AE4DMA DRIVER
M: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
L: dmaengine@vger.kernel.org
S: Supported
F: drivers/dma/amd/ae4dma/
AMD AXI W1 DRIVER
M: Kris Chaplin <kris.chaplin@amd.com>
R: Thomas Delev <thomas.delev@amd.com>
@ -1179,8 +1185,8 @@ F: tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py
AMD PTDMA DRIVER
M: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
L: dmaengine@vger.kernel.org
S: Maintained
F: drivers/dma/ptdma/
S: Supported
F: drivers/dma/amd/ptdma/
AMD QDMA DRIVER
M: Nishad Saraf <nishads@amd.com>

View File

@ -162,8 +162,8 @@ config DMA_SA11X0
config DMA_SUN4I
tristate "Allwinner A10 DMA SoCs support"
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I
default (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I)
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNIV
default (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNIV)
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
@ -740,8 +740,6 @@ source "drivers/dma/bestcomm/Kconfig"
source "drivers/dma/mediatek/Kconfig"
source "drivers/dma/ptdma/Kconfig"
source "drivers/dma/qcom/Kconfig"
source "drivers/dma/dw/Kconfig"

View File

@ -16,7 +16,6 @@ obj-$(CONFIG_DMATEST) += dmatest.o
obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
obj-$(CONFIG_AMD_PTDMA) += ptdma/
obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
obj-$(CONFIG_AT_XDMAC) += at_xdmac.o

View File

@ -1,4 +1,32 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config AMD_AE4DMA
tristate "AMD AE4DMA Engine"
depends on (X86_64 || COMPILE_TEST) && PCI
depends on AMD_PTDMA
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable support for the AMD AE4DMA controller. This controller
provides DMA capabilities to perform high bandwidth memory to
memory and IO copy operations. It performs DMA transfer through
queue-based descriptor management. This DMA controller is intended
to be used with AMD Non-Transparent Bridge devices and not for
general purpose peripheral DMA.
config AMD_PTDMA
tristate "AMD PassThru DMA Engine"
depends on X86_64 && PCI
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable support for the AMD PTDMA controller. This controller
provides DMA capabilities to perform high bandwidth memory to
memory and IO copy operations. It performs DMA transfer through
queue-based descriptor management. This DMA controller is intended
to be used with AMD Non-Transparent Bridge devices and not for
general purpose peripheral DMA.
config AMD_QDMA
tristate "AMD Queue-based DMA"

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_AMD_AE4DMA) += ae4dma/
obj-$(CONFIG_AMD_PTDMA) += ptdma/
obj-$(CONFIG_AMD_QDMA) += qdma/

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# AMD AE4DMA driver
#
obj-$(CONFIG_AMD_AE4DMA) += ae4dma.o
ae4dma-objs := ae4dma-dev.o
ae4dma-$(CONFIG_PCI) += ae4dma-pci.o

View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AMD AE4DMA driver
*
* Copyright (c) 2024, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include "ae4dma.h"
static unsigned int max_hw_q = 1;
module_param(max_hw_q, uint, 0444);
MODULE_PARM_DESC(max_hw_q, "max hw queues supported by engine (any non-zero value, default: 1)");
static void ae4_pending_work(struct work_struct *work)
{
struct ae4_cmd_queue *ae4cmd_q = container_of(work, struct ae4_cmd_queue, p_work.work);
struct pt_cmd_queue *cmd_q = &ae4cmd_q->cmd_q;
struct pt_cmd *cmd;
u32 cridx;
for (;;) {
wait_event_interruptible(ae4cmd_q->q_w,
((atomic64_read(&ae4cmd_q->done_cnt)) <
atomic64_read(&ae4cmd_q->intr_cnt)));
atomic64_inc(&ae4cmd_q->done_cnt);
mutex_lock(&ae4cmd_q->cmd_lock);
cridx = readl(cmd_q->reg_control + AE4_RD_IDX_OFF);
while ((ae4cmd_q->dridx != cridx) && !list_empty(&ae4cmd_q->cmd)) {
cmd = list_first_entry(&ae4cmd_q->cmd, struct pt_cmd, entry);
list_del(&cmd->entry);
ae4_check_status_error(ae4cmd_q, ae4cmd_q->dridx);
cmd->pt_cmd_callback(cmd->data, cmd->ret);
ae4cmd_q->q_cmd_count--;
ae4cmd_q->dridx = (ae4cmd_q->dridx + 1) % CMD_Q_LEN;
complete_all(&ae4cmd_q->cmp);
}
mutex_unlock(&ae4cmd_q->cmd_lock);
}
}
static irqreturn_t ae4_core_irq_handler(int irq, void *data)
{
struct ae4_cmd_queue *ae4cmd_q = data;
struct pt_cmd_queue *cmd_q;
struct pt_device *pt;
u32 status;
cmd_q = &ae4cmd_q->cmd_q;
pt = cmd_q->pt;
pt->total_interrupts++;
atomic64_inc(&ae4cmd_q->intr_cnt);
status = readl(cmd_q->reg_control + AE4_INTR_STS_OFF);
if (status & BIT(0)) {
status &= GENMASK(31, 1);
writel(status, cmd_q->reg_control + AE4_INTR_STS_OFF);
}
wake_up(&ae4cmd_q->q_w);
return IRQ_HANDLED;
}
void ae4_destroy_work(struct ae4_device *ae4)
{
struct ae4_cmd_queue *ae4cmd_q;
int i;
for (i = 0; i < ae4->cmd_q_count; i++) {
ae4cmd_q = &ae4->ae4cmd_q[i];
if (!ae4cmd_q->pws)
break;
cancel_delayed_work_sync(&ae4cmd_q->p_work);
destroy_workqueue(ae4cmd_q->pws);
}
}
int ae4_core_init(struct ae4_device *ae4)
{
struct pt_device *pt = &ae4->pt;
struct ae4_cmd_queue *ae4cmd_q;
struct device *dev = pt->dev;
struct pt_cmd_queue *cmd_q;
int i, ret = 0;
writel(max_hw_q, pt->io_regs);
for (i = 0; i < max_hw_q; i++) {
ae4cmd_q = &ae4->ae4cmd_q[i];
ae4cmd_q->id = ae4->cmd_q_count;
ae4->cmd_q_count++;
cmd_q = &ae4cmd_q->cmd_q;
cmd_q->pt = pt;
cmd_q->reg_control = pt->io_regs + ((i + 1) * AE4_Q_SZ);
ret = devm_request_irq(dev, ae4->ae4_irq[i], ae4_core_irq_handler, 0,
dev_name(pt->dev), ae4cmd_q);
if (ret)
return ret;
cmd_q->qsize = Q_SIZE(sizeof(struct ae4dma_desc));
cmd_q->qbase = dmam_alloc_coherent(dev, cmd_q->qsize, &cmd_q->qbase_dma,
GFP_KERNEL);
if (!cmd_q->qbase)
return -ENOMEM;
}
for (i = 0; i < ae4->cmd_q_count; i++) {
ae4cmd_q = &ae4->ae4cmd_q[i];
cmd_q = &ae4cmd_q->cmd_q;
cmd_q->reg_control = pt->io_regs + ((i + 1) * AE4_Q_SZ);
/* Update the device registers with queue information. */
writel(CMD_Q_LEN, cmd_q->reg_control + AE4_MAX_IDX_OFF);
cmd_q->qdma_tail = cmd_q->qbase_dma;
writel(lower_32_bits(cmd_q->qdma_tail), cmd_q->reg_control + AE4_Q_BASE_L_OFF);
writel(upper_32_bits(cmd_q->qdma_tail), cmd_q->reg_control + AE4_Q_BASE_H_OFF);
INIT_LIST_HEAD(&ae4cmd_q->cmd);
init_waitqueue_head(&ae4cmd_q->q_w);
ae4cmd_q->pws = alloc_ordered_workqueue("ae4dma_%d", WQ_MEM_RECLAIM, ae4cmd_q->id);
if (!ae4cmd_q->pws) {
ae4_destroy_work(ae4);
return -ENOMEM;
}
INIT_DELAYED_WORK(&ae4cmd_q->p_work, ae4_pending_work);
queue_delayed_work(ae4cmd_q->pws, &ae4cmd_q->p_work, usecs_to_jiffies(100));
init_completion(&ae4cmd_q->cmp);
}
ret = pt_dmaengine_register(pt);
if (ret)
ae4_destroy_work(ae4);
else
ptdma_debugfs_setup(pt);
return ret;
}

View File

@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AMD AE4DMA driver
*
* Copyright (c) 2024, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include "ae4dma.h"
static int ae4_get_irqs(struct ae4_device *ae4)
{
struct ae4_msix *ae4_msix = ae4->ae4_msix;
struct pt_device *pt = &ae4->pt;
struct device *dev = pt->dev;
struct pci_dev *pdev;
int i, v, ret;
pdev = to_pci_dev(dev);
for (v = 0; v < ARRAY_SIZE(ae4_msix->msix_entry); v++)
ae4_msix->msix_entry[v].entry = v;
ret = pci_alloc_irq_vectors(pdev, v, v, PCI_IRQ_MSIX);
if (ret != v) {
if (ret > 0)
pci_free_irq_vectors(pdev);
dev_err(dev, "could not enable MSI-X (%d), trying MSI\n", ret);
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (ret < 0) {
dev_err(dev, "could not enable MSI (%d)\n", ret);
return ret;
}
ret = pci_irq_vector(pdev, 0);
if (ret < 0) {
pci_free_irq_vectors(pdev);
return ret;
}
for (i = 0; i < MAX_AE4_HW_QUEUES; i++)
ae4->ae4_irq[i] = ret;
} else {
ae4_msix->msix_count = ret;
for (i = 0; i < MAX_AE4_HW_QUEUES; i++)
ae4->ae4_irq[i] = ae4_msix->msix_entry[i].vector;
}
return ret;
}
static void ae4_free_irqs(struct ae4_device *ae4)
{
struct ae4_msix *ae4_msix = ae4->ae4_msix;
struct pt_device *pt = &ae4->pt;
struct device *dev = pt->dev;
struct pci_dev *pdev;
pdev = to_pci_dev(dev);
if (ae4_msix && (ae4_msix->msix_count || ae4->ae4_irq[MAX_AE4_HW_QUEUES - 1]))
pci_free_irq_vectors(pdev);
}
static void ae4_deinit(struct ae4_device *ae4)
{
ae4_free_irqs(ae4);
}
static int ae4_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct ae4_device *ae4;
struct pt_device *pt;
int bar_mask;
int ret = 0;
ae4 = devm_kzalloc(dev, sizeof(*ae4), GFP_KERNEL);
if (!ae4)
return -ENOMEM;
ae4->ae4_msix = devm_kzalloc(dev, sizeof(struct ae4_msix), GFP_KERNEL);
if (!ae4->ae4_msix)
return -ENOMEM;
ret = pcim_enable_device(pdev);
if (ret)
goto ae4_error;
bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
ret = pcim_iomap_regions(pdev, bar_mask, "ae4dma");
if (ret)
goto ae4_error;
pt = &ae4->pt;
pt->dev = dev;
pt->ver = AE4_DMA_VERSION;
pt->io_regs = pcim_iomap_table(pdev)[0];
if (!pt->io_regs) {
ret = -ENOMEM;
goto ae4_error;
}
ret = ae4_get_irqs(ae4);
if (ret < 0)
goto ae4_error;
pci_set_master(pdev);
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
dev_set_drvdata(dev, ae4);
ret = ae4_core_init(ae4);
if (ret)
goto ae4_error;
return 0;
ae4_error:
ae4_deinit(ae4);
return ret;
}
static void ae4_pci_remove(struct pci_dev *pdev)
{
struct ae4_device *ae4 = dev_get_drvdata(&pdev->dev);
ae4_destroy_work(ae4);
ae4_deinit(ae4);
}
static const struct pci_device_id ae4_pci_table[] = {
{ PCI_VDEVICE(AMD, 0x14C8), },
{ PCI_VDEVICE(AMD, 0x14DC), },
{ PCI_VDEVICE(AMD, 0x149B), },
/* Last entry must be zero */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ae4_pci_table);
static struct pci_driver ae4_pci_driver = {
.name = "ae4dma",
.id_table = ae4_pci_table,
.probe = ae4_pci_probe,
.remove = ae4_pci_remove,
};
module_pci_driver(ae4_pci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AMD AE4DMA driver");

View File

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* AMD AE4DMA driver
*
* Copyright (c) 2024, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef __AE4DMA_H__
#define __AE4DMA_H__
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include "../ptdma/ptdma.h"
#include "../../virt-dma.h"
#define MAX_AE4_HW_QUEUES 16
#define AE4_DESC_COMPLETED 0x03
#define AE4_MAX_IDX_OFF 0x08
#define AE4_RD_IDX_OFF 0x0c
#define AE4_WR_IDX_OFF 0x10
#define AE4_INTR_STS_OFF 0x14
#define AE4_Q_BASE_L_OFF 0x18
#define AE4_Q_BASE_H_OFF 0x1c
#define AE4_Q_SZ 0x20
#define AE4_DMA_VERSION 4
#define CMD_AE4_DESC_DW0_VAL 2
struct ae4_msix {
int msix_count;
struct msix_entry msix_entry[MAX_AE4_HW_QUEUES];
};
struct ae4_cmd_queue {
struct ae4_device *ae4;
struct pt_cmd_queue cmd_q;
struct list_head cmd;
/* protect command operations */
struct mutex cmd_lock;
struct delayed_work p_work;
struct workqueue_struct *pws;
struct completion cmp;
wait_queue_head_t q_w;
atomic64_t intr_cnt;
atomic64_t done_cnt;
u64 q_cmd_count;
u32 dridx;
u32 tail_wi;
u32 id;
};
union dwou {
u32 dw0;
struct dword0 {
u8 byte0;
u8 byte1;
u16 timestamp;
} dws;
};
struct dword1 {
u8 status;
u8 err_code;
u16 desc_id;
};
struct ae4dma_desc {
union dwou dwouv;
struct dword1 dw1;
u32 length;
u32 rsvd;
u32 src_hi;
u32 src_lo;
u32 dst_hi;
u32 dst_lo;
};
struct ae4_device {
struct pt_device pt;
struct ae4_msix *ae4_msix;
struct ae4_cmd_queue ae4cmd_q[MAX_AE4_HW_QUEUES];
unsigned int ae4_irq[MAX_AE4_HW_QUEUES];
unsigned int cmd_q_count;
};
int ae4_core_init(struct ae4_device *ae4);
void ae4_destroy_work(struct ae4_device *ae4);
void ae4_check_status_error(struct ae4_cmd_queue *ae4cmd_q, int idx);
#endif

View File

@ -13,6 +13,7 @@
#include <linux/seq_file.h>
#include "ptdma.h"
#include "../ae4dma/ae4dma.h"
/* DebugFS helpers */
#define RI_VERSION_NUM 0x0000003F
@ -23,11 +24,19 @@
static int pt_debugfs_info_show(struct seq_file *s, void *p)
{
struct pt_device *pt = s->private;
struct ae4_device *ae4;
unsigned int regval;
seq_printf(s, "Device name: %s\n", dev_name(pt->dev));
seq_printf(s, " # Queues: %d\n", 1);
seq_printf(s, " # Cmds: %d\n", pt->cmd_count);
if (pt->ver == AE4_DMA_VERSION) {
ae4 = container_of(pt, struct ae4_device, pt);
seq_printf(s, " # Queues: %d\n", ae4->cmd_q_count);
seq_printf(s, " # Cmds per queue: %d\n", CMD_Q_LEN);
} else {
seq_printf(s, " # Queues: %d\n", 1);
seq_printf(s, " # Cmds: %d\n", pt->cmd_count);
}
regval = ioread32(pt->io_regs + CMD_PT_VERSION);
@ -55,6 +64,7 @@ static int pt_debugfs_stats_show(struct seq_file *s, void *p)
static int pt_debugfs_queue_show(struct seq_file *s, void *p)
{
struct pt_cmd_queue *cmd_q = s->private;
struct pt_device *pt;
unsigned int regval;
if (!cmd_q)
@ -62,18 +72,24 @@ static int pt_debugfs_queue_show(struct seq_file *s, void *p)
seq_printf(s, " Pass-Thru: %ld\n", cmd_q->total_pt_ops);
regval = ioread32(cmd_q->reg_control + 0x000C);
pt = cmd_q->pt;
if (pt->ver == AE4_DMA_VERSION) {
regval = readl(cmd_q->reg_control + 0x4);
seq_printf(s, " Enabled Interrupts:: status 0x%x\n", regval);
} else {
regval = ioread32(cmd_q->reg_control + 0x000C);
seq_puts(s, " Enabled Interrupts:");
if (regval & INT_EMPTY_QUEUE)
seq_puts(s, " EMPTY");
if (regval & INT_QUEUE_STOPPED)
seq_puts(s, " STOPPED");
if (regval & INT_ERROR)
seq_puts(s, " ERROR");
if (regval & INT_COMPLETION)
seq_puts(s, " COMPLETION");
seq_puts(s, "\n");
seq_puts(s, " Enabled Interrupts:");
if (regval & INT_EMPTY_QUEUE)
seq_puts(s, " EMPTY");
if (regval & INT_QUEUE_STOPPED)
seq_puts(s, " STOPPED");
if (regval & INT_ERROR)
seq_puts(s, " ERROR");
if (regval & INT_COMPLETION)
seq_puts(s, " COMPLETION");
seq_puts(s, "\n");
}
return 0;
}
@ -84,8 +100,12 @@ DEFINE_SHOW_ATTRIBUTE(pt_debugfs_stats);
void ptdma_debugfs_setup(struct pt_device *pt)
{
struct pt_cmd_queue *cmd_q;
struct dentry *debugfs_q_instance;
struct ae4_cmd_queue *ae4cmd_q;
struct pt_cmd_queue *cmd_q;
struct ae4_device *ae4;
char name[30];
int i;
if (!debugfs_initialized())
return;
@ -96,11 +116,28 @@ void ptdma_debugfs_setup(struct pt_device *pt)
debugfs_create_file("stats", 0400, pt->dma_dev.dbg_dev_root, pt,
&pt_debugfs_stats_fops);
cmd_q = &pt->cmd_q;
debugfs_q_instance =
debugfs_create_dir("q", pt->dma_dev.dbg_dev_root);
if (pt->ver == AE4_DMA_VERSION) {
ae4 = container_of(pt, struct ae4_device, pt);
for (i = 0; i < ae4->cmd_q_count; i++) {
ae4cmd_q = &ae4->ae4cmd_q[i];
cmd_q = &ae4cmd_q->cmd_q;
debugfs_create_file("stats", 0400, debugfs_q_instance, cmd_q,
&pt_debugfs_queue_fops);
memset(name, 0, sizeof(name));
snprintf(name, 29, "q%d", ae4cmd_q->id);
debugfs_q_instance =
debugfs_create_dir(name, pt->dma_dev.dbg_dev_root);
debugfs_create_file("stats", 0400, debugfs_q_instance, cmd_q,
&pt_debugfs_queue_fops);
}
} else {
debugfs_q_instance =
debugfs_create_dir("q", pt->dma_dev.dbg_dev_root);
cmd_q = &pt->cmd_q;
debugfs_create_file("stats", 0400, debugfs_q_instance, cmd_q,
&pt_debugfs_queue_fops);
}
}
EXPORT_SYMBOL_GPL(ptdma_debugfs_setup);

View File

@ -9,9 +9,58 @@
* Author: Gary R Hook <gary.hook@amd.com>
*/
#include <linux/bitfield.h>
#include "ptdma.h"
#include "../dmaengine.h"
#include "../virt-dma.h"
#include "../ae4dma/ae4dma.h"
#include "../../dmaengine.h"
static char *ae4_error_codes[] = {
"",
"ERR 01: INVALID HEADER DW0",
"ERR 02: INVALID STATUS",
"ERR 03: INVALID LENGTH - 4 BYTE ALIGNMENT",
"ERR 04: INVALID SRC ADDR - 4 BYTE ALIGNMENT",
"ERR 05: INVALID DST ADDR - 4 BYTE ALIGNMENT",
"ERR 06: INVALID ALIGNMENT",
"ERR 07: INVALID DESCRIPTOR",
};
static void ae4_log_error(struct pt_device *d, int e)
{
/* ERR 01 - 07 represents Invalid AE4 errors */
if (e <= 7)
dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", ae4_error_codes[e], e);
/* ERR 08 - 15 represents Invalid Descriptor errors */
else if (e > 7 && e <= 15)
dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "INVALID DESCRIPTOR", e);
/* ERR 16 - 31 represents Firmware errors */
else if (e > 15 && e <= 31)
dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "FIRMWARE ERROR", e);
/* ERR 32 - 63 represents Fatal errors */
else if (e > 31 && e <= 63)
dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "FATAL ERROR", e);
/* ERR 64 - 255 represents PTE errors */
else if (e > 63 && e <= 255)
dev_info(d->dev, "AE4DMA error: %s (0x%x)\n", "PTE ERROR", e);
else
dev_info(d->dev, "Unknown AE4DMA error");
}
void ae4_check_status_error(struct ae4_cmd_queue *ae4cmd_q, int idx)
{
struct pt_cmd_queue *cmd_q = &ae4cmd_q->cmd_q;
struct ae4dma_desc desc;
u8 status;
memcpy(&desc, &cmd_q->qbase[idx], sizeof(struct ae4dma_desc));
status = desc.dw1.status;
if (status && status != AE4_DESC_COMPLETED) {
cmd_q->cmd_error = desc.dw1.err_code;
if (cmd_q->cmd_error)
ae4_log_error(cmd_q->pt, cmd_q->cmd_error);
}
}
EXPORT_SYMBOL_GPL(ae4_check_status_error);
static inline struct pt_dma_chan *to_pt_chan(struct dma_chan *dma_chan)
{
@ -45,7 +94,71 @@ static void pt_do_cleanup(struct virt_dma_desc *vd)
kmem_cache_free(pt->dma_desc_cache, desc);
}
static int pt_dma_start_desc(struct pt_dma_desc *desc)
static struct pt_cmd_queue *pt_get_cmd_queue(struct pt_device *pt, struct pt_dma_chan *chan)
{
struct ae4_cmd_queue *ae4cmd_q;
struct pt_cmd_queue *cmd_q;
struct ae4_device *ae4;
if (pt->ver == AE4_DMA_VERSION) {
ae4 = container_of(pt, struct ae4_device, pt);
ae4cmd_q = &ae4->ae4cmd_q[chan->id];
cmd_q = &ae4cmd_q->cmd_q;
} else {
cmd_q = &pt->cmd_q;
}
return cmd_q;
}
static int ae4_core_execute_cmd(struct ae4dma_desc *desc, struct ae4_cmd_queue *ae4cmd_q)
{
bool soc = FIELD_GET(DWORD0_SOC, desc->dwouv.dw0);
struct pt_cmd_queue *cmd_q = &ae4cmd_q->cmd_q;
if (soc) {
desc->dwouv.dw0 |= FIELD_PREP(DWORD0_IOC, desc->dwouv.dw0);
desc->dwouv.dw0 &= ~DWORD0_SOC;
}
mutex_lock(&ae4cmd_q->cmd_lock);
memcpy(&cmd_q->qbase[ae4cmd_q->tail_wi], desc, sizeof(struct ae4dma_desc));
ae4cmd_q->q_cmd_count++;
ae4cmd_q->tail_wi = (ae4cmd_q->tail_wi + 1) % CMD_Q_LEN;
writel(ae4cmd_q->tail_wi, cmd_q->reg_control + AE4_WR_IDX_OFF);
mutex_unlock(&ae4cmd_q->cmd_lock);
wake_up(&ae4cmd_q->q_w);
return 0;
}
static int pt_core_perform_passthru_ae4(struct pt_cmd_queue *cmd_q,
struct pt_passthru_engine *pt_engine)
{
struct ae4_cmd_queue *ae4cmd_q = container_of(cmd_q, struct ae4_cmd_queue, cmd_q);
struct ae4dma_desc desc;
cmd_q->cmd_error = 0;
cmd_q->total_pt_ops++;
memset(&desc, 0, sizeof(desc));
desc.dwouv.dws.byte0 = CMD_AE4_DESC_DW0_VAL;
desc.dw1.status = 0;
desc.dw1.err_code = 0;
desc.dw1.desc_id = 0;
desc.length = pt_engine->src_len;
desc.src_lo = upper_32_bits(pt_engine->src_dma);
desc.src_hi = lower_32_bits(pt_engine->src_dma);
desc.dst_lo = upper_32_bits(pt_engine->dst_dma);
desc.dst_hi = lower_32_bits(pt_engine->dst_dma);
return ae4_core_execute_cmd(&desc, ae4cmd_q);
}
static int pt_dma_start_desc(struct pt_dma_desc *desc, struct pt_dma_chan *chan)
{
struct pt_passthru_engine *pt_engine;
struct pt_device *pt;
@ -56,13 +169,18 @@ static int pt_dma_start_desc(struct pt_dma_desc *desc)
pt_cmd = &desc->pt_cmd;
pt = pt_cmd->pt;
cmd_q = &pt->cmd_q;
cmd_q = pt_get_cmd_queue(pt, chan);
pt_engine = &pt_cmd->passthru;
pt->tdata.cmd = pt_cmd;
/* Execute the command */
pt_cmd->ret = pt_core_perform_passthru(cmd_q, pt_engine);
if (pt->ver == AE4_DMA_VERSION)
pt_cmd->ret = pt_core_perform_passthru_ae4(cmd_q, pt_engine);
else
pt_cmd->ret = pt_core_perform_passthru(cmd_q, pt_engine);
return 0;
}
@ -151,7 +269,7 @@ static void pt_cmd_callback(void *data, int err)
if (!desc)
break;
ret = pt_dma_start_desc(desc);
ret = pt_dma_start_desc(desc, chan);
if (!ret)
break;
@ -186,7 +304,10 @@ static struct pt_dma_desc *pt_create_desc(struct dma_chan *dma_chan,
{
struct pt_dma_chan *chan = to_pt_chan(dma_chan);
struct pt_passthru_engine *pt_engine;
struct pt_device *pt = chan->pt;
struct ae4_cmd_queue *ae4cmd_q;
struct pt_dma_desc *desc;
struct ae4_device *ae4;
struct pt_cmd *pt_cmd;
desc = pt_alloc_dma_desc(chan, flags);
@ -194,7 +315,7 @@ static struct pt_dma_desc *pt_create_desc(struct dma_chan *dma_chan,
return NULL;
pt_cmd = &desc->pt_cmd;
pt_cmd->pt = chan->pt;
pt_cmd->pt = pt;
pt_engine = &pt_cmd->passthru;
pt_cmd->engine = PT_ENGINE_PASSTHRU;
pt_engine->src_dma = src;
@ -205,6 +326,14 @@ static struct pt_dma_desc *pt_create_desc(struct dma_chan *dma_chan,
desc->len = len;
if (pt->ver == AE4_DMA_VERSION) {
ae4 = container_of(pt, struct ae4_device, pt);
ae4cmd_q = &ae4->ae4cmd_q[chan->id];
mutex_lock(&ae4cmd_q->cmd_lock);
list_add_tail(&pt_cmd->entry, &ae4cmd_q->cmd);
mutex_unlock(&ae4cmd_q->cmd_lock);
}
return desc;
}
@ -258,24 +387,43 @@ static void pt_issue_pending(struct dma_chan *dma_chan)
pt_cmd_callback(desc, 0);
}
static void pt_check_status_trans_ae4(struct pt_device *pt, struct pt_cmd_queue *cmd_q)
{
struct ae4_cmd_queue *ae4cmd_q = container_of(cmd_q, struct ae4_cmd_queue, cmd_q);
int i;
for (i = 0; i < CMD_Q_LEN; i++)
ae4_check_status_error(ae4cmd_q, i);
}
static enum dma_status
pt_tx_status(struct dma_chan *c, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct pt_device *pt = to_pt_chan(c)->pt;
struct pt_cmd_queue *cmd_q = &pt->cmd_q;
struct pt_dma_chan *chan = to_pt_chan(c);
struct pt_device *pt = chan->pt;
struct pt_cmd_queue *cmd_q;
cmd_q = pt_get_cmd_queue(pt, chan);
if (pt->ver == AE4_DMA_VERSION)
pt_check_status_trans_ae4(pt, cmd_q);
else
pt_check_status_trans(pt, cmd_q);
pt_check_status_trans(pt, cmd_q);
return dma_cookie_status(c, cookie, txstate);
}
static int pt_pause(struct dma_chan *dma_chan)
{
struct pt_dma_chan *chan = to_pt_chan(dma_chan);
struct pt_device *pt = chan->pt;
struct pt_cmd_queue *cmd_q;
unsigned long flags;
spin_lock_irqsave(&chan->vc.lock, flags);
pt_stop_queue(&chan->pt->cmd_q);
cmd_q = pt_get_cmd_queue(pt, chan);
pt_stop_queue(cmd_q);
spin_unlock_irqrestore(&chan->vc.lock, flags);
return 0;
@ -285,10 +433,13 @@ static int pt_resume(struct dma_chan *dma_chan)
{
struct pt_dma_chan *chan = to_pt_chan(dma_chan);
struct pt_dma_desc *desc = NULL;
struct pt_device *pt = chan->pt;
struct pt_cmd_queue *cmd_q;
unsigned long flags;
spin_lock_irqsave(&chan->vc.lock, flags);
pt_start_queue(&chan->pt->cmd_q);
cmd_q = pt_get_cmd_queue(pt, chan);
pt_start_queue(cmd_q);
desc = pt_next_dma_desc(chan);
spin_unlock_irqrestore(&chan->vc.lock, flags);
@ -302,11 +453,17 @@ static int pt_resume(struct dma_chan *dma_chan)
static int pt_terminate_all(struct dma_chan *dma_chan)
{
struct pt_dma_chan *chan = to_pt_chan(dma_chan);
struct pt_device *pt = chan->pt;
struct pt_cmd_queue *cmd_q;
unsigned long flags;
struct pt_cmd_queue *cmd_q = &chan->pt->cmd_q;
LIST_HEAD(head);
iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010);
cmd_q = pt_get_cmd_queue(pt, chan);
if (pt->ver == AE4_DMA_VERSION)
pt_stop_queue(cmd_q);
else
iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010);
spin_lock_irqsave(&chan->vc.lock, flags);
vchan_get_all_descriptors(&chan->vc, &head);
spin_unlock_irqrestore(&chan->vc.lock, flags);
@ -319,14 +476,24 @@ static int pt_terminate_all(struct dma_chan *dma_chan)
int pt_dmaengine_register(struct pt_device *pt)
{
struct pt_dma_chan *chan;
struct dma_device *dma_dev = &pt->dma_dev;
char *cmd_cache_name;
struct ae4_cmd_queue *ae4cmd_q = NULL;
struct ae4_device *ae4 = NULL;
struct pt_dma_chan *chan;
char *desc_cache_name;
int ret;
char *cmd_cache_name;
int ret, i;
if (pt->ver == AE4_DMA_VERSION)
ae4 = container_of(pt, struct ae4_device, pt);
if (ae4)
pt->pt_dma_chan = devm_kcalloc(pt->dev, ae4->cmd_q_count,
sizeof(*pt->pt_dma_chan), GFP_KERNEL);
else
pt->pt_dma_chan = devm_kzalloc(pt->dev, sizeof(*pt->pt_dma_chan),
GFP_KERNEL);
pt->pt_dma_chan = devm_kzalloc(pt->dev, sizeof(*pt->pt_dma_chan),
GFP_KERNEL);
if (!pt->pt_dma_chan)
return -ENOMEM;
@ -368,9 +535,6 @@ int pt_dmaengine_register(struct pt_device *pt)
INIT_LIST_HEAD(&dma_dev->channels);
chan = pt->pt_dma_chan;
chan->pt = pt;
/* Set base and prep routines */
dma_dev->device_free_chan_resources = pt_free_chan_resources;
dma_dev->device_prep_dma_memcpy = pt_prep_dma_memcpy;
@ -382,8 +546,21 @@ int pt_dmaengine_register(struct pt_device *pt)
dma_dev->device_terminate_all = pt_terminate_all;
dma_dev->device_synchronize = pt_synchronize;
chan->vc.desc_free = pt_do_cleanup;
vchan_init(&chan->vc, dma_dev);
if (ae4) {
for (i = 0; i < ae4->cmd_q_count; i++) {
chan = pt->pt_dma_chan + i;
ae4cmd_q = &ae4->ae4cmd_q[i];
chan->id = ae4cmd_q->id;
chan->pt = pt;
chan->vc.desc_free = pt_do_cleanup;
vchan_init(&chan->vc, dma_dev);
}
} else {
chan = pt->pt_dma_chan;
chan->pt = pt;
chan->vc.desc_free = pt_do_cleanup;
vchan_init(&chan->vc, dma_dev);
}
ret = dma_async_device_register(dma_dev);
if (ret)
@ -399,6 +576,7 @@ err_cache:
return ret;
}
EXPORT_SYMBOL_GPL(pt_dmaengine_register);
void pt_dmaengine_unregister(struct pt_device *pt)
{

View File

@ -22,7 +22,7 @@
#include <linux/wait.h>
#include <linux/dmapool.h>
#include "../virt-dma.h"
#include "../../virt-dma.h"
#define MAX_PT_NAME_LEN 16
#define MAX_DMAPOOL_NAME_LEN 32
@ -184,6 +184,7 @@ struct pt_dma_desc {
struct pt_dma_chan {
struct virt_dma_chan vc;
struct pt_device *pt;
u32 id;
};
struct pt_cmd_queue {
@ -262,6 +263,7 @@ struct pt_device {
unsigned long total_interrupts;
struct pt_tasklet_data tdata;
int ver;
};
/*

View File

@ -283,16 +283,20 @@ static int qdma_check_queue_status(struct qdma_device *qdev,
static int qdma_clear_queue_context(const struct qdma_queue *queue)
{
enum qdma_ctxt_type h2c_types[] = { QDMA_CTXT_DESC_SW_H2C,
QDMA_CTXT_DESC_HW_H2C,
QDMA_CTXT_DESC_CR_H2C,
QDMA_CTXT_PFTCH, };
enum qdma_ctxt_type c2h_types[] = { QDMA_CTXT_DESC_SW_C2H,
QDMA_CTXT_DESC_HW_C2H,
QDMA_CTXT_DESC_CR_C2H,
QDMA_CTXT_PFTCH, };
static const enum qdma_ctxt_type h2c_types[] = {
QDMA_CTXT_DESC_SW_H2C,
QDMA_CTXT_DESC_HW_H2C,
QDMA_CTXT_DESC_CR_H2C,
QDMA_CTXT_PFTCH,
};
static const enum qdma_ctxt_type c2h_types[] = {
QDMA_CTXT_DESC_SW_C2H,
QDMA_CTXT_DESC_HW_C2H,
QDMA_CTXT_DESC_CR_C2H,
QDMA_CTXT_PFTCH,
};
struct qdma_device *qdev = queue->qdev;
enum qdma_ctxt_type *type;
const enum qdma_ctxt_type *type;
int ret, num, i;
if (queue->dir == DMA_MEM_TO_DEV) {

View File

@ -875,6 +875,27 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
return chan;
}
static int bcm2835_dma_suspend_late(struct device *dev)
{
struct bcm2835_dmadev *od = dev_get_drvdata(dev);
struct bcm2835_chan *c, *next;
list_for_each_entry_safe(c, next, &od->ddev.channels,
vc.chan.device_node) {
void __iomem *chan_base = c->chan_base;
/* Check if DMA channel is busy */
if (readl(chan_base + BCM2835_DMA_ADDR))
return -EBUSY;
}
return 0;
}
static const struct dev_pm_ops bcm2835_dma_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(bcm2835_dma_suspend_late, NULL)
};
static int bcm2835_dma_probe(struct platform_device *pdev)
{
struct bcm2835_dmadev *od;
@ -1033,6 +1054,7 @@ static struct platform_driver bcm2835_dma_driver = {
.driver = {
.name = "bcm2835-dma",
.of_match_table = of_match_ptr(bcm2835_dma_of_match),
.pm = pm_ptr(&bcm2835_dma_pm_ops),
},
};

View File

@ -480,8 +480,8 @@ void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan,
bool disable_req, bool enable_sg)
{
struct dma_slave_config *cfg = &fsl_chan->cfg;
u32 burst = 0;
u16 csr = 0;
u32 burst;
/*
* eDMA hardware SGs require the TCDs to be stored in little
@ -496,16 +496,30 @@ void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan,
fsl_edma_set_tcd_to_le(fsl_chan, tcd, soff, soff);
if (fsl_chan->is_multi_fifo) {
/* set mloff to support multiple fifo */
burst = cfg->direction == DMA_DEV_TO_MEM ?
cfg->src_maxburst : cfg->dst_maxburst;
nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-(burst * 4));
/* enable DMLOE/SMLOE */
if (cfg->direction == DMA_MEM_TO_DEV) {
/* If we expect to have either multi_fifo or a port window size,
* we will use minor loop offset, meaning bits 29-10 will be used for
* address offset, while bits 9-0 will be used to tell DMA how much
* data to read from addr.
* If we don't have either of those, will use a major loop reading from addr
* nbytes (29bits).
*/
if (cfg->direction == DMA_MEM_TO_DEV) {
if (fsl_chan->is_multi_fifo)
burst = cfg->dst_maxburst * 4;
if (cfg->dst_port_window_size)
burst = cfg->dst_port_window_size * cfg->dst_addr_width;
if (burst) {
nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-burst);
nbytes |= EDMA_V3_TCD_NBYTES_DMLOE;
nbytes &= ~EDMA_V3_TCD_NBYTES_SMLOE;
} else {
}
} else {
if (fsl_chan->is_multi_fifo)
burst = cfg->src_maxburst * 4;
if (cfg->src_port_window_size)
burst = cfg->src_port_window_size * cfg->src_addr_width;
if (burst) {
nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-burst);
nbytes |= EDMA_V3_TCD_NBYTES_SMLOE;
nbytes &= ~EDMA_V3_TCD_NBYTES_DMLOE;
}
@ -623,11 +637,15 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = fsl_chan->is_multi_fifo ? 4 : 0;
if (fsl_chan->cfg.dst_port_window_size)
doff = fsl_chan->cfg.dst_addr_width;
} else if (direction == DMA_DEV_TO_MEM) {
src_addr = fsl_chan->dma_dev_addr;
dst_addr = dma_buf_next;
soff = fsl_chan->is_multi_fifo ? 4 : 0;
doff = fsl_chan->cfg.src_addr_width;
if (fsl_chan->cfg.src_port_window_size)
soff = fsl_chan->cfg.src_addr_width;
} else {
/* DMA_DEV_TO_DEV */
src_addr = fsl_chan->cfg.src_addr;

View File

@ -68,6 +68,8 @@
#define EDMA_V3_CH_CSR_EEI BIT(2)
#define EDMA_V3_CH_CSR_DONE BIT(30)
#define EDMA_V3_CH_CSR_ACTIVE BIT(31)
#define EDMA_V3_CH_ES_ERR BIT(31)
#define EDMA_V3_MP_ES_VLD BIT(31)
enum fsl_edma_pm_state {
RUNNING = 0,
@ -241,6 +243,7 @@ struct fsl_edma_engine {
const struct fsl_edma_drvdata *drvdata;
u32 n_chans;
int txirq;
int txirq_16_31;
int errirq;
bool big_endian;
struct edma_regs regs;

View File

@ -3,10 +3,11 @@
* drivers/dma/fsl-edma.c
*
* Copyright 2013-2014 Freescale Semiconductor, Inc.
* Copyright 2024 NXP
*
* Driver for the Freescale eDMA engine with flexible channel multiplexing
* capability for DMA request sources. The eDMA block can be found on some
* Vybrid and Layerscape SoCs.
* Vybrid, Layerscape and S32G SoCs.
*/
#include <dt-bindings/dma/fsl-edma.h>
@ -72,6 +73,60 @@ static irqreturn_t fsl_edma2_tx_handler(int irq, void *devi_id)
return fsl_edma_tx_handler(irq, fsl_chan->edma);
}
static irqreturn_t fsl_edma3_or_tx_handler(int irq, void *dev_id,
u8 start, u8 end)
{
struct fsl_edma_engine *fsl_edma = dev_id;
struct fsl_edma_chan *chan;
int i;
end = min(end, fsl_edma->n_chans);
for (i = start; i < end; i++) {
chan = &fsl_edma->chans[i];
fsl_edma3_tx_handler(irq, chan);
}
return IRQ_HANDLED;
}
static irqreturn_t fsl_edma3_tx_0_15_handler(int irq, void *dev_id)
{
return fsl_edma3_or_tx_handler(irq, dev_id, 0, 16);
}
static irqreturn_t fsl_edma3_tx_16_31_handler(int irq, void *dev_id)
{
return fsl_edma3_or_tx_handler(irq, dev_id, 16, 32);
}
static irqreturn_t fsl_edma3_or_err_handler(int irq, void *dev_id)
{
struct fsl_edma_engine *fsl_edma = dev_id;
struct edma_regs *regs = &fsl_edma->regs;
unsigned int err, ch, ch_es;
struct fsl_edma_chan *chan;
err = edma_readl(fsl_edma, regs->es);
if (!(err & EDMA_V3_MP_ES_VLD))
return IRQ_NONE;
for (ch = 0; ch < fsl_edma->n_chans; ch++) {
chan = &fsl_edma->chans[ch];
ch_es = edma_readl_chreg(chan, ch_es);
if (!(ch_es & EDMA_V3_CH_ES_ERR))
continue;
edma_writel_chreg(chan, EDMA_V3_CH_ES_ERR, ch_es);
fsl_edma_disable_request(chan);
fsl_edma->chans[ch].status = DMA_ERROR;
}
return IRQ_HANDLED;
}
static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
{
struct fsl_edma_engine *fsl_edma = dev_id;
@ -274,6 +329,49 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
return 0;
}
static int fsl_edma3_or_irq_init(struct platform_device *pdev,
struct fsl_edma_engine *fsl_edma)
{
int ret;
fsl_edma->txirq = platform_get_irq_byname(pdev, "tx-0-15");
if (fsl_edma->txirq < 0)
return fsl_edma->txirq;
fsl_edma->txirq_16_31 = platform_get_irq_byname(pdev, "tx-16-31");
if (fsl_edma->txirq_16_31 < 0)
return fsl_edma->txirq_16_31;
fsl_edma->errirq = platform_get_irq_byname(pdev, "err");
if (fsl_edma->errirq < 0)
return fsl_edma->errirq;
ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
fsl_edma3_tx_0_15_handler, 0, "eDMA tx0_15",
fsl_edma);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't register eDMA tx0_15 IRQ.\n");
if (fsl_edma->n_chans > 16) {
ret = devm_request_irq(&pdev->dev, fsl_edma->txirq_16_31,
fsl_edma3_tx_16_31_handler, 0,
"eDMA tx16_31", fsl_edma);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't register eDMA tx16_31 IRQ.\n");
}
ret = devm_request_irq(&pdev->dev, fsl_edma->errirq,
fsl_edma3_or_err_handler, 0, "eDMA err",
fsl_edma);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't register eDMA err IRQ.\n");
return 0;
}
static int
fsl_edma2_irq_init(struct platform_device *pdev,
struct fsl_edma_engine *fsl_edma)
@ -404,6 +502,14 @@ static struct fsl_edma_drvdata imx95_data5 = {
.setup_irq = fsl_edma3_irq_init,
};
static const struct fsl_edma_drvdata s32g2_data = {
.dmamuxs = DMAMUX_NR,
.chreg_space_sz = EDMA_TCD,
.chreg_off = 0x4000,
.flags = FSL_EDMA_DRV_EDMA3 | FSL_EDMA_DRV_MUX_SWAP,
.setup_irq = fsl_edma3_or_irq_init,
};
static const struct of_device_id fsl_edma_dt_ids[] = {
{ .compatible = "fsl,vf610-edma", .data = &vf610_data},
{ .compatible = "fsl,ls1028a-edma", .data = &ls1028a_data},
@ -413,6 +519,7 @@ static const struct of_device_id fsl_edma_dt_ids[] = {
{ .compatible = "fsl,imx93-edma3", .data = &imx93_data3},
{ .compatible = "fsl,imx93-edma4", .data = &imx93_data4},
{ .compatible = "fsl,imx95-edma5", .data = &imx95_data5},
{ .compatible = "nxp,s32g2-edma", .data = &s32g2_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
@ -545,10 +652,6 @@ static int fsl_edma_probe(struct platform_device *pdev)
for (i = 0; i < fsl_edma->drvdata->dmamuxs; i++) {
char clkname[32];
/* eDMAv3 mux register move to TCD area if ch_mux exist */
if (drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)
break;
fsl_edma->muxbase[i] = devm_platform_ioremap_resource(pdev,
1 + i);
if (IS_ERR(fsl_edma->muxbase[i])) {
@ -677,7 +780,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
}
ret = of_dma_controller_register(np,
drvdata->flags & FSL_EDMA_DRV_SPLIT_REG ? fsl_edma3_xlate : fsl_edma_xlate,
drvdata->dmamuxs ? fsl_edma_xlate : fsl_edma3_xlate,
fsl_edma);
if (ret) {
dev_err(&pdev->dev,

View File

@ -28,7 +28,6 @@ struct idxd_cdev_context {
* global to avoid conflict file names.
*/
static DEFINE_IDA(file_ida);
static DEFINE_MUTEX(ida_lock);
/*
* ictx is an array based off of accelerator types. enum idxd_type
@ -123,9 +122,7 @@ static void idxd_file_dev_release(struct device *dev)
struct idxd_device *idxd = wq->idxd;
int rc;
mutex_lock(&ida_lock);
ida_free(&file_ida, ctx->id);
mutex_unlock(&ida_lock);
/* Wait for in-flight operations to complete. */
if (wq_shared(wq)) {
@ -284,9 +281,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
}
idxd_cdev = wq->idxd_cdev;
mutex_lock(&ida_lock);
ctx->id = ida_alloc(&file_ida, GFP_KERNEL);
mutex_unlock(&ida_lock);
if (ctx->id < 0) {
dev_warn(dev, "ida alloc failure\n");
goto failed_ida;

View File

@ -374,6 +374,17 @@ struct idxd_device {
struct dentry *dbgfs_evl_file;
bool user_submission_safe;
struct idxd_saved_states *idxd_saved;
};
struct idxd_saved_states {
struct idxd_device saved_idxd;
struct idxd_evl saved_evl;
struct idxd_engine **saved_engines;
struct idxd_wq **saved_wqs;
struct idxd_group **saved_groups;
unsigned long *saved_wq_enable_map;
};
static inline unsigned int evl_ent_size(struct idxd_device *idxd)
@ -725,8 +736,6 @@ static inline void idxd_desc_complete(struct idxd_desc *desc,
&desc->txd, &status);
}
int idxd_register_bus_type(void);
void idxd_unregister_bus_type(void);
int idxd_register_devices(struct idxd_device *idxd);
void idxd_unregister_devices(struct idxd_device *idxd);
void idxd_wqs_quiesce(struct idxd_device *idxd);
@ -742,6 +751,8 @@ void idxd_unmask_error_interrupts(struct idxd_device *idxd);
/* device control */
int idxd_device_drv_probe(struct idxd_dev *idxd_dev);
int idxd_pci_probe_alloc(struct idxd_device *idxd, struct pci_dev *pdev,
const struct pci_device_id *id);
void idxd_device_drv_remove(struct idxd_dev *idxd_dev);
int idxd_drv_enable_wq(struct idxd_wq *wq);
void idxd_drv_disable_wq(struct idxd_wq *wq);

View File

@ -78,6 +78,8 @@ static struct pci_device_id idxd_pci_tbl[] = {
{ PCI_DEVICE_DATA(INTEL, IAX_SPR0, &idxd_driver_data[IDXD_TYPE_IAX]) },
/* IAA on DMR platforms */
{ PCI_DEVICE_DATA(INTEL, IAA_DMR, &idxd_driver_data[IDXD_TYPE_IAX]) },
/* IAA PTL platforms */
{ PCI_DEVICE_DATA(INTEL, IAA_PTL, &idxd_driver_data[IDXD_TYPE_IAX]) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, idxd_pci_tbl);
@ -723,67 +725,464 @@ static void idxd_cleanup(struct idxd_device *idxd)
idxd_disable_sva(idxd->pdev);
}
static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/*
* Attach IDXD device to IDXD driver.
*/
static int idxd_bind(struct device_driver *drv, const char *buf)
{
struct device *dev = &pdev->dev;
struct idxd_device *idxd;
struct idxd_driver_data *data = (struct idxd_driver_data *)id->driver_data;
const struct bus_type *bus = drv->bus;
struct device *dev;
int err = -ENODEV;
dev = bus_find_device_by_name(bus, NULL, buf);
if (dev)
err = device_driver_attach(drv, dev);
put_device(dev);
return err;
}
/*
* Detach IDXD device from driver.
*/
static void idxd_unbind(struct device_driver *drv, const char *buf)
{
const struct bus_type *bus = drv->bus;
struct device *dev;
dev = bus_find_device_by_name(bus, NULL, buf);
if (dev && dev->driver == drv)
device_release_driver(dev);
put_device(dev);
}
#define idxd_free_saved_configs(saved_configs, count) \
do { \
int i; \
\
for (i = 0; i < (count); i++) \
kfree(saved_configs[i]); \
} while (0)
static void idxd_free_saved(struct idxd_group **saved_groups,
struct idxd_engine **saved_engines,
struct idxd_wq **saved_wqs,
struct idxd_device *idxd)
{
if (saved_groups)
idxd_free_saved_configs(saved_groups, idxd->max_groups);
if (saved_engines)
idxd_free_saved_configs(saved_engines, idxd->max_engines);
if (saved_wqs)
idxd_free_saved_configs(saved_wqs, idxd->max_wqs);
}
/*
* Save IDXD device configurations including engines, groups, wqs etc.
* The saved configurations can be restored when needed.
*/
static int idxd_device_config_save(struct idxd_device *idxd,
struct idxd_saved_states *idxd_saved)
{
struct device *dev = &idxd->pdev->dev;
int i;
memcpy(&idxd_saved->saved_idxd, idxd, sizeof(*idxd));
if (idxd->evl) {
memcpy(&idxd_saved->saved_evl, idxd->evl,
sizeof(struct idxd_evl));
}
struct idxd_group **saved_groups __free(kfree) =
kcalloc_node(idxd->max_groups,
sizeof(struct idxd_group *),
GFP_KERNEL, dev_to_node(dev));
if (!saved_groups)
return -ENOMEM;
for (i = 0; i < idxd->max_groups; i++) {
struct idxd_group *saved_group __free(kfree) =
kzalloc_node(sizeof(*saved_group), GFP_KERNEL,
dev_to_node(dev));
if (!saved_group) {
/* Free saved groups */
idxd_free_saved(saved_groups, NULL, NULL, idxd);
return -ENOMEM;
}
memcpy(saved_group, idxd->groups[i], sizeof(*saved_group));
saved_groups[i] = no_free_ptr(saved_group);
}
struct idxd_engine **saved_engines =
kcalloc_node(idxd->max_engines,
sizeof(struct idxd_engine *),
GFP_KERNEL, dev_to_node(dev));
if (!saved_engines) {
/* Free saved groups */
idxd_free_saved(saved_groups, NULL, NULL, idxd);
return -ENOMEM;
}
for (i = 0; i < idxd->max_engines; i++) {
struct idxd_engine *saved_engine __free(kfree) =
kzalloc_node(sizeof(*saved_engine), GFP_KERNEL,
dev_to_node(dev));
if (!saved_engine) {
/* Free saved groups and engines */
idxd_free_saved(saved_groups, saved_engines, NULL,
idxd);
return -ENOMEM;
}
memcpy(saved_engine, idxd->engines[i], sizeof(*saved_engine));
saved_engines[i] = no_free_ptr(saved_engine);
}
unsigned long *saved_wq_enable_map __free(bitmap) =
bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL,
dev_to_node(dev));
if (!saved_wq_enable_map) {
/* Free saved groups and engines */
idxd_free_saved(saved_groups, saved_engines, NULL, idxd);
return -ENOMEM;
}
bitmap_copy(saved_wq_enable_map, idxd->wq_enable_map, idxd->max_wqs);
struct idxd_wq **saved_wqs __free(kfree) =
kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *),
GFP_KERNEL, dev_to_node(dev));
if (!saved_wqs) {
/* Free saved groups and engines */
idxd_free_saved(saved_groups, saved_engines, NULL, idxd);
return -ENOMEM;
}
for (i = 0; i < idxd->max_wqs; i++) {
struct idxd_wq *saved_wq __free(kfree) =
kzalloc_node(sizeof(*saved_wq), GFP_KERNEL,
dev_to_node(dev));
struct idxd_wq *wq;
if (!saved_wq) {
/* Free saved groups, engines, and wqs */
idxd_free_saved(saved_groups, saved_engines, saved_wqs,
idxd);
return -ENOMEM;
}
if (!test_bit(i, saved_wq_enable_map))
continue;
wq = idxd->wqs[i];
mutex_lock(&wq->wq_lock);
memcpy(saved_wq, wq, sizeof(*saved_wq));
saved_wqs[i] = no_free_ptr(saved_wq);
mutex_unlock(&wq->wq_lock);
}
/* Save configurations */
idxd_saved->saved_groups = no_free_ptr(saved_groups);
idxd_saved->saved_engines = no_free_ptr(saved_engines);
idxd_saved->saved_wq_enable_map = no_free_ptr(saved_wq_enable_map);
idxd_saved->saved_wqs = no_free_ptr(saved_wqs);
return 0;
}
/*
* Restore IDXD device configurations including engines, groups, wqs etc
* that were saved before.
*/
static void idxd_device_config_restore(struct idxd_device *idxd,
struct idxd_saved_states *idxd_saved)
{
struct idxd_evl *saved_evl = &idxd_saved->saved_evl;
int i;
idxd->rdbuf_limit = idxd_saved->saved_idxd.rdbuf_limit;
if (saved_evl)
idxd->evl->size = saved_evl->size;
for (i = 0; i < idxd->max_groups; i++) {
struct idxd_group *saved_group, *group;
saved_group = idxd_saved->saved_groups[i];
group = idxd->groups[i];
group->rdbufs_allowed = saved_group->rdbufs_allowed;
group->rdbufs_reserved = saved_group->rdbufs_reserved;
group->tc_a = saved_group->tc_a;
group->tc_b = saved_group->tc_b;
group->use_rdbuf_limit = saved_group->use_rdbuf_limit;
kfree(saved_group);
}
kfree(idxd_saved->saved_groups);
for (i = 0; i < idxd->max_engines; i++) {
struct idxd_engine *saved_engine, *engine;
saved_engine = idxd_saved->saved_engines[i];
engine = idxd->engines[i];
engine->group = saved_engine->group;
kfree(saved_engine);
}
kfree(idxd_saved->saved_engines);
bitmap_copy(idxd->wq_enable_map, idxd_saved->saved_wq_enable_map,
idxd->max_wqs);
bitmap_free(idxd_saved->saved_wq_enable_map);
for (i = 0; i < idxd->max_wqs; i++) {
struct idxd_wq *saved_wq, *wq;
size_t len;
if (!test_bit(i, idxd->wq_enable_map))
continue;
saved_wq = idxd_saved->saved_wqs[i];
wq = idxd->wqs[i];
mutex_lock(&wq->wq_lock);
wq->group = saved_wq->group;
wq->flags = saved_wq->flags;
wq->threshold = saved_wq->threshold;
wq->size = saved_wq->size;
wq->priority = saved_wq->priority;
wq->type = saved_wq->type;
len = strlen(saved_wq->name) + 1;
strscpy(wq->name, saved_wq->name, len);
wq->max_xfer_bytes = saved_wq->max_xfer_bytes;
wq->max_batch_size = saved_wq->max_batch_size;
wq->enqcmds_retries = saved_wq->enqcmds_retries;
wq->descs = saved_wq->descs;
wq->idxd_chan = saved_wq->idxd_chan;
len = strlen(saved_wq->driver_name) + 1;
strscpy(wq->driver_name, saved_wq->driver_name, len);
mutex_unlock(&wq->wq_lock);
kfree(saved_wq);
}
kfree(idxd_saved->saved_wqs);
}
static void idxd_reset_prepare(struct pci_dev *pdev)
{
struct idxd_device *idxd = pci_get_drvdata(pdev);
struct device *dev = &idxd->pdev->dev;
const char *idxd_name;
int rc;
dev = &idxd->pdev->dev;
idxd_name = dev_name(idxd_confdev(idxd));
struct idxd_saved_states *idxd_saved __free(kfree) =
kzalloc_node(sizeof(*idxd_saved), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!idxd_saved) {
dev_err(dev, "HALT: no memory\n");
return;
}
/* Save IDXD configurations. */
rc = idxd_device_config_save(idxd, idxd_saved);
if (rc < 0) {
dev_err(dev, "HALT: cannot save %s configs\n", idxd_name);
return;
}
idxd->idxd_saved = no_free_ptr(idxd_saved);
/* Save PCI device state. */
pci_save_state(idxd->pdev);
}
static void idxd_reset_done(struct pci_dev *pdev)
{
struct idxd_device *idxd = pci_get_drvdata(pdev);
const char *idxd_name;
struct device *dev;
int rc, i;
if (!idxd->idxd_saved)
return;
dev = &idxd->pdev->dev;
idxd_name = dev_name(idxd_confdev(idxd));
/* Restore PCI device state. */
pci_restore_state(idxd->pdev);
/* Unbind idxd device from driver. */
idxd_unbind(&idxd_drv.drv, idxd_name);
/*
* Probe PCI device without allocating or changing
* idxd software data which keeps the same as before FLR.
*/
idxd_pci_probe_alloc(idxd, NULL, NULL);
/* Restore IDXD configurations. */
idxd_device_config_restore(idxd, idxd->idxd_saved);
/* Re-configure IDXD device if allowed. */
if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) {
rc = idxd_device_config(idxd);
if (rc < 0) {
dev_err(dev, "HALT: %s config fails\n", idxd_name);
goto out;
}
}
/* Bind IDXD device to driver. */
rc = idxd_bind(&idxd_drv.drv, idxd_name);
if (rc < 0) {
dev_err(dev, "HALT: binding %s to driver fails\n", idxd_name);
goto out;
}
/* Bind enabled wq in the IDXD device to driver. */
for (i = 0; i < idxd->max_wqs; i++) {
if (test_bit(i, idxd->wq_enable_map)) {
struct idxd_wq *wq = idxd->wqs[i];
char wq_name[32];
wq->state = IDXD_WQ_DISABLED;
sprintf(wq_name, "wq%d.%d", idxd->id, wq->id);
/*
* Bind to user driver depending on wq type.
*
* Currently only support user type WQ. Will support
* kernel type WQ in the future.
*/
if (wq->type == IDXD_WQT_USER)
rc = idxd_bind(&idxd_user_drv.drv, wq_name);
else
rc = -EINVAL;
if (rc < 0) {
clear_bit(i, idxd->wq_enable_map);
dev_err(dev,
"HALT: unable to re-enable wq %s\n",
dev_name(wq_confdev(wq)));
}
}
}
out:
kfree(idxd->idxd_saved);
}
static const struct pci_error_handlers idxd_error_handler = {
.reset_prepare = idxd_reset_prepare,
.reset_done = idxd_reset_done,
};
/*
* Probe idxd PCI device.
* If idxd is not given, need to allocate idxd and set up its data.
*
* If idxd is given, idxd was allocated and setup already. Just need to
* configure device without re-allocating and re-configuring idxd data.
* This is useful for recovering from FLR.
*/
int idxd_pci_probe_alloc(struct idxd_device *idxd, struct pci_dev *pdev,
const struct pci_device_id *id)
{
bool alloc_idxd = idxd ? false : true;
struct idxd_driver_data *data;
struct device *dev;
int rc;
pdev = idxd ? idxd->pdev : pdev;
dev = &pdev->dev;
data = id ? (struct idxd_driver_data *)id->driver_data : NULL;
rc = pci_enable_device(pdev);
if (rc)
return rc;
dev_dbg(dev, "Alloc IDXD context\n");
idxd = idxd_alloc(pdev, data);
if (!idxd) {
rc = -ENOMEM;
goto err_idxd_alloc;
}
if (alloc_idxd) {
dev_dbg(dev, "Alloc IDXD context\n");
idxd = idxd_alloc(pdev, data);
if (!idxd) {
rc = -ENOMEM;
goto err_idxd_alloc;
}
dev_dbg(dev, "Mapping BARs\n");
idxd->reg_base = pci_iomap(pdev, IDXD_MMIO_BAR, 0);
if (!idxd->reg_base) {
rc = -ENOMEM;
goto err_iomap;
}
dev_dbg(dev, "Mapping BARs\n");
idxd->reg_base = pci_iomap(pdev, IDXD_MMIO_BAR, 0);
if (!idxd->reg_base) {
rc = -ENOMEM;
goto err_iomap;
}
dev_dbg(dev, "Set DMA masks\n");
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
goto err;
dev_dbg(dev, "Set DMA masks\n");
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
goto err;
}
dev_dbg(dev, "Set PCI master\n");
pci_set_master(pdev);
pci_set_drvdata(pdev, idxd);
idxd->hw.version = ioread32(idxd->reg_base + IDXD_VER_OFFSET);
rc = idxd_probe(idxd);
if (rc) {
dev_err(dev, "Intel(R) IDXD DMA Engine init failed\n");
goto err;
}
if (alloc_idxd) {
idxd->hw.version = ioread32(idxd->reg_base + IDXD_VER_OFFSET);
rc = idxd_probe(idxd);
if (rc) {
dev_err(dev, "Intel(R) IDXD DMA Engine init failed\n");
goto err;
}
if (data->load_device_defaults) {
rc = data->load_device_defaults(idxd);
if (data->load_device_defaults) {
rc = data->load_device_defaults(idxd);
if (rc)
dev_warn(dev, "IDXD loading device defaults failed\n");
}
rc = idxd_register_devices(idxd);
if (rc) {
dev_err(dev, "IDXD sysfs setup failed\n");
goto err_dev_register;
}
rc = idxd_device_init_debugfs(idxd);
if (rc)
dev_warn(dev, "IDXD loading device defaults failed\n");
dev_warn(dev, "IDXD debugfs failed to setup\n");
}
rc = idxd_register_devices(idxd);
if (rc) {
dev_err(dev, "IDXD sysfs setup failed\n");
goto err_dev_register;
}
if (!alloc_idxd) {
/* Release interrupts in the IDXD device. */
idxd_cleanup_interrupts(idxd);
rc = idxd_device_init_debugfs(idxd);
if (rc)
dev_warn(dev, "IDXD debugfs failed to setup\n");
/* Re-enable interrupts in the IDXD device. */
rc = idxd_setup_interrupts(idxd);
if (rc)
dev_warn(dev, "IDXD interrupts failed to setup\n");
}
dev_info(&pdev->dev, "Intel(R) Accelerator Device (v%x)\n",
idxd->hw.version);
idxd->user_submission_safe = data->user_submission_safe;
if (data)
idxd->user_submission_safe = data->user_submission_safe;
return 0;
@ -798,6 +1197,11 @@ static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return rc;
}
static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
return idxd_pci_probe_alloc(NULL, pdev, id);
}
void idxd_wqs_quiesce(struct idxd_device *idxd)
{
struct idxd_wq *wq;
@ -864,6 +1268,7 @@ static struct pci_driver idxd_pci_driver = {
.probe = idxd_pci_probe,
.remove = idxd_remove,
.shutdown = idxd_shutdown,
.err_handler = &idxd_error_handler,
};
static int __init idxd_init_module(void)

View File

@ -383,15 +383,65 @@ static void process_evl_entries(struct idxd_device *idxd)
mutex_unlock(&evl->lock);
}
static void idxd_device_flr(struct work_struct *work)
{
struct idxd_device *idxd = container_of(work, struct idxd_device, work);
int rc;
/*
* IDXD device requires a Function Level Reset (FLR).
* pci_reset_function() will reset the device with FLR.
*/
rc = pci_reset_function(idxd->pdev);
if (rc)
dev_err(&idxd->pdev->dev, "FLR failed\n");
}
static irqreturn_t idxd_halt(struct idxd_device *idxd)
{
union gensts_reg gensts;
gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
if (gensts.state == IDXD_DEVICE_STATE_HALT) {
idxd->state = IDXD_DEV_HALTED;
if (gensts.reset_type == IDXD_DEVICE_RESET_SOFTWARE) {
/*
* If we need a software reset, we will throw the work
* on a system workqueue in order to allow interrupts
* for the device command completions.
*/
INIT_WORK(&idxd->work, idxd_device_reinit);
queue_work(idxd->wq, &idxd->work);
} else if (gensts.reset_type == IDXD_DEVICE_RESET_FLR) {
idxd->state = IDXD_DEV_HALTED;
idxd_mask_error_interrupts(idxd);
dev_dbg(&idxd->pdev->dev,
"idxd halted, doing FLR. After FLR, configs are restored\n");
INIT_WORK(&idxd->work, idxd_device_flr);
queue_work(idxd->wq, &idxd->work);
} else {
idxd->state = IDXD_DEV_HALTED;
idxd_wqs_quiesce(idxd);
idxd_wqs_unmap_portal(idxd);
idxd_device_clear_state(idxd);
dev_err(&idxd->pdev->dev,
"idxd halted, need system reset");
return -ENXIO;
}
}
return IRQ_HANDLED;
}
irqreturn_t idxd_misc_thread(int vec, void *data)
{
struct idxd_irq_entry *irq_entry = data;
struct idxd_device *idxd = ie_to_idxd(irq_entry);
struct device *dev = &idxd->pdev->dev;
union gensts_reg gensts;
u32 val = 0;
int i;
bool err = false;
u32 cause;
cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
@ -401,7 +451,7 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
if (cause & IDXD_INTC_HALT_STATE)
goto halt;
return idxd_halt(idxd);
if (cause & IDXD_INTC_ERR) {
spin_lock(&idxd->dev_lock);
@ -435,7 +485,6 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
for (i = 0; i < 4; i++)
dev_warn_ratelimited(dev, "err[%d]: %#16.16llx\n",
i, idxd->sw_err.bits[i]);
err = true;
}
if (cause & IDXD_INTC_INT_HANDLE_REVOKED) {
@ -480,34 +529,6 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
dev_warn_once(dev, "Unexpected interrupt cause bits set: %#x\n",
val);
if (!err)
goto out;
halt:
gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
if (gensts.state == IDXD_DEVICE_STATE_HALT) {
idxd->state = IDXD_DEV_HALTED;
if (gensts.reset_type == IDXD_DEVICE_RESET_SOFTWARE) {
/*
* If we need a software reset, we will throw the work
* on a system workqueue in order to allow interrupts
* for the device command completions.
*/
INIT_WORK(&idxd->work, idxd_device_reinit);
queue_work(idxd->wq, &idxd->work);
} else {
idxd->state = IDXD_DEV_HALTED;
idxd_wqs_quiesce(idxd);
idxd_wqs_unmap_portal(idxd);
idxd_device_clear_state(idxd);
dev_err(&idxd->pdev->dev,
"idxd halted, need %s.\n",
gensts.reset_type == IDXD_DEVICE_RESET_FLR ?
"FLR" : "system reset");
}
}
out:
return IRQ_HANDLED;
}

View File

@ -9,6 +9,7 @@
#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb
#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212
#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216
#define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d
#define DEVICE_VERSION_1 0x100
#define DEVICE_VERSION_2 0x200

View File

@ -1979,13 +1979,3 @@ void idxd_unregister_devices(struct idxd_device *idxd)
device_unregister(group_confdev(group));
}
}
int idxd_register_bus_type(void)
{
return bus_register(&dsa_bus_type);
}
void idxd_unregister_bus_type(void)
{
bus_unregister(&dsa_bus_type);
}

View File

@ -1369,10 +1369,9 @@ static int mv_xor_probe(struct platform_device *pdev)
return 0;
if (pdev->dev.of_node) {
struct device_node *np;
int i = 0;
for_each_child_of_node(pdev->dev.of_node, np) {
for_each_child_of_node_scoped(pdev->dev.of_node, np) {
struct mv_xor_chan *chan;
dma_cap_mask_t cap_mask;
int irq;
@ -1388,7 +1387,6 @@ static int mv_xor_probe(struct platform_device *pdev)
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -ENODEV;
of_node_put(np);
goto err_channel_add;
}
@ -1397,7 +1395,6 @@ static int mv_xor_probe(struct platform_device *pdev)
if (IS_ERR(chan)) {
ret = PTR_ERR(chan);
irq_dispose_mapping(irq);
of_node_put(np);
goto err_channel_add;
}

View File

@ -1,13 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config AMD_PTDMA
tristate "AMD PassThru DMA Engine"
depends on X86_64 && PCI
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable support for the AMD PTDMA controller. This controller
provides DMA capabilities to perform high bandwidth memory to
memory and IO copy operations. It performs DMA transfer through
queue-based descriptor management. This DMA controller is intended
to be used with AMD Non-Transparent Bridge devices and not for
general purpose peripheral DMA.

View File

@ -59,6 +59,9 @@ struct bam_desc_hw {
#define DESC_FLAG_NWD BIT(12)
#define DESC_FLAG_CMD BIT(11)
#define BAM_NDP_REVISION_START 0x20
#define BAM_NDP_REVISION_END 0x27
struct bam_async_desc {
struct virt_dma_desc vd;
@ -398,6 +401,7 @@ struct bam_device {
/* dma start transaction tasklet */
struct tasklet_struct task;
u32 bam_revision;
};
/**
@ -441,8 +445,10 @@ static void bam_reset(struct bam_device *bdev)
writel_relaxed(val, bam_addr(bdev, 0, BAM_CTRL));
/* set descriptor threshold, start with 4 bytes */
writel_relaxed(DEFAULT_CNT_THRSHLD,
bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
if (in_range(bdev->bam_revision, BAM_NDP_REVISION_START,
BAM_NDP_REVISION_END))
writel_relaxed(DEFAULT_CNT_THRSHLD,
bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
/* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */
writel_relaxed(BAM_CNFG_BITS_DEFAULT, bam_addr(bdev, 0, BAM_CNFG_BITS));
@ -1000,9 +1006,10 @@ static void bam_apply_new_config(struct bam_chan *bchan,
maxburst = bchan->slave.src_maxburst;
else
maxburst = bchan->slave.dst_maxburst;
writel_relaxed(maxburst,
bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
if (in_range(bdev->bam_revision, BAM_NDP_REVISION_START,
BAM_NDP_REVISION_END))
writel_relaxed(maxburst,
bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
}
bchan->reconfigure = 0;
@ -1192,10 +1199,11 @@ static int bam_init(struct bam_device *bdev)
u32 val;
/* read revision and configuration information */
if (!bdev->num_ees) {
val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION));
val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION));
if (!bdev->num_ees)
bdev->num_ees = (val >> NUM_EES_SHIFT) & NUM_EES_MASK;
}
bdev->bam_revision = val & REVISION_MASK;
/* check that configured EE is within range */
if (bdev->ee >= bdev->num_ees)

View File

@ -18,6 +18,7 @@
#include "../virt-dma.h"
#define TRE_TYPE_DMA 0x10
#define TRE_TYPE_IMMEDIATE_DMA 0x11
#define TRE_TYPE_GO 0x20
#define TRE_TYPE_CONFIG0 0x22
@ -64,6 +65,7 @@
/* DMA TRE */
#define TRE_DMA_LEN GENMASK(23, 0)
#define TRE_DMA_IMMEDIATE_LEN GENMASK(3, 0)
/* Register offsets from gpi-top */
#define GPII_n_CH_k_CNTXT_0_OFFS(n, k) (0x20000 + (0x4000 * (n)) + (0x80 * (k)))
@ -1711,6 +1713,7 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
dma_addr_t address;
struct gpi_tre *tre;
unsigned int i;
int len;
/* first create config tre if applicable */
if (direction == DMA_MEM_TO_DEV && spi->set_config) {
@ -1763,14 +1766,30 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre_idx++;
address = sg_dma_address(sgl);
tre->dword[0] = lower_32_bits(address);
tre->dword[1] = upper_32_bits(address);
len = sg_dma_len(sgl);
tre->dword[2] = u32_encode_bits(sg_dma_len(sgl), TRE_DMA_LEN);
/* Support Immediate dma for write transfers for data length up to 8 bytes */
if (direction == DMA_MEM_TO_DEV && len <= 2 * sizeof(tre->dword[0])) {
/*
* For Immediate dma, data length may not always be length of 8 bytes,
* it can be length less than 8, hence initialize both dword's with 0
*/
tre->dword[0] = 0;
tre->dword[1] = 0;
memcpy(&tre->dword[0], sg_virt(sgl), 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);
tre->dword[2] = u32_encode_bits(len, TRE_DMA_IMMEDIATE_LEN);
tre->dword[3] = u32_encode_bits(TRE_TYPE_IMMEDIATE_DMA, TRE_FLAGS_TYPE);
} else {
tre->dword[0] = lower_32_bits(address);
tre->dword[1] = upper_32_bits(address);
tre->dword[2] = u32_encode_bits(len, TRE_DMA_LEN);
tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE);
}
tre->dword[3] |= u32_encode_bits(direction == DMA_MEM_TO_DEV,
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],

View File

@ -2023,6 +2023,10 @@ static const struct of_device_id rcar_dmac_of_ids[] = {
.compatible = "renesas,rcar-gen4-dmac",
.data = &rcar_gen4_dmac_data,
}, {
/*
* Backward compatibility for between v5.12 - v5.19
* which didn't combined with "renesas,rcar-gen4-dmac"
*/
.compatible = "renesas,dmac-r8a779a0",
.data = &rcar_gen4_dmac_data,
},

View File

@ -13,7 +13,9 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_dma.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@ -31,12 +33,21 @@
#define SUN4I_DMA_CFG_SRC_ADDR_MODE(mode) ((mode) << 5)
#define SUN4I_DMA_CFG_SRC_DRQ_TYPE(type) (type)
#define SUNIV_DMA_CFG_DST_DATA_WIDTH(width) ((width) << 24)
#define SUNIV_DMA_CFG_SRC_DATA_WIDTH(width) ((width) << 8)
#define SUN4I_MAX_BURST 8
#define SUNIV_MAX_BURST 4
/** Normal DMA register values **/
/* Normal DMA source/destination data request type values */
#define SUN4I_NDMA_DRQ_TYPE_SDRAM 0x16
#define SUN4I_NDMA_DRQ_TYPE_LIMIT (0x1F + 1)
#define SUNIV_NDMA_DRQ_TYPE_SDRAM 0x11
#define SUNIV_NDMA_DRQ_TYPE_LIMIT (0x17 + 1)
/** Normal DMA register layout **/
/* Dedicated DMA source/destination address mode values */
@ -50,6 +61,9 @@
#define SUN4I_NDMA_CFG_BYTE_COUNT_MODE_REMAIN BIT(15)
#define SUN4I_NDMA_CFG_SRC_NON_SECURE BIT(6)
#define SUNIV_NDMA_CFG_CONT_MODE BIT(29)
#define SUNIV_NDMA_CFG_WAIT_STATE(n) ((n) << 26)
/** Dedicated DMA register values **/
/* Dedicated DMA source/destination address mode values */
@ -62,6 +76,9 @@
#define SUN4I_DDMA_DRQ_TYPE_SDRAM 0x1
#define SUN4I_DDMA_DRQ_TYPE_LIMIT (0x1F + 1)
#define SUNIV_DDMA_DRQ_TYPE_SDRAM 0x1
#define SUNIV_DDMA_DRQ_TYPE_LIMIT (0x9 + 1)
/** Dedicated DMA register layout **/
/* Dedicated DMA configuration register layout */
@ -115,6 +132,11 @@
#define SUN4I_DMA_NR_MAX_VCHANS \
(SUN4I_NDMA_NR_MAX_VCHANS + SUN4I_DDMA_NR_MAX_VCHANS)
#define SUNIV_NDMA_NR_MAX_CHANNELS 4
#define SUNIV_DDMA_NR_MAX_CHANNELS 4
#define SUNIV_NDMA_NR_MAX_VCHANS (24 * 2 - 1)
#define SUNIV_DDMA_NR_MAX_VCHANS 10
/* This set of SUN4I_DDMA timing parameters were found experimentally while
* working with the SPI driver and seem to make it behave correctly */
#define SUN4I_DDMA_MAGIC_SPI_PARAMETERS \
@ -132,6 +154,33 @@
#define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M
#define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE
/*
* Hardware channels / ports representation
*
* The hardware is used in several SoCs, with differing numbers
* of channels and endpoints. This structure ties those numbers
* to a certain compatible string.
*/
struct sun4i_dma_config {
u32 ndma_nr_max_channels;
u32 ndma_nr_max_vchans;
u32 ddma_nr_max_channels;
u32 ddma_nr_max_vchans;
u32 dma_nr_max_channels;
void (*set_dst_data_width)(u32 *p_cfg, s8 data_width);
void (*set_src_data_width)(u32 *p_cfg, s8 data_width);
int (*convert_burst)(u32 maxburst);
u8 ndma_drq_sdram;
u8 ddma_drq_sdram;
u8 max_burst;
bool has_reset;
};
struct sun4i_dma_pchan {
/* Register base of channel */
void __iomem *base;
@ -170,7 +219,7 @@ struct sun4i_dma_contract {
};
struct sun4i_dma_dev {
DECLARE_BITMAP(pchans_used, SUN4I_DMA_NR_MAX_CHANNELS);
unsigned long *pchans_used;
struct dma_device slave;
struct sun4i_dma_pchan *pchans;
struct sun4i_dma_vchan *vchans;
@ -178,6 +227,8 @@ struct sun4i_dma_dev {
struct clk *clk;
int irq;
spinlock_t lock;
const struct sun4i_dma_config *cfg;
struct reset_control *rst;
};
static struct sun4i_dma_dev *to_sun4i_dma_dev(struct dma_device *dev)
@ -200,7 +251,27 @@ static struct device *chan2dev(struct dma_chan *chan)
return &chan->dev->device;
}
static int convert_burst(u32 maxburst)
static void set_dst_data_width_a10(u32 *p_cfg, s8 data_width)
{
*p_cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(data_width);
}
static void set_src_data_width_a10(u32 *p_cfg, s8 data_width)
{
*p_cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(data_width);
}
static void set_dst_data_width_f1c100s(u32 *p_cfg, s8 data_width)
{
*p_cfg |= SUNIV_DMA_CFG_DST_DATA_WIDTH(data_width);
}
static void set_src_data_width_f1c100s(u32 *p_cfg, s8 data_width)
{
*p_cfg |= SUNIV_DMA_CFG_SRC_DATA_WIDTH(data_width);
}
static int convert_burst_a10(u32 maxburst)
{
if (maxburst > 8)
return -EINVAL;
@ -209,6 +280,15 @@ static int convert_burst(u32 maxburst)
return (maxburst >> 2);
}
static int convert_burst_f1c100s(u32 maxburst)
{
if (maxburst > 4)
return -EINVAL;
/* 1 -> 0, 4 -> 1 */
return (maxburst >> 2);
}
static int convert_buswidth(enum dma_slave_buswidth addr_width)
{
if (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)
@ -233,15 +313,15 @@ static struct sun4i_dma_pchan *find_and_use_pchan(struct sun4i_dma_dev *priv,
int i, max;
/*
* pchans 0-SUN4I_NDMA_NR_MAX_CHANNELS are normal, and
* SUN4I_NDMA_NR_MAX_CHANNELS+ are dedicated ones
* pchans 0-priv->cfg->ndma_nr_max_channels are normal, and
* priv->cfg->ndma_nr_max_channels+ are dedicated ones
*/
if (vchan->is_dedicated) {
i = SUN4I_NDMA_NR_MAX_CHANNELS;
max = SUN4I_DMA_NR_MAX_CHANNELS;
i = priv->cfg->ndma_nr_max_channels;
max = priv->cfg->dma_nr_max_channels;
} else {
i = 0;
max = SUN4I_NDMA_NR_MAX_CHANNELS;
max = priv->cfg->ndma_nr_max_channels;
}
spin_lock_irqsave(&priv->lock, flags);
@ -444,6 +524,7 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
size_t len, struct dma_slave_config *sconfig,
enum dma_transfer_direction direction)
{
struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
struct sun4i_dma_promise *promise;
int ret;
@ -467,13 +548,13 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
sconfig->src_addr_width, sconfig->dst_addr_width);
/* Source burst */
ret = convert_burst(sconfig->src_maxburst);
ret = priv->cfg->convert_burst(sconfig->src_maxburst);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
/* Destination burst */
ret = convert_burst(sconfig->dst_maxburst);
ret = priv->cfg->convert_burst(sconfig->dst_maxburst);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
@ -482,13 +563,13 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
ret = convert_buswidth(sconfig->src_addr_width);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
priv->cfg->set_src_data_width(&promise->cfg, ret);
/* Destination bus width */
ret = convert_buswidth(sconfig->dst_addr_width);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
priv->cfg->set_dst_data_width(&promise->cfg, ret);
return promise;
@ -510,6 +591,7 @@ static struct sun4i_dma_promise *
generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
size_t len, struct dma_slave_config *sconfig)
{
struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
struct sun4i_dma_promise *promise;
int ret;
@ -524,13 +606,13 @@ generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
SUN4I_DDMA_CFG_BYTE_COUNT_MODE_REMAIN;
/* Source burst */
ret = convert_burst(sconfig->src_maxburst);
ret = priv->cfg->convert_burst(sconfig->src_maxburst);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
/* Destination burst */
ret = convert_burst(sconfig->dst_maxburst);
ret = priv->cfg->convert_burst(sconfig->dst_maxburst);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
@ -539,13 +621,13 @@ generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
ret = convert_buswidth(sconfig->src_addr_width);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
priv->cfg->set_src_data_width(&promise->cfg, ret);
/* Destination bus width */
ret = convert_buswidth(sconfig->dst_addr_width);
if (ret < 0)
goto fail;
promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
priv->cfg->set_dst_data_width(&promise->cfg, ret);
return promise;
@ -622,6 +704,7 @@ static struct dma_async_tx_descriptor *
sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
dma_addr_t src, size_t len, unsigned long flags)
{
struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
struct dma_slave_config *sconfig = &vchan->cfg;
struct sun4i_dma_promise *promise;
@ -638,8 +721,8 @@ sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
*/
sconfig->src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
sconfig->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
sconfig->src_maxburst = 8;
sconfig->dst_maxburst = 8;
sconfig->src_maxburst = priv->cfg->max_burst;
sconfig->dst_maxburst = priv->cfg->max_burst;
if (vchan->is_dedicated)
promise = generate_ddma_promise(chan, src, dest, len, sconfig);
@ -654,11 +737,13 @@ sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
/* Configure memcpy mode */
if (vchan->is_dedicated) {
promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM) |
SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM);
promise->cfg |=
SUN4I_DMA_CFG_SRC_DRQ_TYPE(priv->cfg->ddma_drq_sdram) |
SUN4I_DMA_CFG_DST_DRQ_TYPE(priv->cfg->ddma_drq_sdram);
} else {
promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM) |
SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM);
promise->cfg |=
SUN4I_DMA_CFG_SRC_DRQ_TYPE(priv->cfg->ndma_drq_sdram) |
SUN4I_DMA_CFG_DST_DRQ_TYPE(priv->cfg->ndma_drq_sdram);
}
/* Fill the contract with our only promise */
@ -673,6 +758,7 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
size_t period_len, enum dma_transfer_direction dir,
unsigned long flags)
{
struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
struct dma_slave_config *sconfig = &vchan->cfg;
struct sun4i_dma_promise *promise;
@ -696,11 +782,11 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
if (vchan->is_dedicated) {
io_mode = SUN4I_DDMA_ADDR_MODE_IO;
linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
ram_type = priv->cfg->ddma_drq_sdram;
} else {
io_mode = SUN4I_NDMA_ADDR_MODE_IO;
linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
ram_type = priv->cfg->ndma_drq_sdram;
}
if (dir == DMA_MEM_TO_DEV) {
@ -793,6 +879,7 @@ sun4i_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction dir,
unsigned long flags, void *context)
{
struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
struct dma_slave_config *sconfig = &vchan->cfg;
struct sun4i_dma_promise *promise;
@ -818,11 +905,11 @@ sun4i_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (vchan->is_dedicated) {
io_mode = SUN4I_DDMA_ADDR_MODE_IO;
linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
ram_type = priv->cfg->ddma_drq_sdram;
} else {
io_mode = SUN4I_NDMA_ADDR_MODE_IO;
linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
ram_type = priv->cfg->ndma_drq_sdram;
}
if (dir == DMA_MEM_TO_DEV)
@ -1150,6 +1237,10 @@ static int sun4i_dma_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
priv->cfg = of_device_get_match_data(&pdev->dev);
if (!priv->cfg)
return -ENODEV;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
@ -1164,6 +1255,13 @@ static int sun4i_dma_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
if (priv->cfg->has_reset) {
priv->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(priv->rst))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->rst),
"Failed to get reset control\n");
}
platform_set_drvdata(pdev, priv);
spin_lock_init(&priv->lock);
@ -1197,23 +1295,26 @@ static int sun4i_dma_probe(struct platform_device *pdev)
priv->slave.dev = &pdev->dev;
priv->pchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_CHANNELS,
priv->pchans = devm_kcalloc(&pdev->dev, priv->cfg->dma_nr_max_channels,
sizeof(struct sun4i_dma_pchan), GFP_KERNEL);
priv->vchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_VCHANS,
sizeof(struct sun4i_dma_vchan), GFP_KERNEL);
if (!priv->vchans || !priv->pchans)
priv->pchans_used = devm_kcalloc(&pdev->dev,
BITS_TO_LONGS(priv->cfg->dma_nr_max_channels),
sizeof(unsigned long), GFP_KERNEL);
if (!priv->vchans || !priv->pchans || !priv->pchans_used)
return -ENOMEM;
/*
* [0..SUN4I_NDMA_NR_MAX_CHANNELS) are normal pchans, and
* [SUN4I_NDMA_NR_MAX_CHANNELS..SUN4I_DMA_NR_MAX_CHANNELS) are
* [0..priv->cfg->ndma_nr_max_channels) are normal pchans, and
* [priv->cfg->ndma_nr_max_channels..priv->cfg->dma_nr_max_channels) are
* dedicated ones
*/
for (i = 0; i < SUN4I_NDMA_NR_MAX_CHANNELS; i++)
for (i = 0; i < priv->cfg->ndma_nr_max_channels; i++)
priv->pchans[i].base = priv->base +
SUN4I_NDMA_CHANNEL_REG_BASE(i);
for (j = 0; i < SUN4I_DMA_NR_MAX_CHANNELS; i++, j++) {
for (j = 0; i < priv->cfg->dma_nr_max_channels; i++, j++) {
priv->pchans[i].base = priv->base +
SUN4I_DDMA_CHANNEL_REG_BASE(j);
priv->pchans[i].is_dedicated = 1;
@ -1284,8 +1385,51 @@ static void sun4i_dma_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk);
}
static struct sun4i_dma_config sun4i_a10_dma_cfg = {
.ndma_nr_max_channels = SUN4I_NDMA_NR_MAX_CHANNELS,
.ndma_nr_max_vchans = SUN4I_NDMA_NR_MAX_VCHANS,
.ddma_nr_max_channels = SUN4I_DDMA_NR_MAX_CHANNELS,
.ddma_nr_max_vchans = SUN4I_DDMA_NR_MAX_VCHANS,
.dma_nr_max_channels = SUN4I_DMA_NR_MAX_CHANNELS,
.set_dst_data_width = set_dst_data_width_a10,
.set_src_data_width = set_src_data_width_a10,
.convert_burst = convert_burst_a10,
.ndma_drq_sdram = SUN4I_NDMA_DRQ_TYPE_SDRAM,
.ddma_drq_sdram = SUN4I_DDMA_DRQ_TYPE_SDRAM,
.max_burst = SUN4I_MAX_BURST,
.has_reset = false,
};
static struct sun4i_dma_config suniv_f1c100s_dma_cfg = {
.ndma_nr_max_channels = SUNIV_NDMA_NR_MAX_CHANNELS,
.ndma_nr_max_vchans = SUNIV_NDMA_NR_MAX_VCHANS,
.ddma_nr_max_channels = SUNIV_DDMA_NR_MAX_CHANNELS,
.ddma_nr_max_vchans = SUNIV_DDMA_NR_MAX_VCHANS,
.dma_nr_max_channels = SUNIV_NDMA_NR_MAX_CHANNELS +
SUNIV_DDMA_NR_MAX_CHANNELS,
.set_dst_data_width = set_dst_data_width_f1c100s,
.set_src_data_width = set_src_data_width_f1c100s,
.convert_burst = convert_burst_f1c100s,
.ndma_drq_sdram = SUNIV_NDMA_DRQ_TYPE_SDRAM,
.ddma_drq_sdram = SUNIV_DDMA_DRQ_TYPE_SDRAM,
.max_burst = SUNIV_MAX_BURST,
.has_reset = true,
};
static const struct of_device_id sun4i_dma_match[] = {
{ .compatible = "allwinner,sun4i-a10-dma" },
{ .compatible = "allwinner,sun4i-a10-dma", .data = &sun4i_a10_dma_cfg },
{ .compatible = "allwinner,suniv-f1c100s-dma",
.data = &suniv_f1c100s_dma_cfg },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun4i_dma_match);

View File

@ -43,6 +43,10 @@
#define ADMA_CH_CONFIG_MAX_BUFS 8
#define TEGRA186_ADMA_CH_CONFIG_OUTSTANDING_REQS(reqs) (reqs << 4)
#define TEGRA186_ADMA_GLOBAL_PAGE_CHGRP 0x30
#define TEGRA186_ADMA_GLOBAL_PAGE_RX_REQ 0x70
#define TEGRA186_ADMA_GLOBAL_PAGE_TX_REQ 0x84
#define ADMA_CH_FIFO_CTRL 0x2c
#define ADMA_CH_TX_FIFO_SIZE_SHIFT 8
#define ADMA_CH_RX_FIFO_SIZE_SHIFT 0
@ -96,6 +100,7 @@ struct tegra_adma_chip_data {
unsigned int ch_fifo_size_mask;
unsigned int sreq_index_offset;
bool has_outstanding_reqs;
void (*set_global_pg_config)(struct tegra_adma *tdma);
};
/*
@ -151,6 +156,7 @@ struct tegra_adma {
struct dma_device dma_dev;
struct device *dev;
void __iomem *base_addr;
void __iomem *ch_base_addr;
struct clk *ahub_clk;
unsigned int nr_channels;
unsigned long *dma_chan_mask;
@ -159,6 +165,7 @@ struct tegra_adma {
/* Used to store global command register state when suspending */
unsigned int global_cmd;
unsigned int ch_page_no;
const struct tegra_adma_chip_data *cdata;
@ -176,6 +183,11 @@ static inline u32 tdma_read(struct tegra_adma *tdma, u32 reg)
return readl(tdma->base_addr + tdma->cdata->global_reg_offset + reg);
}
static inline void tdma_ch_global_write(struct tegra_adma *tdma, u32 reg, u32 val)
{
writel(val, tdma->ch_base_addr + tdma->cdata->global_reg_offset + reg);
}
static inline void tdma_ch_write(struct tegra_adma_chan *tdc, u32 reg, u32 val)
{
writel(val, tdc->chan_addr + reg);
@ -217,13 +229,30 @@ static int tegra_adma_slave_config(struct dma_chan *dc,
return 0;
}
static void tegra186_adma_global_page_config(struct tegra_adma *tdma)
{
/*
* Clear the default page1 channel group configs and program
* the global registers based on the actual page usage
*/
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_CHGRP, 0);
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_RX_REQ, 0);
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_TX_REQ, 0);
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_CHGRP + (tdma->ch_page_no * 0x4), 0xff);
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_RX_REQ + (tdma->ch_page_no * 0x4), 0x1ffffff);
tdma_write(tdma, TEGRA186_ADMA_GLOBAL_PAGE_TX_REQ + (tdma->ch_page_no * 0x4), 0xffffff);
}
static int tegra_adma_init(struct tegra_adma *tdma)
{
u32 status;
int ret;
/* Clear any interrupts */
tdma_write(tdma, tdma->cdata->ch_base_offset + tdma->cdata->global_int_clear, 0x1);
/* Clear any channels group global interrupts */
tdma_ch_global_write(tdma, tdma->cdata->global_int_clear, 0x1);
if (!tdma->base_addr)
return 0;
/* Assert soft reset */
tdma_write(tdma, ADMA_GLOBAL_SOFT_RESET, 0x1);
@ -237,6 +266,9 @@ static int tegra_adma_init(struct tegra_adma *tdma)
if (ret)
return ret;
if (tdma->cdata->set_global_pg_config)
tdma->cdata->set_global_pg_config(tdma);
/* Enable global ADMA registers */
tdma_write(tdma, ADMA_GLOBAL_CMD, 1);
@ -736,7 +768,9 @@ static int __maybe_unused tegra_adma_runtime_suspend(struct device *dev)
struct tegra_adma_chan *tdc;
int i;
tdma->global_cmd = tdma_read(tdma, ADMA_GLOBAL_CMD);
if (tdma->base_addr)
tdma->global_cmd = tdma_read(tdma, ADMA_GLOBAL_CMD);
if (!tdma->global_cmd)
goto clk_disable;
@ -777,7 +811,11 @@ static int __maybe_unused tegra_adma_runtime_resume(struct device *dev)
dev_err(dev, "ahub clk_enable failed: %d\n", ret);
return ret;
}
tdma_write(tdma, ADMA_GLOBAL_CMD, tdma->global_cmd);
if (tdma->base_addr) {
tdma_write(tdma, ADMA_GLOBAL_CMD, tdma->global_cmd);
if (tdma->cdata->set_global_pg_config)
tdma->cdata->set_global_pg_config(tdma);
}
if (!tdma->global_cmd)
return 0;
@ -817,6 +855,7 @@ static const struct tegra_adma_chip_data tegra210_chip_data = {
.ch_fifo_size_mask = 0xf,
.sreq_index_offset = 2,
.has_outstanding_reqs = false,
.set_global_pg_config = NULL,
};
static const struct tegra_adma_chip_data tegra186_chip_data = {
@ -833,6 +872,7 @@ static const struct tegra_adma_chip_data tegra186_chip_data = {
.ch_fifo_size_mask = 0x1f,
.sreq_index_offset = 4,
.has_outstanding_reqs = true,
.set_global_pg_config = tegra186_adma_global_page_config,
};
static const struct of_device_id tegra_adma_of_match[] = {
@ -846,7 +886,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
{
const struct tegra_adma_chip_data *cdata;
struct tegra_adma *tdma;
int ret, i;
struct resource *res_page, *res_base;
int ret, i, page_no;
cdata = of_device_get_match_data(&pdev->dev);
if (!cdata) {
@ -865,9 +906,35 @@ static int tegra_adma_probe(struct platform_device *pdev)
tdma->nr_channels = cdata->nr_channels;
platform_set_drvdata(pdev, tdma);
tdma->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tdma->base_addr))
return PTR_ERR(tdma->base_addr);
res_page = platform_get_resource_byname(pdev, IORESOURCE_MEM, "page");
if (res_page) {
tdma->ch_base_addr = devm_ioremap_resource(&pdev->dev, res_page);
if (IS_ERR(tdma->ch_base_addr))
return PTR_ERR(tdma->ch_base_addr);
res_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global");
if (res_base) {
page_no = (res_page->start - res_base->start) / cdata->ch_base_offset;
if (page_no <= 0)
return -EINVAL;
tdma->ch_page_no = page_no - 1;
tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base);
if (IS_ERR(tdma->base_addr))
return PTR_ERR(tdma->base_addr);
}
} else {
/* If no 'page' property found, then reg DT binding would be legacy */
res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_base) {
tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base);
if (IS_ERR(tdma->base_addr))
return PTR_ERR(tdma->base_addr);
} else {
return -ENODEV;
}
tdma->ch_base_addr = tdma->base_addr + cdata->ch_base_offset;
}
tdma->ahub_clk = devm_clk_get(&pdev->dev, "d_audio");
if (IS_ERR(tdma->ahub_clk)) {
@ -900,8 +967,7 @@ static int tegra_adma_probe(struct platform_device *pdev)
if (!test_bit(i, tdma->dma_chan_mask))
continue;
tdc->chan_addr = tdma->base_addr + cdata->ch_base_offset
+ (cdata->ch_reg_size * i);
tdc->chan_addr = tdma->ch_base_addr + (cdata->ch_reg_size * i);
tdc->irq = of_irq_get(pdev->dev.of_node, i);
if (tdc->irq <= 0) {

View File

@ -208,7 +208,6 @@ struct edma_desc {
struct edma_cc;
struct edma_tc {
struct device_node *node;
u16 id;
};
@ -2460,19 +2459,19 @@ static int edma_probe(struct platform_device *pdev)
goto err_reg1;
}
for (i = 0;; i++) {
for (i = 0; i < ecc->num_tc; i++) {
ret = of_parse_phandle_with_fixed_args(node, "ti,tptcs",
1, i, &tc_args);
if (ret || i == ecc->num_tc)
if (ret)
break;
ecc->tc_list[i].node = tc_args.np;
ecc->tc_list[i].id = i;
queue_priority_mapping[i][1] = tc_args.args[0];
if (queue_priority_mapping[i][1] > lowest_priority) {
lowest_priority = queue_priority_mapping[i][1];
info->default_queue = i;
}
of_node_put(tc_args.np);
}
/* See if we have optional dma-channel-mask array */

View File

@ -4404,6 +4404,18 @@ static struct udma_match_data j721s2_bcdma_csi_data = {
.soc_data = &j721s2_bcdma_csi_soc_data,
};
static struct udma_match_data j722s_bcdma_csi_data = {
.type = DMA_TYPE_BCDMA,
.psil_base = 0x3100,
.enable_memcpy_support = false,
.burst_size = {
TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */
0, /* No H Channels */
0, /* No UH Channels */
},
.soc_data = &j721s2_bcdma_csi_soc_data,
};
static const struct of_device_id udma_of_match[] = {
{
.compatible = "ti,am654-navss-main-udmap",
@ -4435,6 +4447,10 @@ static const struct of_device_id udma_of_match[] = {
.compatible = "ti,j721s2-dmss-bcdma-csi",
.data = &j721s2_bcdma_csi_data,
},
{
.compatible = "ti,j722s-dmss-bcdma-csi",
.data = &j722s_bcdma_csi_data,
},
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, udma_of_match);

View File

@ -390,15 +390,11 @@ static int xdma_xfer_start(struct xdma_chan *xchan)
*/
static int xdma_xfer_stop(struct xdma_chan *xchan)
{
int ret;
struct xdma_device *xdev = xchan->xdev_hdl;
/* clear run stop bit to prevent any further auto-triggering */
ret = regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL_W1C,
CHAN_CTRL_RUN_STOP);
if (ret)
return ret;
return ret;
return regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL_W1C,
CHAN_CTRL_RUN_STOP);
}
/**

View File

@ -1404,16 +1404,18 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg);
j = chan->desc_submitcount;
reg = dma_read(chan, XILINX_DMA_REG_PARK_PTR);
if (chan->direction == DMA_MEM_TO_DEV) {
reg &= ~XILINX_DMA_PARK_PTR_RD_REF_MASK;
reg |= j << XILINX_DMA_PARK_PTR_RD_REF_SHIFT;
} else {
reg &= ~XILINX_DMA_PARK_PTR_WR_REF_MASK;
reg |= j << XILINX_DMA_PARK_PTR_WR_REF_SHIFT;
if (config->park) {
j = chan->desc_submitcount;
reg = dma_read(chan, XILINX_DMA_REG_PARK_PTR);
if (chan->direction == DMA_MEM_TO_DEV) {
reg &= ~XILINX_DMA_PARK_PTR_RD_REF_MASK;
reg |= j << XILINX_DMA_PARK_PTR_RD_REF_SHIFT;
} else {
reg &= ~XILINX_DMA_PARK_PTR_WR_REF_MASK;
reg |= j << XILINX_DMA_PARK_PTR_WR_REF_SHIFT;
}
dma_write(chan, XILINX_DMA_REG_PARK_PTR, reg);
}
dma_write(chan, XILINX_DMA_REG_PARK_PTR, reg);
/* Start the hardware */
xilinx_dma_start(chan);