You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
dmaengine: Add Synopsys eDMA IP core driver
Add Synopsys PCIe Endpoint eDMA IP core driver to kernel. This IP is generally distributed with Synopsys PCIe Endpoint IP (depends of the use and licensing agreement). This core driver, initializes and configures the eDMA IP using vma-helpers functions and dma-engine subsystem. This driver can be compile as built-in or external module in kernel. To enable this driver just select DW_EDMA option in kernel configuration, however it requires and selects automatically DMA_ENGINE and DMA_VIRTUAL_CHANNELS option too. In order to transfer data from point A to B as fast as possible this IP requires a dedicated memory space containing linked list of elements. All elements of this linked list are continuous and each one describes a data transfer (source and destination addresses, length and a control variable). For the sake of simplicity, lets assume a memory space for channel write 0 which allows about 42 elements. +---------+ | Desc #0 |-+ +---------+ | V +----------+ | Chunk #0 |-+ | CB = 1 | | +----------+ +-----+ +-----------+ +-----+ +----------+ +->| Burst #0 |->| ... |->| Burst #41 |->| llp | | +----------+ +-----+ +-----------+ +-----+ V +----------+ | Chunk #1 |-+ | CB = 0 | | +-----------+ +-----+ +-----------+ +-----+ +----------+ +->| Burst #42 |->| ... |->| Burst #83 |->| llp | | +-----------+ +-----+ +-----------+ +-----+ V +----------+ | Chunk #2 |-+ | CB = 1 | | +-----------+ +-----+ +------------+ +-----+ +----------+ +->| Burst #84 |->| ... |->| Burst #125 |->| llp | | +-----------+ +-----+ +------------+ +-----+ V +----------+ | Chunk #3 |-+ | CB = 0 | | +------------+ +-----+ +------------+ +-----+ +----------+ +->| Burst #126 |->| ... |->| Burst #129 |->| llp | +------------+ +-----+ +------------+ +-----+ Legend: - Linked list, also know as Chunk - Linked list element*, also know as Burst *CB*, also know as Change Bit, it's a control bit (and typically is toggled) that allows to easily identify and differentiate between the current linked list and the previous or the next one. - LLP, is a special element that indicates the end of the linked list element stream also informs that the next CB should be toggle On every last Burst of the Chunk (Burst #41, Burst #83, Burst #125 or even Burst #129) is set some flags on their control variable (RIE and LIE bits) that will trigger the send of "done" interruption. On the interruptions callback, is decided whether to recycle the linked list memory space by writing a new set of Bursts elements (if still exists Chunks to transfer) or is considered completed (if there is no Chunks available to transfer). On scatter-gather transfer mode, the client will submit a scatter-gather list of n (on this case 130) elements, that will be divide in multiple Chunks, each Chunk will have (on this case 42) a limited number of Bursts and after transferring all Bursts, an interrupt will be triggered, which will allow to recycle the all linked list dedicated memory again with the new information relative to the next Chunk and respective Burst associated and repeat the whole cycle again. On cyclic transfer mode, the client will submit a buffer pointer, length of it and number of repetitions, in this case each burst will correspond directly to each repetition. Each Burst can describes a data transfer from point A(source) to point B(destination) with a length that can be from 1 byte up to 4 GB. Since dedicated the memory space where the linked list will reside is limited, the whole n burst elements will be organized in several Chunks, that will be used later to recycle the dedicated memory space to initiate a new sequence of data transfers. The whole transfer is considered has completed when it was transferred all bursts. Currently this IP has a set well-known register map, which includes support for legacy and unroll modes. Legacy mode is version of this register map that has multiplexer register that allows to switch registers between all write and read channels and the unroll modes repeats all write and read channels registers with an offset between them. This register map is called v0. The IP team is creating a new register map more suitable to the latest PCIe features, that very likely will change the map register, which this version will be called v1. As soon as this new version is released by the IP team the support for this version in be included on this driver. According to the logic, patches 1, 2 and 3 should be squashed into 1 unique patch, but for the sake of simplicity of review, it was divided in this 3 patches files. Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com> Cc: Vinod Koul <vkoul@kernel.org> Cc: Dan Williams <dan.j.williams@intel.com> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: Russell King <rmk+kernel@armlinux.org.uk> Cc: Joao Pinto <jpinto@synopsys.com> Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
committed by
Vinod Koul
parent
fb6dda8349
commit
e63d79d1ff
@@ -665,6 +665,8 @@ source "drivers/dma/qcom/Kconfig"
|
||||
|
||||
source "drivers/dma/dw/Kconfig"
|
||||
|
||||
source "drivers/dma/dw-edma/Kconfig"
|
||||
|
||||
source "drivers/dma/hsu/Kconfig"
|
||||
|
||||
source "drivers/dma/sh/Kconfig"
|
||||
|
||||
@@ -29,6 +29,7 @@ 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_DW_EDMA) += dw-edma/
|
||||
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
|
||||
obj-$(CONFIG_FSL_DMA) += fsldma.o
|
||||
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
|
||||
|
||||
9
drivers/dma/dw-edma/Kconfig
Normal file
9
drivers/dma/dw-edma/Kconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config DW_EDMA
|
||||
tristate "Synopsys DesignWare eDMA controller driver"
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
Support the Synopsys DesignWare eDMA controller, normally
|
||||
implemented on endpoints SoCs.
|
||||
4
drivers/dma/dw-edma/Makefile
Normal file
4
drivers/dma/dw-edma/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_DW_EDMA) += dw-edma.o
|
||||
dw-edma-objs := dw-edma-core.o
|
||||
936
drivers/dma/dw-edma/dw-edma-core.c
Normal file
936
drivers/dma/dw-edma/dw-edma-core.c
Normal file
File diff suppressed because it is too large
Load Diff
165
drivers/dma/dw-edma/dw-edma-core.h
Normal file
165
drivers/dma/dw-edma/dw-edma-core.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
|
||||
* Synopsys DesignWare eDMA core driver
|
||||
*
|
||||
* Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
|
||||
*/
|
||||
|
||||
#ifndef _DW_EDMA_CORE_H
|
||||
#define _DW_EDMA_CORE_H
|
||||
|
||||
#include <linux/msi.h>
|
||||
#include <linux/dma/edma.h>
|
||||
|
||||
#include "../virt-dma.h"
|
||||
|
||||
#define EDMA_LL_SZ 24
|
||||
|
||||
enum dw_edma_dir {
|
||||
EDMA_DIR_WRITE = 0,
|
||||
EDMA_DIR_READ
|
||||
};
|
||||
|
||||
enum dw_edma_mode {
|
||||
EDMA_MODE_LEGACY = 0,
|
||||
EDMA_MODE_UNROLL
|
||||
};
|
||||
|
||||
enum dw_edma_request {
|
||||
EDMA_REQ_NONE = 0,
|
||||
EDMA_REQ_STOP,
|
||||
EDMA_REQ_PAUSE
|
||||
};
|
||||
|
||||
enum dw_edma_status {
|
||||
EDMA_ST_IDLE = 0,
|
||||
EDMA_ST_PAUSE,
|
||||
EDMA_ST_BUSY
|
||||
};
|
||||
|
||||
struct dw_edma_chan;
|
||||
struct dw_edma_chunk;
|
||||
|
||||
struct dw_edma_burst {
|
||||
struct list_head list;
|
||||
u64 sar;
|
||||
u64 dar;
|
||||
u32 sz;
|
||||
};
|
||||
|
||||
struct dw_edma_region {
|
||||
phys_addr_t paddr;
|
||||
dma_addr_t vaddr;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
struct dw_edma_chunk {
|
||||
struct list_head list;
|
||||
struct dw_edma_chan *chan;
|
||||
struct dw_edma_burst *burst;
|
||||
|
||||
u32 bursts_alloc;
|
||||
|
||||
u8 cb;
|
||||
struct dw_edma_region ll_region; /* Linked list */
|
||||
};
|
||||
|
||||
struct dw_edma_desc {
|
||||
struct virt_dma_desc vd;
|
||||
struct dw_edma_chan *chan;
|
||||
struct dw_edma_chunk *chunk;
|
||||
|
||||
u32 chunks_alloc;
|
||||
|
||||
u32 alloc_sz;
|
||||
u32 xfer_sz;
|
||||
};
|
||||
|
||||
struct dw_edma_chan {
|
||||
struct virt_dma_chan vc;
|
||||
struct dw_edma_chip *chip;
|
||||
int id;
|
||||
enum dw_edma_dir dir;
|
||||
|
||||
off_t ll_off;
|
||||
u32 ll_max;
|
||||
|
||||
off_t dt_off;
|
||||
|
||||
struct msi_msg msi;
|
||||
|
||||
enum dw_edma_request request;
|
||||
enum dw_edma_status status;
|
||||
u8 configured;
|
||||
|
||||
struct dma_slave_config config;
|
||||
};
|
||||
|
||||
struct dw_edma_irq {
|
||||
struct msi_msg msi;
|
||||
u32 wr_mask;
|
||||
u32 rd_mask;
|
||||
struct dw_edma *dw;
|
||||
};
|
||||
|
||||
struct dw_edma {
|
||||
char name[20];
|
||||
|
||||
struct dma_device wr_edma;
|
||||
u16 wr_ch_cnt;
|
||||
|
||||
struct dma_device rd_edma;
|
||||
u16 rd_ch_cnt;
|
||||
|
||||
struct dw_edma_region rg_region; /* Registers */
|
||||
struct dw_edma_region ll_region; /* Linked list */
|
||||
struct dw_edma_region dt_region; /* Data */
|
||||
|
||||
struct dw_edma_irq *irq;
|
||||
int nr_irqs;
|
||||
|
||||
u32 version;
|
||||
enum dw_edma_mode mode;
|
||||
|
||||
struct dw_edma_chan *chan;
|
||||
const struct dw_edma_core_ops *ops;
|
||||
|
||||
raw_spinlock_t lock; /* Only for legacy */
|
||||
};
|
||||
|
||||
struct dw_edma_sg {
|
||||
struct scatterlist *sgl;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct dw_edma_cyclic {
|
||||
dma_addr_t paddr;
|
||||
size_t len;
|
||||
size_t cnt;
|
||||
};
|
||||
|
||||
struct dw_edma_transfer {
|
||||
struct dma_chan *dchan;
|
||||
union dw_edma_xfer {
|
||||
struct dw_edma_sg sg;
|
||||
struct dw_edma_cyclic cyclic;
|
||||
} xfer;
|
||||
enum dma_transfer_direction direction;
|
||||
unsigned long flags;
|
||||
bool cyclic;
|
||||
};
|
||||
|
||||
static inline
|
||||
struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
|
||||
{
|
||||
return container_of(vc, struct dw_edma_chan, vc);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
|
||||
{
|
||||
return vc2dw_edma_chan(to_virt_chan(dchan));
|
||||
}
|
||||
|
||||
#endif /* _DW_EDMA_CORE_H */
|
||||
47
include/linux/dma/edma.h
Normal file
47
include/linux/dma/edma.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
|
||||
* Synopsys DesignWare eDMA core driver
|
||||
*
|
||||
* Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
|
||||
*/
|
||||
|
||||
#ifndef _DW_EDMA_H
|
||||
#define _DW_EDMA_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct dw_edma;
|
||||
|
||||
/**
|
||||
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
|
||||
* @dev: struct device of the eDMA controller
|
||||
* @id: instance ID
|
||||
* @irq: irq line
|
||||
* @dw: struct dw_edma that is filed by dw_edma_probe()
|
||||
*/
|
||||
struct dw_edma_chip {
|
||||
struct device *dev;
|
||||
int id;
|
||||
int irq;
|
||||
struct dw_edma *dw;
|
||||
};
|
||||
|
||||
/* Export to the platform drivers */
|
||||
#if IS_ENABLED(CONFIG_DW_EDMA)
|
||||
int dw_edma_probe(struct dw_edma_chip *chip);
|
||||
int dw_edma_remove(struct dw_edma_chip *chip);
|
||||
#else
|
||||
static inline int dw_edma_probe(struct dw_edma_chip *chip)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int dw_edma_remove(struct dw_edma_chip *chip)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DW_EDMA */
|
||||
|
||||
#endif /* _DW_EDMA_H */
|
||||
Reference in New Issue
Block a user