DMAengine updates for v4.17-rc1
This time we have couple of new drivers along with updates to drivers. - new driver for DesignWare AXI DMAC and MediaTek High-Speed DMA controller - stm32 dma and qcom bam dma driver updates - norandom test option for dmatest -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJazNiVAAoJEHwUBw8lI4NHGoYP/0G4butX2wkKgQMt+CCTSvC8 3J3E3hqyd4aIPu9O2ebxz4MpVRcJKQRDZiJ5ZIcVaP+79ehjpRH1iVMxO3vX0Eb4 urTRB0F+waZJK1Cdm/tXtLkCGIzxFuv4HJbd+Z2CHYiuKT5SbNvhz4j8HRUmoV35 +Vlify3NZgKdQzVAbD+ZHPWAnyIQFeQHjywS8PIGSKg/iXTpSGDV+2NweH8rOPre MlTL5/YknO/rn5w34/kz4nzqc1AWH57+HBh3tEtsrlrgdZjO0czFmEGbLzTls5tw AcbFm41m7ZV2PADHaH+LLF0cxDp299sbHqROkVM9evnLYu4IEUIUL6mOp13wKh45 x78QDzlLQiFQ+gbjJ37PXg3SKsQ8Krr/lXnvBITnnC+56w+L7/OuAyM3bxFRCTke 9iCIOeKXHxhESh3J3Znup9BEEdP5DN84jjjaF4EZ2e6wXbd+GaPQABYoRRwyn7pr G49a0k5o3j4vT7vo9j6Eqw7kqwI8oOkAtAVKGWttoO1r0YbaV0ATPBuvD9SZgFpx bLZehKWZCVRnr18J4MiMcSq+bwci9FM7P3HU7nECeJWTwb694LER4hnbQqGS2mZh kOZXexrqQljKbaLRR8YCXy8BcgK4qufOPSX//ts7FX0J1cNqtWU86XkWwPI7WYoa KkN2omd6rNtdxoXtVO4Y =0YWt -----END PGP SIGNATURE----- Merge tag 'dmaengine-4.17-rc1' of git://git.infradead.org/users/vkoul/slave-dma Pull dmaengine updates from Vinod Koul: "This time we have couple of new drivers along with updates to drivers: - new drivers for the DesignWare AXI DMAC and MediaTek High-Speed DMA controllers - stm32 dma and qcom bam dma driver updates - norandom test option for dmatest" * tag 'dmaengine-4.17-rc1' of git://git.infradead.org/users/vkoul/slave-dma: (30 commits) dmaengine: stm32-dma: properly mask irq bits dmaengine: stm32-dma: fix max items per transfer dmaengine: stm32-dma: fix DMA IRQ status handling dmaengine: stm32-dma: Improve memory burst management dmaengine: stm32-dma: fix typo and reported checkpatch warnings dmaengine: stm32-dma: fix incomplete configuration in cyclic mode dmaengine: stm32-dma: threshold manages with bitfield feature dt-bindings: stm32-dma: introduce DMA features bitfield dt-bindings: rcar-dmac: Document r8a77470 support dmaengine: rcar-dmac: Fix too early/late system suspend/resume callbacks dmaengine: dw-axi-dmac: fix spelling mistake: "catched" -> "caught" dmaengine: edma: Check the memory allocation for the memcpy dma device dmaengine: at_xdmac: fix rare residue corruption dmaengine: mediatek: update MAINTAINERS entry with MediaTek DMA driver dmaengine: mediatek: Add MediaTek High-Speed DMA controller for MT7622 and MT7623 SoC dt-bindings: dmaengine: Add MediaTek High-Speed DMA controller bindings dt-bindings: Document the Synopsys DW AXI DMA bindings dmaengine: Introduce DW AXI DMAC driver dmaengine: pl330: fix a race condition in case of threaded irqs dmaengine: imx-sdma: fix pagefault when channel is disabled during interrupt ...
This commit is contained in:
commit
1b02dcb9fa
|
@ -0,0 +1,33 @@
|
|||
MediaTek High-Speed DMA Controller
|
||||
==================================
|
||||
|
||||
This device follows the generic DMA bindings defined in dma/dma.txt.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be one of
|
||||
"mediatek,mt7622-hsdma": for MT7622 SoC
|
||||
"mediatek,mt7623-hsdma": for MT7623 SoC
|
||||
- reg: Should contain the register's base address and length.
|
||||
- interrupts: Should contain a reference to the interrupt used by this
|
||||
device.
|
||||
- clocks: Should be the clock specifiers corresponding to the entry in
|
||||
clock-names property.
|
||||
- clock-names: Should contain "hsdma" entries.
|
||||
- power-domains: Phandle to the power domain that the device is part of
|
||||
- #dma-cells: The length of the DMA specifier, must be <1>. This one cell
|
||||
in dmas property of a client device represents the channel
|
||||
number.
|
||||
Example:
|
||||
|
||||
hsdma: dma-controller@1b007000 {
|
||||
compatible = "mediatek,mt7623-hsdma";
|
||||
reg = <0 0x1b007000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <ðsys CLK_ETHSYS_HSDMA>;
|
||||
clock-names = "hsdma";
|
||||
power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
|
||||
#dma-cells = <1>;
|
||||
};
|
||||
|
||||
DMA clients must use the format described in dma/dma.txt file.
|
|
@ -15,6 +15,10 @@ Required properties:
|
|||
the secure world.
|
||||
- qcom,controlled-remotely : optional, indicates that the bam is controlled by
|
||||
remote proccessor i.e. execution environment.
|
||||
- num-channels : optional, indicates supported number of DMA channels in a
|
||||
remotely controlled bam.
|
||||
- qcom,num-ees : optional, indicates supported number of Execution Environments
|
||||
in a remotely controlled bam.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ Required Properties:
|
|||
Examples with soctypes are:
|
||||
- "renesas,dmac-r8a7743" (RZ/G1M)
|
||||
- "renesas,dmac-r8a7745" (RZ/G1E)
|
||||
- "renesas,dmac-r8a77470" (RZ/G1C)
|
||||
- "renesas,dmac-r8a7790" (R-Car H2)
|
||||
- "renesas,dmac-r8a7791" (R-Car M2-W)
|
||||
- "renesas,dmac-r8a7792" (R-Car V2H)
|
||||
|
@ -26,6 +27,7 @@ Required Properties:
|
|||
- "renesas,dmac-r8a7795" (R-Car H3)
|
||||
- "renesas,dmac-r8a7796" (R-Car M3-W)
|
||||
- "renesas,dmac-r8a77970" (R-Car V3M)
|
||||
- "renesas,dmac-r8a77980" (R-Car V3H)
|
||||
|
||||
- reg: base address and length of the registers block for the DMAC
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ Required Properties:
|
|||
- "renesas,r8a7794-usb-dmac" (R-Car E2)
|
||||
- "renesas,r8a7795-usb-dmac" (R-Car H3)
|
||||
- "renesas,r8a7796-usb-dmac" (R-Car M3-W)
|
||||
- "renesas,r8a77965-usb-dmac" (R-Car M3-N)
|
||||
- reg: base address and length of the registers block for the DMAC
|
||||
- interrupts: interrupt specifiers for the DMAC, one for each entry in
|
||||
interrupt-names.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
Synopsys DesignWare AXI DMA Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "snps,axi-dma-1.01a"
|
||||
- reg: Address range of the DMAC registers. This should include
|
||||
all of the per-channel registers.
|
||||
- interrupt: Should contain the DMAC interrupt number.
|
||||
- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
that services interrupts for this device.
|
||||
- dma-channels: Number of channels supported by hardware.
|
||||
- snps,dma-masters: Number of AXI masters supported by the hardware.
|
||||
- snps,data-width: Maximum AXI data width supported by hardware.
|
||||
(0 - 8bits, 1 - 16bits, 2 - 32bits, ..., 6 - 512bits)
|
||||
- snps,priority: Priority of channel. Array size is equal to the number of
|
||||
dma-channels. Priority value must be programmed within [0:dma-channels-1]
|
||||
range. (0 - minimum priority)
|
||||
- snps,block-size: Maximum block size supported by the controller channel.
|
||||
Array size is equal to the number of dma-channels.
|
||||
|
||||
Optional properties:
|
||||
- snps,axi-max-burst-len: Restrict master AXI burst length by value specified
|
||||
in this property. If this property is missing the maximum AXI burst length
|
||||
supported by DMAC is used. [1:256]
|
||||
|
||||
Example:
|
||||
|
||||
dmac: dma-controller@80000 {
|
||||
compatible = "snps,axi-dma-1.01a";
|
||||
reg = <0x80000 0x400>;
|
||||
clocks = <&core_clk>, <&cfgr_clk>;
|
||||
clock-names = "core-clk", "cfgr-clk";
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <27>;
|
||||
|
||||
dma-channels = <4>;
|
||||
snps,dma-masters = <2>;
|
||||
snps,data-width = <3>;
|
||||
snps,block-size = <4096 4096 4096 4096>;
|
||||
snps,priority = <0 1 2 3>;
|
||||
snps,axi-max-burst-len = <16>;
|
||||
};
|
|
@ -62,14 +62,14 @@ channel: a phandle to the DMA controller plus the following four integer cells:
|
|||
0x1: medium
|
||||
0x2: high
|
||||
0x3: very high
|
||||
4. A 32bit mask specifying the DMA FIFO threshold configuration which are device
|
||||
dependent:
|
||||
-bit 0-1: Fifo threshold
|
||||
4. A 32bit bitfield value specifying DMA features which are device dependent:
|
||||
-bit 0-1: DMA FIFO threshold selection
|
||||
0x0: 1/4 full FIFO
|
||||
0x1: 1/2 full FIFO
|
||||
0x2: 3/4 full FIFO
|
||||
0x3: full FIFO
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
usart1: serial@40011000 {
|
||||
|
|
15
MAINTAINERS
15
MAINTAINERS
|
@ -8859,6 +8859,15 @@ M: Sean Wang <sean.wang@mediatek.com>
|
|||
S: Maintained
|
||||
F: drivers/media/rc/mtk-cir.c
|
||||
|
||||
MEDIATEK DMA DRIVER
|
||||
M: Sean Wang <sean.wang@mediatek.com>
|
||||
L: dmaengine@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/dma/mtk-*
|
||||
F: drivers/dma/mediatek/
|
||||
|
||||
MEDIATEK PMIC LED DRIVER
|
||||
M: Sean Wang <sean.wang@mediatek.com>
|
||||
S: Maintained
|
||||
|
@ -13482,6 +13491,12 @@ S: Maintained
|
|||
F: drivers/gpio/gpio-dwapb.c
|
||||
F: Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
|
||||
|
||||
SYNOPSYS DESIGNWARE AXI DMAC DRIVER
|
||||
M: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
S: Maintained
|
||||
F: drivers/dma/dwi-axi-dmac/
|
||||
F: Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.txt
|
||||
|
||||
SYNOPSYS DESIGNWARE DMAC DRIVER
|
||||
M: Viresh Kumar <vireshk@kernel.org>
|
||||
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
|
|
|
@ -187,6 +187,16 @@ config DMA_SUN6I
|
|||
help
|
||||
Support for the DMA engine first found in Allwinner A31 SoCs.
|
||||
|
||||
config DW_AXI_DMAC
|
||||
tristate "Synopsys DesignWare AXI DMA support"
|
||||
depends on OF || COMPILE_TEST
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
Enable support for Synopsys DesignWare AXI DMA controller.
|
||||
NOTE: This driver wasn't tested on 64 bit platform because
|
||||
of lack 64 bit platform with Synopsys DW AXI DMAC.
|
||||
|
||||
config EP93XX_DMA
|
||||
bool "Cirrus Logic EP93xx DMA support"
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
|
@ -633,6 +643,8 @@ config ZX_DMA
|
|||
# driver files
|
||||
source "drivers/dma/bestcomm/Kconfig"
|
||||
|
||||
source "drivers/dma/mediatek/Kconfig"
|
||||
|
||||
source "drivers/dma/qcom/Kconfig"
|
||||
|
||||
source "drivers/dma/dw/Kconfig"
|
||||
|
|
|
@ -28,6 +28,7 @@ obj-$(CONFIG_DMA_OMAP) += omap-dma.o
|
|||
obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
|
||||
obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
|
||||
obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
|
||||
obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
|
||||
obj-$(CONFIG_DW_DMAC_CORE) += dw/
|
||||
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
|
||||
obj-$(CONFIG_FSL_DMA) += fsldma.o
|
||||
|
@ -75,5 +76,6 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
|
|||
obj-$(CONFIG_ZX_DMA) += zx_dma.o
|
||||
obj-$(CONFIG_ST_FDMA) += st_fdma.o
|
||||
|
||||
obj-y += mediatek/
|
||||
obj-y += qcom/
|
||||
obj-y += xilinx/
|
||||
|
|
|
@ -1471,10 +1471,10 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
|||
for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
|
||||
check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
|
||||
rmb();
|
||||
initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
|
||||
rmb();
|
||||
cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
|
||||
rmb();
|
||||
initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
|
||||
rmb();
|
||||
cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
|
||||
rmb();
|
||||
|
||||
|
|
|
@ -74,7 +74,11 @@ MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
|
|||
|
||||
static bool noverify;
|
||||
module_param(noverify, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(noverify, "Disable random data setup and verification");
|
||||
MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)");
|
||||
|
||||
static bool norandom;
|
||||
module_param(norandom, bool, 0644);
|
||||
MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)");
|
||||
|
||||
static bool verbose;
|
||||
module_param(verbose, bool, S_IRUGO | S_IWUSR);
|
||||
|
@ -103,6 +107,7 @@ struct dmatest_params {
|
|||
unsigned int pq_sources;
|
||||
int timeout;
|
||||
bool noverify;
|
||||
bool norandom;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -575,7 +580,7 @@ static int dmatest_func(void *data)
|
|||
break;
|
||||
}
|
||||
|
||||
if (params->noverify)
|
||||
if (params->norandom)
|
||||
len = params->buf_size;
|
||||
else
|
||||
len = dmatest_random() % params->buf_size + 1;
|
||||
|
@ -586,17 +591,19 @@ static int dmatest_func(void *data)
|
|||
|
||||
total_len += len;
|
||||
|
||||
if (params->noverify) {
|
||||
if (params->norandom) {
|
||||
src_off = 0;
|
||||
dst_off = 0;
|
||||
} else {
|
||||
start = ktime_get();
|
||||
src_off = dmatest_random() % (params->buf_size - len + 1);
|
||||
dst_off = dmatest_random() % (params->buf_size - len + 1);
|
||||
|
||||
src_off = (src_off >> align) << align;
|
||||
dst_off = (dst_off >> align) << align;
|
||||
}
|
||||
|
||||
if (!params->noverify) {
|
||||
start = ktime_get();
|
||||
dmatest_init_srcs(thread->srcs, src_off, len,
|
||||
params->buf_size, is_memset);
|
||||
dmatest_init_dsts(thread->dsts, dst_off, len,
|
||||
|
@ -975,6 +982,7 @@ static void run_threaded_test(struct dmatest_info *info)
|
|||
params->pq_sources = pq_sources;
|
||||
params->timeout = timeout;
|
||||
params->noverify = noverify;
|
||||
params->norandom = norandom;
|
||||
|
||||
request_channels(info, DMA_MEMCPY);
|
||||
request_channels(info, DMA_MEMSET);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac-platform.o
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,334 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// (C) 2017-2018 Synopsys, Inc. (www.synopsys.com)
|
||||
|
||||
/*
|
||||
* Synopsys DesignWare AXI DMA Controller driver.
|
||||
*
|
||||
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
*/
|
||||
|
||||
#ifndef _AXI_DMA_PLATFORM_H
|
||||
#define _AXI_DMA_PLATFORM_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "../virt-dma.h"
|
||||
|
||||
#define DMAC_MAX_CHANNELS 8
|
||||
#define DMAC_MAX_MASTERS 2
|
||||
#define DMAC_MAX_BLK_SIZE 0x200000
|
||||
|
||||
struct dw_axi_dma_hcfg {
|
||||
u32 nr_channels;
|
||||
u32 nr_masters;
|
||||
u32 m_data_width;
|
||||
u32 block_size[DMAC_MAX_CHANNELS];
|
||||
u32 priority[DMAC_MAX_CHANNELS];
|
||||
/* maximum supported axi burst length */
|
||||
u32 axi_rw_burst_len;
|
||||
bool restrict_axi_burst_len;
|
||||
};
|
||||
|
||||
struct axi_dma_chan {
|
||||
struct axi_dma_chip *chip;
|
||||
void __iomem *chan_regs;
|
||||
u8 id;
|
||||
atomic_t descs_allocated;
|
||||
|
||||
struct virt_dma_chan vc;
|
||||
|
||||
/* these other elements are all protected by vc.lock */
|
||||
bool is_paused;
|
||||
};
|
||||
|
||||
struct dw_axi_dma {
|
||||
struct dma_device dma;
|
||||
struct dw_axi_dma_hcfg *hdata;
|
||||
struct dma_pool *desc_pool;
|
||||
|
||||
/* channels */
|
||||
struct axi_dma_chan *chan;
|
||||
};
|
||||
|
||||
struct axi_dma_chip {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
void __iomem *regs;
|
||||
struct clk *core_clk;
|
||||
struct clk *cfgr_clk;
|
||||
struct dw_axi_dma *dw;
|
||||
};
|
||||
|
||||
/* LLI == Linked List Item */
|
||||
struct __packed axi_dma_lli {
|
||||
__le64 sar;
|
||||
__le64 dar;
|
||||
__le32 block_ts_lo;
|
||||
__le32 block_ts_hi;
|
||||
__le64 llp;
|
||||
__le32 ctl_lo;
|
||||
__le32 ctl_hi;
|
||||
__le32 sstat;
|
||||
__le32 dstat;
|
||||
__le32 status_lo;
|
||||
__le32 ststus_hi;
|
||||
__le32 reserved_lo;
|
||||
__le32 reserved_hi;
|
||||
};
|
||||
|
||||
struct axi_dma_desc {
|
||||
struct axi_dma_lli lli;
|
||||
|
||||
struct virt_dma_desc vd;
|
||||
struct axi_dma_chan *chan;
|
||||
struct list_head xfer_list;
|
||||
};
|
||||
|
||||
static inline struct device *dchan2dev(struct dma_chan *dchan)
|
||||
{
|
||||
return &dchan->dev->device;
|
||||
}
|
||||
|
||||
static inline struct device *chan2dev(struct axi_dma_chan *chan)
|
||||
{
|
||||
return &chan->vc.chan.dev->device;
|
||||
}
|
||||
|
||||
static inline struct axi_dma_desc *vd_to_axi_desc(struct virt_dma_desc *vd)
|
||||
{
|
||||
return container_of(vd, struct axi_dma_desc, vd);
|
||||
}
|
||||
|
||||
static inline struct axi_dma_chan *vc_to_axi_dma_chan(struct virt_dma_chan *vc)
|
||||
{
|
||||
return container_of(vc, struct axi_dma_chan, vc);
|
||||
}
|
||||
|
||||
static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
|
||||
{
|
||||
return vc_to_axi_dma_chan(to_virt_chan(dchan));
|
||||
}
|
||||
|
||||
|
||||
#define COMMON_REG_LEN 0x100
|
||||
#define CHAN_REG_LEN 0x100
|
||||
|
||||
/* Common registers offset */
|
||||
#define DMAC_ID 0x000 /* R DMAC ID */
|
||||
#define DMAC_COMPVER 0x008 /* R DMAC Component Version */
|
||||
#define DMAC_CFG 0x010 /* R/W DMAC Configuration */
|
||||
#define DMAC_CHEN 0x018 /* R/W DMAC Channel Enable */
|
||||
#define DMAC_CHEN_L 0x018 /* R/W DMAC Channel Enable 00-31 */
|
||||
#define DMAC_CHEN_H 0x01C /* R/W DMAC Channel Enable 32-63 */
|
||||
#define DMAC_INTSTATUS 0x030 /* R DMAC Interrupt Status */
|
||||
#define DMAC_COMMON_INTCLEAR 0x038 /* W DMAC Interrupt Clear */
|
||||
#define DMAC_COMMON_INTSTATUS_ENA 0x040 /* R DMAC Interrupt Status Enable */
|
||||
#define DMAC_COMMON_INTSIGNAL_ENA 0x048 /* R/W DMAC Interrupt Signal Enable */
|
||||
#define DMAC_COMMON_INTSTATUS 0x050 /* R DMAC Interrupt Status */
|
||||
#define DMAC_RESET 0x058 /* R DMAC Reset Register1 */
|
||||
|
||||
/* DMA channel registers offset */
|
||||
#define CH_SAR 0x000 /* R/W Chan Source Address */
|
||||
#define CH_DAR 0x008 /* R/W Chan Destination Address */
|
||||
#define CH_BLOCK_TS 0x010 /* R/W Chan Block Transfer Size */
|
||||
#define CH_CTL 0x018 /* R/W Chan Control */
|
||||
#define CH_CTL_L 0x018 /* R/W Chan Control 00-31 */
|
||||
#define CH_CTL_H 0x01C /* R/W Chan Control 32-63 */
|
||||
#define CH_CFG 0x020 /* R/W Chan Configuration */
|
||||
#define CH_CFG_L 0x020 /* R/W Chan Configuration 00-31 */
|
||||
#define CH_CFG_H 0x024 /* R/W Chan Configuration 32-63 */
|
||||
#define CH_LLP 0x028 /* R/W Chan Linked List Pointer */
|
||||
#define CH_STATUS 0x030 /* R Chan Status */
|
||||
#define CH_SWHSSRC 0x038 /* R/W Chan SW Handshake Source */
|
||||
#define CH_SWHSDST 0x040 /* R/W Chan SW Handshake Destination */
|
||||
#define CH_BLK_TFR_RESUMEREQ 0x048 /* W Chan Block Transfer Resume Req */
|
||||
#define CH_AXI_ID 0x050 /* R/W Chan AXI ID */
|
||||
#define CH_AXI_QOS 0x058 /* R/W Chan AXI QOS */
|
||||
#define CH_SSTAT 0x060 /* R Chan Source Status */
|
||||
#define CH_DSTAT 0x068 /* R Chan Destination Status */
|
||||
#define CH_SSTATAR 0x070 /* R/W Chan Source Status Fetch Addr */
|
||||
#define CH_DSTATAR 0x078 /* R/W Chan Destination Status Fetch Addr */
|
||||
#define CH_INTSTATUS_ENA 0x080 /* R/W Chan Interrupt Status Enable */
|
||||
#define CH_INTSTATUS 0x088 /* R/W Chan Interrupt Status */
|
||||
#define CH_INTSIGNAL_ENA 0x090 /* R/W Chan Interrupt Signal Enable */
|
||||
#define CH_INTCLEAR 0x098 /* W Chan Interrupt Clear */
|
||||
|
||||
|
||||
/* DMAC_CFG */
|
||||
#define DMAC_EN_POS 0
|
||||
#define DMAC_EN_MASK BIT(DMAC_EN_POS)
|
||||
|
||||
#define INT_EN_POS 1
|
||||
#define INT_EN_MASK BIT(INT_EN_POS)
|
||||
|
||||
#define DMAC_CHAN_EN_SHIFT 0
|
||||
#define DMAC_CHAN_EN_WE_SHIFT 8
|
||||
|
||||
#define DMAC_CHAN_SUSP_SHIFT 16
|
||||
#define DMAC_CHAN_SUSP_WE_SHIFT 24
|
||||
|
||||
/* CH_CTL_H */
|
||||
#define CH_CTL_H_ARLEN_EN BIT(6)
|
||||
#define CH_CTL_H_ARLEN_POS 7
|
||||
#define CH_CTL_H_AWLEN_EN BIT(15)
|
||||
#define CH_CTL_H_AWLEN_POS 16
|
||||
|
||||
enum {
|
||||
DWAXIDMAC_ARWLEN_1 = 0,
|
||||
DWAXIDMAC_ARWLEN_2 = 1,
|
||||
DWAXIDMAC_ARWLEN_4 = 3,
|
||||
DWAXIDMAC_ARWLEN_8 = 7,
|
||||
DWAXIDMAC_ARWLEN_16 = 15,
|
||||
DWAXIDMAC_ARWLEN_32 = 31,
|
||||
DWAXIDMAC_ARWLEN_64 = 63,
|
||||
DWAXIDMAC_ARWLEN_128 = 127,
|
||||
DWAXIDMAC_ARWLEN_256 = 255,
|
||||
DWAXIDMAC_ARWLEN_MIN = DWAXIDMAC_ARWLEN_1,
|
||||
DWAXIDMAC_ARWLEN_MAX = DWAXIDMAC_ARWLEN_256
|
||||
};
|
||||
|
||||
#define CH_CTL_H_LLI_LAST BIT(30)
|
||||
#define CH_CTL_H_LLI_VALID BIT(31)
|
||||
|
||||
/* CH_CTL_L */
|
||||
#define CH_CTL_L_LAST_WRITE_EN BIT(30)
|
||||
|
||||
#define CH_CTL_L_DST_MSIZE_POS 18
|
||||
#define CH_CTL_L_SRC_MSIZE_POS 14
|
||||
|
||||
enum {
|
||||
DWAXIDMAC_BURST_TRANS_LEN_1 = 0,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_4,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_8,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_16,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_32,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_64,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_128,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_256,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_512,
|
||||
DWAXIDMAC_BURST_TRANS_LEN_1024
|
||||
};
|
||||
|
||||
#define CH_CTL_L_DST_WIDTH_POS 11
|
||||
#define CH_CTL_L_SRC_WIDTH_POS 8
|
||||
|
||||
#define CH_CTL_L_DST_INC_POS 6
|
||||
#define CH_CTL_L_SRC_INC_POS 4
|
||||
enum {
|
||||
DWAXIDMAC_CH_CTL_L_INC = 0,
|
||||
DWAXIDMAC_CH_CTL_L_NOINC
|
||||
};
|
||||
|
||||
#define CH_CTL_L_DST_MAST BIT(2)
|
||||
#define CH_CTL_L_SRC_MAST BIT(0)
|
||||
|
||||
/* CH_CFG_H */
|
||||
#define CH_CFG_H_PRIORITY_POS 17
|
||||
#define CH_CFG_H_HS_SEL_DST_POS 4
|
||||
#define CH_CFG_H_HS_SEL_SRC_POS 3
|
||||
enum {
|
||||
DWAXIDMAC_HS_SEL_HW = 0,
|
||||
DWAXIDMAC_HS_SEL_SW
|
||||
};
|
||||
|
||||
#define CH_CFG_H_TT_FC_POS 0
|
||||
enum {
|
||||
DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC = 0,
|
||||
DWAXIDMAC_TT_FC_MEM_TO_PER_DMAC,
|
||||
DWAXIDMAC_TT_FC_PER_TO_MEM_DMAC,
|
||||
DWAXIDMAC_TT_FC_PER_TO_PER_DMAC,
|
||||
DWAXIDMAC_TT_FC_PER_TO_MEM_SRC,
|
||||
DWAXIDMAC_TT_FC_PER_TO_PER_SRC,
|
||||
DWAXIDMAC_TT_FC_MEM_TO_PER_DST,
|
||||
DWAXIDMAC_TT_FC_PER_TO_PER_DST
|
||||
};
|
||||
|
||||
/* CH_CFG_L */
|
||||
#define CH_CFG_L_DST_MULTBLK_TYPE_POS 2
|
||||
#define CH_CFG_L_SRC_MULTBLK_TYPE_POS 0
|
||||
enum {
|
||||
DWAXIDMAC_MBLK_TYPE_CONTIGUOUS = 0,
|
||||
DWAXIDMAC_MBLK_TYPE_RELOAD,
|
||||
DWAXIDMAC_MBLK_TYPE_SHADOW_REG,
|
||||
DWAXIDMAC_MBLK_TYPE_LL
|
||||
};
|
||||
|
||||
/**
|
||||
* DW AXI DMA channel interrupts
|
||||
*
|
||||
* @DWAXIDMAC_IRQ_NONE: Bitmask of no one interrupt
|
||||
* @DWAXIDMAC_IRQ_BLOCK_TRF: Block transfer complete
|
||||
* @DWAXIDMAC_IRQ_DMA_TRF: Dma transfer complete
|
||||
* @DWAXIDMAC_IRQ_SRC_TRAN: Source transaction complete
|
||||
* @DWAXIDMAC_IRQ_DST_TRAN: Destination transaction complete
|
||||
* @DWAXIDMAC_IRQ_SRC_DEC_ERR: Source decode error
|
||||
* @DWAXIDMAC_IRQ_DST_DEC_ERR: Destination decode error
|
||||
* @DWAXIDMAC_IRQ_SRC_SLV_ERR: Source slave error
|
||||
* @DWAXIDMAC_IRQ_DST_SLV_ERR: Destination slave error
|
||||
* @DWAXIDMAC_IRQ_LLI_RD_DEC_ERR: LLI read decode error
|
||||
* @DWAXIDMAC_IRQ_LLI_WR_DEC_ERR: LLI write decode error
|
||||
* @DWAXIDMAC_IRQ_LLI_RD_SLV_ERR: LLI read slave error
|
||||
* @DWAXIDMAC_IRQ_LLI_WR_SLV_ERR: LLI write slave error
|
||||
* @DWAXIDMAC_IRQ_INVALID_ERR: LLI invalid error or Shadow register error
|
||||
* @DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR: Slave Interface Multiblock type error
|
||||
* @DWAXIDMAC_IRQ_DEC_ERR: Slave Interface decode error
|
||||
* @DWAXIDMAC_IRQ_WR2RO_ERR: Slave Interface write to read only error
|
||||
* @DWAXIDMAC_IRQ_RD2RWO_ERR: Slave Interface read to write only error
|
||||
* @DWAXIDMAC_IRQ_WRONCHEN_ERR: Slave Interface write to channel error
|
||||
* @DWAXIDMAC_IRQ_SHADOWREG_ERR: Slave Interface shadow reg error
|
||||
* @DWAXIDMAC_IRQ_WRONHOLD_ERR: Slave Interface hold error
|
||||
* @DWAXIDMAC_IRQ_LOCK_CLEARED: Lock Cleared Status
|
||||
* @DWAXIDMAC_IRQ_SRC_SUSPENDED: Source Suspended Status
|
||||
* @DWAXIDMAC_IRQ_SUSPENDED: Channel Suspended Status
|
||||
* @DWAXIDMAC_IRQ_DISABLED: Channel Disabled Status
|
||||
* @DWAXIDMAC_IRQ_ABORTED: Channel Aborted Status
|
||||
* @DWAXIDMAC_IRQ_ALL_ERR: Bitmask of all error interrupts
|
||||
* @DWAXIDMAC_IRQ_ALL: Bitmask of all interrupts
|
||||
*/
|
||||
enum {
|
||||
DWAXIDMAC_IRQ_NONE = 0,
|
||||
DWAXIDMAC_IRQ_BLOCK_TRF = BIT(0),
|
||||
DWAXIDMAC_IRQ_DMA_TRF = BIT(1),
|
||||
DWAXIDMAC_IRQ_SRC_TRAN = BIT(3),
|
||||
DWAXIDMAC_IRQ_DST_TRAN = BIT(4),
|
||||
DWAXIDMAC_IRQ_SRC_DEC_ERR = BIT(5),
|
||||
DWAXIDMAC_IRQ_DST_DEC_ERR = BIT(6),
|
||||
DWAXIDMAC_IRQ_SRC_SLV_ERR = BIT(7),
|
||||
DWAXIDMAC_IRQ_DST_SLV_ERR = BIT(8),
|
||||
DWAXIDMAC_IRQ_LLI_RD_DEC_ERR = BIT(9),
|
||||
DWAXIDMAC_IRQ_LLI_WR_DEC_ERR = BIT(10),
|
||||
DWAXIDMAC_IRQ_LLI_RD_SLV_ERR = BIT(11),
|
||||
DWAXIDMAC_IRQ_LLI_WR_SLV_ERR = BIT(12),
|
||||
DWAXIDMAC_IRQ_INVALID_ERR = BIT(13),
|
||||
DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR = BIT(14),
|
||||
DWAXIDMAC_IRQ_DEC_ERR = BIT(16),
|
||||
DWAXIDMAC_IRQ_WR2RO_ERR = BIT(17),
|
||||
DWAXIDMAC_IRQ_RD2RWO_ERR = BIT(18),
|
||||
DWAXIDMAC_IRQ_WRONCHEN_ERR = BIT(19),
|
||||
DWAXIDMAC_IRQ_SHADOWREG_ERR = BIT(20),
|
||||
DWAXIDMAC_IRQ_WRONHOLD_ERR = BIT(21),
|
||||
DWAXIDMAC_IRQ_LOCK_CLEARED = BIT(27),
|
||||
DWAXIDMAC_IRQ_SRC_SUSPENDED = BIT(28),
|
||||
DWAXIDMAC_IRQ_SUSPENDED = BIT(29),
|
||||
DWAXIDMAC_IRQ_DISABLED = BIT(30),
|
||||
DWAXIDMAC_IRQ_ABORTED = BIT(31),
|
||||
DWAXIDMAC_IRQ_ALL_ERR = (GENMASK(21, 16) | GENMASK(14, 5)),
|
||||
DWAXIDMAC_IRQ_ALL = GENMASK(31, 0)
|
||||
};
|
||||
|
||||
enum {
|
||||
DWAXIDMAC_TRANS_WIDTH_8 = 0,
|
||||
DWAXIDMAC_TRANS_WIDTH_16,
|
||||
DWAXIDMAC_TRANS_WIDTH_32,
|
||||
DWAXIDMAC_TRANS_WIDTH_64,
|
||||
DWAXIDMAC_TRANS_WIDTH_128,
|
||||
DWAXIDMAC_TRANS_WIDTH_256,
|
||||
DWAXIDMAC_TRANS_WIDTH_512,
|
||||
DWAXIDMAC_TRANS_WIDTH_MAX = DWAXIDMAC_TRANS_WIDTH_512
|
||||
};
|
||||
|
||||
#endif /* _AXI_DMA_PLATFORM_H */
|
|
@ -1876,6 +1876,11 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
|
|||
|
||||
if (memcpy_channels) {
|
||||
m_ddev = devm_kzalloc(ecc->dev, sizeof(*m_ddev), GFP_KERNEL);
|
||||
if (!m_ddev) {
|
||||
dev_warn(ecc->dev, "memcpy is disabled due to OoM\n");
|
||||
memcpy_channels = NULL;
|
||||
goto ch_setup;
|
||||
}
|
||||
ecc->dma_memcpy = m_ddev;
|
||||
|
||||
dma_cap_zero(m_ddev->cap_mask);
|
||||
|
@ -1903,6 +1908,7 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
|
|||
dev_info(ecc->dev, "memcpy is disabled\n");
|
||||
}
|
||||
|
||||
ch_setup:
|
||||
for (i = 0; i < ecc->num_channels; i++) {
|
||||
struct edma_chan *echan = &ecc->slave_chans[i];
|
||||
echan->ch_num = EDMA_CTLR_CHAN(ecc->id, i);
|
||||
|
|
|
@ -338,6 +338,7 @@ struct sdma_channel {
|
|||
unsigned int chn_real_count;
|
||||
struct tasklet_struct tasklet;
|
||||
struct imx_dma_data data;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
#define IMX_DMA_SG_LOOP BIT(0)
|
||||
|
@ -596,7 +597,14 @@ static int sdma_config_ownership(struct sdma_channel *sdmac,
|
|||
|
||||
static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sdma_channel *sdmac = &sdma->channel[channel];
|
||||
|
||||
writel(BIT(channel), sdma->regs + SDMA_H_START);
|
||||
|
||||
spin_lock_irqsave(&sdmac->lock, flags);
|
||||
sdmac->enabled = true;
|
||||
spin_unlock_irqrestore(&sdmac->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -685,6 +693,14 @@ static void sdma_update_channel_loop(struct sdma_channel *sdmac)
|
|||
struct sdma_buffer_descriptor *bd;
|
||||
int error = 0;
|
||||
enum dma_status old_status = sdmac->status;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sdmac->lock, flags);
|
||||
if (!sdmac->enabled) {
|
||||
spin_unlock_irqrestore(&sdmac->lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock_irqrestore(&sdmac->lock, flags);
|
||||
|
||||
/*
|
||||
* loop mode. Iterate over descriptors, re-setup them and
|
||||
|
@ -938,10 +954,15 @@ static int sdma_disable_channel(struct dma_chan *chan)
|
|||
struct sdma_channel *sdmac = to_sdma_chan(chan);
|
||||
struct sdma_engine *sdma = sdmac->sdma;
|
||||
int channel = sdmac->channel;
|
||||
unsigned long flags;
|
||||
|
||||
writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP);
|
||||
sdmac->status = DMA_ERROR;
|
||||
|
||||
spin_lock_irqsave(&sdmac->lock, flags);
|
||||
sdmac->enabled = false;
|
||||
spin_unlock_irqrestore(&sdmac->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
config MTK_HSDMA
|
||||
tristate "MediaTek High-Speed DMA controller support"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
---help---
|
||||
Enable support for High-Speed DMA controller on MediaTek
|
||||
SoCs.
|
||||
|
||||
This controller provides the channels which is dedicated to
|
||||
memory-to-memory transfer to offload from CPU through ring-
|
||||
based descriptor management.
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
|
File diff suppressed because it is too large
Load Diff
|
@ -1510,7 +1510,7 @@ static void pl330_dotask(unsigned long data)
|
|||
/* Returns 1 if state was updated, 0 otherwise */
|
||||
static int pl330_update(struct pl330_dmac *pl330)
|
||||
{
|
||||
struct dma_pl330_desc *descdone, *tmp;
|
||||
struct dma_pl330_desc *descdone;
|
||||
unsigned long flags;
|
||||
void __iomem *regs;
|
||||
u32 val;
|
||||
|
@ -1588,7 +1588,9 @@ static int pl330_update(struct pl330_dmac *pl330)
|
|||
}
|
||||
|
||||
/* Now that we are in no hurry, do the callbacks */
|
||||
list_for_each_entry_safe(descdone, tmp, &pl330->req_done, rqd) {
|
||||
while (!list_empty(&pl330->req_done)) {
|
||||
descdone = list_first_entry(&pl330->req_done,
|
||||
struct dma_pl330_desc, rqd);
|
||||
list_del(&descdone->rqd);
|
||||
spin_unlock_irqrestore(&pl330->lock, flags);
|
||||
dma_pl330_rqcb(descdone, PL330_ERR_NONE);
|
||||
|
|
|
@ -393,6 +393,7 @@ struct bam_device {
|
|||
struct device_dma_parameters dma_parms;
|
||||
struct bam_chan *channels;
|
||||
u32 num_channels;
|
||||
u32 num_ees;
|
||||
|
||||
/* execution environment ID, from DT */
|
||||
u32 ee;
|
||||
|
@ -934,12 +935,15 @@ static void bam_apply_new_config(struct bam_chan *bchan,
|
|||
struct bam_device *bdev = bchan->bdev;
|
||||
u32 maxburst;
|
||||
|
||||
if (dir == DMA_DEV_TO_MEM)
|
||||
maxburst = bchan->slave.src_maxburst;
|
||||
else
|
||||
maxburst = bchan->slave.dst_maxburst;
|
||||
if (!bdev->controlled_remotely) {
|
||||
if (dir == DMA_DEV_TO_MEM)
|
||||
maxburst = bchan->slave.src_maxburst;
|
||||
else
|
||||
maxburst = bchan->slave.dst_maxburst;
|
||||
|
||||
writel_relaxed(maxburst, bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
|
||||
writel_relaxed(maxburst,
|
||||
bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
|
||||
}
|
||||
|
||||
bchan->reconfigure = 0;
|
||||
}
|
||||
|
@ -1128,15 +1132,19 @@ static int bam_init(struct bam_device *bdev)
|
|||
u32 val;
|
||||
|
||||
/* read revision and configuration information */
|
||||
val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION)) >> NUM_EES_SHIFT;
|
||||
val &= NUM_EES_MASK;
|
||||
if (!bdev->num_ees) {
|
||||
val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION));
|
||||
bdev->num_ees = (val >> NUM_EES_SHIFT) & NUM_EES_MASK;
|
||||
}
|
||||
|
||||
/* check that configured EE is within range */
|
||||
if (bdev->ee >= val)
|
||||
if (bdev->ee >= bdev->num_ees)
|
||||
return -EINVAL;
|
||||
|
||||
val = readl_relaxed(bam_addr(bdev, 0, BAM_NUM_PIPES));
|
||||
bdev->num_channels = val & BAM_NUM_PIPES_MASK;
|
||||
if (!bdev->num_channels) {
|
||||
val = readl_relaxed(bam_addr(bdev, 0, BAM_NUM_PIPES));
|
||||
bdev->num_channels = val & BAM_NUM_PIPES_MASK;
|
||||
}
|
||||
|
||||
if (bdev->controlled_remotely)
|
||||
return 0;
|
||||
|
@ -1232,9 +1240,25 @@ static int bam_dma_probe(struct platform_device *pdev)
|
|||
bdev->controlled_remotely = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,controlled-remotely");
|
||||
|
||||
if (bdev->controlled_remotely) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "num-channels",
|
||||
&bdev->num_channels);
|
||||
if (ret)
|
||||
dev_err(bdev->dev, "num-channels unspecified in dt\n");
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-ees",
|
||||
&bdev->num_ees);
|
||||
if (ret)
|
||||
dev_err(bdev->dev, "num-ees unspecified in dt\n");
|
||||
}
|
||||
|
||||
bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
|
||||
if (IS_ERR(bdev->bamclk))
|
||||
return PTR_ERR(bdev->bamclk);
|
||||
if (IS_ERR(bdev->bamclk)) {
|
||||
if (!bdev->controlled_remotely)
|
||||
return PTR_ERR(bdev->bamclk);
|
||||
|
||||
bdev->bamclk = NULL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(bdev->bamclk);
|
||||
if (ret) {
|
||||
|
@ -1309,6 +1333,11 @@ static int bam_dma_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_unregister_dma;
|
||||
|
||||
if (bdev->controlled_remotely) {
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pm_runtime_irq_safe(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, BAM_DMA_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
@ -1392,7 +1421,8 @@ static int __maybe_unused bam_dma_suspend(struct device *dev)
|
|||
{
|
||||
struct bam_device *bdev = dev_get_drvdata(dev);
|
||||
|
||||
pm_runtime_force_suspend(dev);
|
||||
if (!bdev->controlled_remotely)
|
||||
pm_runtime_force_suspend(dev);
|
||||
|
||||
clk_unprepare(bdev->bamclk);
|
||||
|
||||
|
@ -1408,7 +1438,8 @@ static int __maybe_unused bam_dma_resume(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_force_resume(dev);
|
||||
if (!bdev->controlled_remotely)
|
||||
pm_runtime_force_resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1301,8 +1301,17 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
|||
* If the cookie doesn't correspond to the currently running transfer
|
||||
* then the descriptor hasn't been processed yet, and the residue is
|
||||
* equal to the full descriptor size.
|
||||
* Also, a client driver is possible to call this function before
|
||||
* rcar_dmac_isr_channel_thread() runs. In this case, the "desc.running"
|
||||
* will be the next descriptor, and the done list will appear. So, if
|
||||
* the argument cookie matches the done list's cookie, we can assume
|
||||
* the residue is zero.
|
||||
*/
|
||||
if (cookie != desc->async_tx.cookie) {
|
||||
list_for_each_entry(desc, &chan->desc.done, node) {
|
||||
if (cookie == desc->async_tx.cookie)
|
||||
return 0;
|
||||
}
|
||||
list_for_each_entry(desc, &chan->desc.pending, node) {
|
||||
if (cookie == desc->async_tx.cookie)
|
||||
return desc->size;
|
||||
|
@ -1677,8 +1686,8 @@ static const struct dev_pm_ops rcar_dmac_pm = {
|
|||
* - Wait for the current transfer to complete and stop the device,
|
||||
* - Resume transfers, if any.
|
||||
*/
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*
|
||||
* Copyright (C) M'boumba Cedric Madianga 2015
|
||||
* Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
|
||||
* Pierre-Yves Mordret <pierre-yves.mordret@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
@ -33,9 +34,14 @@
|
|||
#define STM32_DMA_LIFCR 0x0008 /* DMA Low Int Flag Clear Reg */
|
||||
#define STM32_DMA_HIFCR 0x000c /* DMA High Int Flag Clear Reg */
|
||||
#define STM32_DMA_TCI BIT(5) /* Transfer Complete Interrupt */
|
||||
#define STM32_DMA_HTI BIT(4) /* Half Transfer Interrupt */
|
||||
#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
|
||||
#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
|
||||
#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
|
||||
#define STM32_DMA_MASKI (STM32_DMA_TCI \
|
||||
| STM32_DMA_TEI \
|
||||
| STM32_DMA_DMEI \
|
||||
| STM32_DMA_FEI)
|
||||
|
||||
/* DMA Stream x Configuration Register */
|
||||
#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
|
||||
|
@ -60,7 +66,8 @@
|
|||
#define STM32_DMA_SCR_PINC BIT(9) /* Peripheral increment mode */
|
||||
#define STM32_DMA_SCR_CIRC BIT(8) /* Circular mode */
|
||||
#define STM32_DMA_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */
|
||||
#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Cplete Int Enable*/
|
||||
#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Complete Int Enable
|
||||
*/
|
||||
#define STM32_DMA_SCR_TEIE BIT(2) /* Transfer Error Int Enable */
|
||||
#define STM32_DMA_SCR_DMEIE BIT(1) /* Direct Mode Err Int Enable */
|
||||
#define STM32_DMA_SCR_EN BIT(0) /* Stream Enable */
|
||||
|
@ -111,11 +118,24 @@
|
|||
#define STM32_DMA_FIFO_THRESHOLD_FULL 0x03
|
||||
|
||||
#define STM32_DMA_MAX_DATA_ITEMS 0xffff
|
||||
/*
|
||||
* Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter
|
||||
* gather at boundary. Thus it's safer to round down this value on FIFO
|
||||
* size (16 Bytes)
|
||||
*/
|
||||
#define STM32_DMA_ALIGNED_MAX_DATA_ITEMS \
|
||||
ALIGN_DOWN(STM32_DMA_MAX_DATA_ITEMS, 16)
|
||||
#define STM32_DMA_MAX_CHANNELS 0x08
|
||||
#define STM32_DMA_MAX_REQUEST_ID 0x08
|
||||
#define STM32_DMA_MAX_DATA_PARAM 0x03
|
||||
#define STM32_DMA_FIFO_SIZE 16 /* FIFO is 16 bytes */
|
||||
#define STM32_DMA_MIN_BURST 4
|
||||
#define STM32_DMA_MAX_BURST 16
|
||||
|
||||
/* DMA Features */
|
||||
#define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0)
|
||||
#define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK)
|
||||
|
||||
enum stm32_dma_width {
|
||||
STM32_DMA_BYTE,
|
||||
STM32_DMA_HALF_WORD,
|
||||
|
@ -129,11 +149,18 @@ enum stm32_dma_burst_size {
|
|||
STM32_DMA_BURST_INCR16,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_dma_cfg - STM32 DMA custom configuration
|
||||
* @channel_id: channel ID
|
||||
* @request_line: DMA request
|
||||
* @stream_config: 32bit mask specifying the DMA channel configuration
|
||||
* @features: 32bit mask specifying the DMA Feature list
|
||||
*/
|
||||
struct stm32_dma_cfg {
|
||||
u32 channel_id;
|
||||
u32 request_line;
|
||||
u32 stream_config;
|
||||
u32 threshold;
|
||||
u32 features;
|
||||
};
|
||||
|
||||
struct stm32_dma_chan_reg {
|
||||
|
@ -171,6 +198,9 @@ struct stm32_dma_chan {
|
|||
u32 next_sg;
|
||||
struct dma_slave_config dma_sconfig;
|
||||
struct stm32_dma_chan_reg chan_reg;
|
||||
u32 threshold;
|
||||
u32 mem_burst;
|
||||
u32 mem_width;
|
||||
};
|
||||
|
||||
struct stm32_dma_device {
|
||||
|
@ -235,6 +265,85 @@ static int stm32_dma_get_width(struct stm32_dma_chan *chan,
|
|||
}
|
||||
}
|
||||
|
||||
static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len,
|
||||
u32 threshold)
|
||||
{
|
||||
enum dma_slave_buswidth max_width;
|
||||
|
||||
if (threshold == STM32_DMA_FIFO_THRESHOLD_FULL)
|
||||
max_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
else
|
||||
max_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
|
||||
while ((buf_len < max_width || buf_len % max_width) &&
|
||||
max_width > DMA_SLAVE_BUSWIDTH_1_BYTE)
|
||||
max_width = max_width >> 1;
|
||||
|
||||
return max_width;
|
||||
}
|
||||
|
||||
static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold,
|
||||
enum dma_slave_buswidth width)
|
||||
{
|
||||
u32 remaining;
|
||||
|
||||
if (width != DMA_SLAVE_BUSWIDTH_UNDEFINED) {
|
||||
if (burst != 0) {
|
||||
/*
|
||||
* If number of beats fit in several whole bursts
|
||||
* this configuration is allowed.
|
||||
*/
|
||||
remaining = ((STM32_DMA_FIFO_SIZE / width) *
|
||||
(threshold + 1) / 4) % burst;
|
||||
|
||||
if (remaining == 0)
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold)
|
||||
{
|
||||
switch (threshold) {
|
||||
case STM32_DMA_FIFO_THRESHOLD_FULL:
|
||||
if (buf_len >= STM32_DMA_MAX_BURST)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case STM32_DMA_FIFO_THRESHOLD_HALFFULL:
|
||||
if (buf_len >= STM32_DMA_MAX_BURST / 2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold,
|
||||
enum dma_slave_buswidth width)
|
||||
{
|
||||
u32 best_burst = max_burst;
|
||||
|
||||
if (best_burst == 1 || !stm32_dma_is_burst_possible(buf_len, threshold))
|
||||
return 0;
|
||||
|
||||
while ((buf_len < best_burst * width && best_burst > 1) ||
|
||||
!stm32_dma_fifo_threshold_is_allowed(best_burst, threshold,
|
||||
width)) {
|
||||
if (best_burst > STM32_DMA_MIN_BURST)
|
||||
best_burst = best_burst >> 1;
|
||||
else
|
||||
best_burst = 0;
|
||||
}
|
||||
|
||||
return best_burst;
|
||||
}
|
||||
|
||||
static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
|
||||
{
|
||||
switch (maxburst) {
|
||||
|
@ -254,12 +363,12 @@ static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
|
|||
}
|
||||
|
||||
static void stm32_dma_set_fifo_config(struct stm32_dma_chan *chan,
|
||||
u32 src_maxburst, u32 dst_maxburst)
|
||||
u32 src_burst, u32 dst_burst)
|
||||
{
|
||||
chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK;
|
||||
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_DMEIE;
|
||||
|
||||
if ((!src_maxburst) && (!dst_maxburst)) {
|
||||
if (!src_burst && !dst_burst) {
|
||||
/* Using direct mode */
|
||||
chan->chan_reg.dma_scr |= STM32_DMA_SCR_DMEIE;
|
||||
} else {
|
||||
|
@ -300,7 +409,7 @@ static u32 stm32_dma_irq_status(struct stm32_dma_chan *chan)
|
|||
|
||||
flags = dma_isr >> (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
|
||||
|
||||
return flags;
|
||||
return flags & STM32_DMA_MASKI;
|
||||
}
|
||||
|
||||
static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
|
||||
|
@ -315,6 +424,7 @@ static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
|
|||
* If (ch % 4) is 2 or 3, left shift the mask by 16 bits.
|
||||
* If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits.
|
||||
*/
|
||||
flags &= STM32_DMA_MASKI;
|
||||
dma_ifcr = flags << (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
|
||||
|
||||
if (chan->id & 4)
|
||||
|
@ -429,6 +539,8 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan)
|
|||
dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr);
|
||||
}
|
||||
|
||||
static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan);
|
||||
|
||||
static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
|
||||
{
|
||||
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
|
||||
|
@ -471,6 +583,9 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
|
|||
if (status)
|
||||
stm32_dma_irq_clear(chan, status);
|
||||
|
||||
if (chan->desc->cyclic)
|
||||
stm32_dma_configure_next_sg(chan);
|
||||
|
||||
stm32_dma_dump_reg(chan);
|
||||
|
||||
/* Start DMA */
|
||||
|
@ -541,13 +656,29 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
|
|||
status = stm32_dma_irq_status(chan);
|
||||
scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
|
||||
|
||||
if ((status & STM32_DMA_TCI) && (scr & STM32_DMA_SCR_TCIE)) {
|
||||
if (status & STM32_DMA_TCI) {
|
||||
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
|
||||
stm32_dma_handle_chan_done(chan);
|
||||
|
||||
} else {
|
||||
if (scr & STM32_DMA_SCR_TCIE)
|
||||
stm32_dma_handle_chan_done(chan);
|
||||
status &= ~STM32_DMA_TCI;
|
||||
}
|
||||
if (status & STM32_DMA_HTI) {
|
||||
stm32_dma_irq_clear(chan, STM32_DMA_HTI);
|
||||
status &= ~STM32_DMA_HTI;
|
||||
}
|
||||
if (status & STM32_DMA_FEI) {
|
||||
stm32_dma_irq_clear(chan, STM32_DMA_FEI);
|
||||
status &= ~STM32_DMA_FEI;
|
||||
if (!(scr & STM32_DMA_SCR_EN))
|
||||
dev_err(chan2dev(chan), "FIFO Error\n");
|
||||
else
|
||||
dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
|
||||
}
|
||||
if (status) {
|
||||
stm32_dma_irq_clear(chan, status);
|
||||
dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
|
||||
if (!(scr & STM32_DMA_SCR_EN))
|
||||
dev_err(chan2dev(chan), "chan disabled by HW\n");
|
||||
}
|
||||
|
||||
spin_unlock(&chan->vchan.lock);
|
||||
|
@ -564,45 +695,59 @@ static void stm32_dma_issue_pending(struct dma_chan *c)
|
|||
if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) {
|
||||
dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan);
|
||||
stm32_dma_start_transfer(chan);
|
||||
if (chan->desc->cyclic)
|
||||
stm32_dma_configure_next_sg(chan);
|
||||
|
||||
}
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
}
|
||||
|
||||
static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
|
||||
enum dma_transfer_direction direction,
|
||||
enum dma_slave_buswidth *buswidth)
|
||||
enum dma_slave_buswidth *buswidth,
|
||||
u32 buf_len)
|
||||
{
|
||||
enum dma_slave_buswidth src_addr_width, dst_addr_width;
|
||||
int src_bus_width, dst_bus_width;
|
||||
int src_burst_size, dst_burst_size;
|
||||
u32 src_maxburst, dst_maxburst;
|
||||
u32 dma_scr = 0;
|
||||
u32 src_maxburst, dst_maxburst, src_best_burst, dst_best_burst;
|
||||
u32 dma_scr, threshold;
|
||||
|
||||
src_addr_width = chan->dma_sconfig.src_addr_width;
|
||||
dst_addr_width = chan->dma_sconfig.dst_addr_width;
|
||||
src_maxburst = chan->dma_sconfig.src_maxburst;
|
||||
dst_maxburst = chan->dma_sconfig.dst_maxburst;
|
||||
threshold = chan->threshold;
|
||||
|
||||
switch (direction) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
/* Set device data size */
|
||||
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
|
||||
if (dst_bus_width < 0)
|
||||
return dst_bus_width;
|
||||
|
||||
dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
|
||||
/* Set device burst size */
|
||||
dst_best_burst = stm32_dma_get_best_burst(buf_len,
|
||||
dst_maxburst,
|
||||
threshold,
|
||||
dst_addr_width);
|
||||
|
||||
dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
|
||||
if (dst_burst_size < 0)
|
||||
return dst_burst_size;
|
||||
|
||||
if (!src_addr_width)
|
||||
src_addr_width = dst_addr_width;
|
||||
|
||||
/* Set memory data size */
|
||||
src_addr_width = stm32_dma_get_max_width(buf_len, threshold);
|
||||
chan->mem_width = src_addr_width;
|
||||
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
|
||||
if (src_bus_width < 0)
|
||||
return src_bus_width;
|
||||
|
||||
src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
|
||||
/* Set memory burst size */
|
||||
src_maxburst = STM32_DMA_MAX_BURST;
|
||||
src_best_burst = stm32_dma_get_best_burst(buf_len,
|
||||
src_maxburst,
|
||||
threshold,
|
||||
src_addr_width);
|
||||
src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
|
||||
if (src_burst_size < 0)
|
||||
return src_burst_size;
|
||||
|
||||
|
@ -612,27 +757,46 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
|
|||
STM32_DMA_SCR_PBURST(dst_burst_size) |
|
||||
STM32_DMA_SCR_MBURST(src_burst_size);
|
||||
|
||||
/* Set FIFO threshold */
|
||||
chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
|
||||
chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
|
||||
|
||||
/* Set peripheral address */
|
||||
chan->chan_reg.dma_spar = chan->dma_sconfig.dst_addr;
|
||||
*buswidth = dst_addr_width;
|
||||
break;
|
||||
|
||||
case DMA_DEV_TO_MEM:
|
||||
/* Set device data size */
|
||||
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
|
||||
if (src_bus_width < 0)
|
||||
return src_bus_width;
|
||||
|
||||
src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
|
||||
/* Set device burst size */
|
||||
src_best_burst = stm32_dma_get_best_burst(buf_len,
|
||||
src_maxburst,
|
||||
threshold,
|
||||
src_addr_width);
|
||||
chan->mem_burst = src_best_burst;
|
||||
src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
|
||||
if (src_burst_size < 0)
|
||||
return src_burst_size;
|
||||
|
||||
if (!dst_addr_width)
|
||||
dst_addr_width = src_addr_width;
|
||||
|
||||
/* Set memory data size */
|
||||
dst_addr_width = stm32_dma_get_max_width(buf_len, threshold);
|
||||
chan->mem_width = dst_addr_width;
|
||||
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
|
||||
if (dst_bus_width < 0)
|
||||
return dst_bus_width;
|
||||
|
||||
dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
|
||||
/* Set memory burst size */
|
||||
dst_maxburst = STM32_DMA_MAX_BURST;
|
||||
dst_best_burst = stm32_dma_get_best_burst(buf_len,
|
||||
dst_maxburst,
|
||||
threshold,
|
||||
dst_addr_width);
|
||||
chan->mem_burst = dst_best_burst;
|
||||
dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
|
||||
if (dst_burst_size < 0)
|
||||
return dst_burst_size;
|
||||
|
||||
|
@ -642,6 +806,11 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
|
|||
STM32_DMA_SCR_PBURST(src_burst_size) |
|
||||
STM32_DMA_SCR_MBURST(dst_burst_size);
|
||||
|
||||
/* Set FIFO threshold */
|
||||
chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
|
||||
chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
|
||||
|
||||
/* Set peripheral address */
|
||||
chan->chan_reg.dma_spar = chan->dma_sconfig.src_addr;
|
||||
*buswidth = chan->dma_sconfig.src_addr_width;
|
||||
break;
|
||||
|
@ -651,8 +820,9 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
stm32_dma_set_fifo_config(chan, src_maxburst, dst_maxburst);
|
||||
stm32_dma_set_fifo_config(chan, src_best_burst, dst_best_burst);
|
||||
|
||||
/* Set DMA control register */
|
||||
chan->chan_reg.dma_scr &= ~(STM32_DMA_SCR_DIR_MASK |
|
||||
STM32_DMA_SCR_PSIZE_MASK | STM32_DMA_SCR_MSIZE_MASK |
|
||||
STM32_DMA_SCR_PBURST_MASK | STM32_DMA_SCR_MBURST_MASK);
|
||||
|
@ -692,10 +862,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
|
|||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Set peripheral flow controller */
|
||||
if (chan->dma_sconfig.device_fc)
|
||||
chan->chan_reg.dma_scr |= STM32_DMA_SCR_PFCTRL;
|
||||
|
@ -703,10 +869,15 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
|
|||
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
|
||||
sg_dma_len(sg));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
desc->sg_req[i].len = sg_dma_len(sg);
|
||||
|
||||
nb_data_items = desc->sg_req[i].len / buswidth;
|
||||
if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
|
||||
if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
|
||||
dev_err(chan2dev(chan), "nb items not supported\n");
|
||||
goto err;
|
||||
}
|
||||
|
@ -767,12 +938,12 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
|
||||
ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
nb_data_items = period_len / buswidth;
|
||||
if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
|
||||
if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
|
||||
dev_err(chan2dev(chan), "number of items not supported\n");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -816,35 +987,45 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
|
|||
dma_addr_t src, size_t len, unsigned long flags)
|
||||
{
|
||||
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
|
||||
u32 num_sgs;
|
||||
enum dma_slave_buswidth max_width;
|
||||
struct stm32_dma_desc *desc;
|
||||
size_t xfer_count, offset;
|
||||
u32 num_sgs, best_burst, dma_burst, threshold;
|
||||
int i;
|
||||
|
||||
num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS);
|
||||
num_sgs = DIV_ROUND_UP(len, STM32_DMA_ALIGNED_MAX_DATA_ITEMS);
|
||||
desc = stm32_dma_alloc_desc(num_sgs);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
threshold = chan->threshold;
|
||||
|
||||
for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) {
|
||||
xfer_count = min_t(size_t, len - offset,
|
||||
STM32_DMA_MAX_DATA_ITEMS);
|
||||
STM32_DMA_ALIGNED_MAX_DATA_ITEMS);
|
||||
|
||||
desc->sg_req[i].len = xfer_count;
|
||||
/* Compute best burst size */
|
||||
max_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
best_burst = stm32_dma_get_best_burst(len, STM32_DMA_MAX_BURST,
|
||||
threshold, max_width);
|
||||
dma_burst = stm32_dma_get_burst(chan, best_burst);
|
||||
|
||||
stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
|
||||
desc->sg_req[i].chan_reg.dma_scr =
|
||||
STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) |
|
||||
STM32_DMA_SCR_PBURST(dma_burst) |
|
||||
STM32_DMA_SCR_MBURST(dma_burst) |
|
||||
STM32_DMA_SCR_MINC |
|
||||
STM32_DMA_SCR_PINC |
|
||||
STM32_DMA_SCR_TCIE |
|
||||
STM32_DMA_SCR_TEIE;
|
||||
desc->sg_req[i].chan_reg.dma_sfcr = STM32_DMA_SFCR_DMDIS |
|
||||
STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL) |
|
||||
STM32_DMA_SFCR_FEIE;
|
||||
desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK;
|
||||
desc->sg_req[i].chan_reg.dma_sfcr |=
|
||||
STM32_DMA_SFCR_FTH(threshold);
|
||||
desc->sg_req[i].chan_reg.dma_spar = src + offset;
|
||||
desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset;
|
||||
desc->sg_req[i].chan_reg.dma_sndtr = xfer_count;
|
||||
desc->sg_req[i].len = xfer_count;
|
||||
}
|
||||
|
||||
desc->num_sgs = num_sgs;
|
||||
|
@ -869,6 +1050,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
|
|||
struct stm32_dma_desc *desc,
|
||||
u32 next_sg)
|
||||
{
|
||||
u32 modulo, burst_size;
|
||||
u32 residue = 0;
|
||||
int i;
|
||||
|
||||
|
@ -876,8 +1058,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
|
|||
* In cyclic mode, for the last period, residue = remaining bytes from
|
||||
* NDTR
|
||||
*/
|
||||
if (chan->desc->cyclic && next_sg == 0)
|
||||
return stm32_dma_get_remaining_bytes(chan);
|
||||
if (chan->desc->cyclic && next_sg == 0) {
|
||||
residue = stm32_dma_get_remaining_bytes(chan);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* For all other periods in cyclic mode, and in sg mode,
|
||||
|
@ -888,6 +1072,15 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
|
|||
residue += desc->sg_req[i].len;
|
||||
residue += stm32_dma_get_remaining_bytes(chan);
|
||||
|
||||
end:
|
||||
if (!chan->mem_burst)
|
||||
return residue;
|
||||
|
||||
burst_size = chan->mem_burst * chan->mem_width;
|
||||
modulo = residue % burst_size;
|
||||
if (modulo)
|
||||
residue = residue - modulo + burst_size;
|
||||
|
||||
return residue;
|
||||
}
|
||||
|
||||
|
@ -902,7 +1095,7 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c,
|
|||
u32 residue = 0;
|
||||
|
||||
status = dma_cookie_status(c, cookie, state);
|
||||
if ((status == DMA_COMPLETE) || (!state))
|
||||
if (status == DMA_COMPLETE || !state)
|
||||
return status;
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
|
@ -966,7 +1159,7 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
|
|||
}
|
||||
|
||||
static void stm32_dma_set_config(struct stm32_dma_chan *chan,
|
||||
struct stm32_dma_cfg *cfg)
|
||||
struct stm32_dma_cfg *cfg)
|
||||
{
|
||||
stm32_dma_clear_reg(&chan->chan_reg);
|
||||
|
||||
|
@ -976,7 +1169,7 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan,
|
|||
/* Enable Interrupts */
|
||||
chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;
|
||||
|
||||
chan->chan_reg.dma_sfcr = cfg->threshold & STM32_DMA_SFCR_FTH_MASK;
|
||||
chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features);
|
||||
}
|
||||
|
||||
static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||
|
@ -996,10 +1189,10 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
|
|||
cfg.channel_id = dma_spec->args[0];
|
||||
cfg.request_line = dma_spec->args[1];
|
||||
cfg.stream_config = dma_spec->args[2];
|
||||
cfg.threshold = dma_spec->args[3];
|
||||
cfg.features = dma_spec->args[3];
|
||||
|
||||
if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) ||
|
||||
(cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) {
|
||||
if (cfg.channel_id >= STM32_DMA_MAX_CHANNELS ||
|
||||
cfg.request_line >= STM32_DMA_MAX_REQUEST_ID) {
|
||||
dev_err(dev, "Bad channel and/or request id\n");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -470,7 +470,11 @@ typedef void (*dma_async_tx_callback_result)(void *dma_async_param,
|
|||
const struct dmaengine_result *result);
|
||||
|
||||
struct dmaengine_unmap_data {
|
||||
#if IS_ENABLED(CONFIG_DMA_ENGINE_RAID)
|
||||
u16 map_cnt;
|
||||
#else
|
||||
u8 map_cnt;
|
||||
#endif
|
||||
u8 to_cnt;
|
||||
u8 from_cnt;
|
||||
u8 bidi_cnt;
|
||||
|
|
Loading…
Reference in New Issue