dma: add Efinix DMA controller driver

This commit is contained in:
Mohamad Noor Alim Hussin
2025-02-13 16:41:50 +08:00
committed by Byron Lathi
parent 6f082dfd7a
commit 89d4b5521f
3 changed files with 897 additions and 0 deletions

View File

@@ -187,6 +187,13 @@ config DW_AXI_DMAC
NOTE: This driver wasn't tested on 64 bit platform because
of lack 64 bit platform with Synopsys DW AXI DMAC.
config EFINIX_DMA
bool "Efinix DMA support"
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable support for Efinix DMA controller
config EP93XX_DMA
bool "Cirrus Logic EP93xx DMA support"
depends on ARCH_EP93XX || COMPILE_TEST

View File

@@ -29,6 +29,7 @@ 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_EFINIX_DMA) += efx_dma.o
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
obj-$(CONFIG_FSL_DMA) += fsldma.o
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o

889
drivers/dma/efx_dma.c Normal file
View File

@@ -0,0 +1,889 @@
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/of_irq.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include "dmaengine.h"
#include "virt-dma.h"
#define EFX_DMA_CHANNEL_INPUT_ADDRESS 0x00
#define EFX_DMA_CHANNEL_INPUT_STREAM 0x08
#define EFX_DMA_CHANNEL_INPUT_CONFIG 0x0c
#define EFX_DMA_CHANNEL_INPUT_CONFIG_MEMORY (1 << 12)
#define EFX_DMA_CHANNEL_INPUT_CONFIG_STREAM 0x0
#define EFX_DMA_CHANNEL_INPUT_CONFIG_COMPLETION_ON_PACKET (1 << 13)
#define EFX_DMA_CHANNEL_INPUT_CONFIG_WAIT_ON_PACKET (1 << 14)
#define EFX_DMA_CHANNEL_OUTPUT_ADDRESS 0x10
#define EFX_DMA_CHANNEL_OUTPUT_STREAM 0x18
#define EFX_DMA_CHANNEL_OUTPUT_CONFIG 0x1c
#define EFX_DMA_CHANNEL_OUTPUT_CONFIG_MEMORY (1 << 12)
#define EFX_DMA_CHANNEL_OUTPUT_CONFIG_STREAM 0x0
#define EFX_DMA_CHANNEL_OUTPUT_CONFIG_LAST (1 << 13)
#define EFX_DMA_CHANNEL_DIRECT_BYTES 0x20
#define EFX_DMA_CHANNEL_STATUS 0x2c
#define EFX_DMA_CHANNEL_STATUS_DIRECT_START (1 << 0)
#define EFX_DMA_CHANNEL_STATUS_BUSY (1 << 0)
#define EFX_DMA_CHANNEL_STATUS_SELF_RESTART (1 << 1)
#define EFX_DMA_CHANNEL_STATUS_STOP (1 << 2)
#define EFX_DMA_CHANNEL_STATUS_LINKED_LIST_START (1 << 4)
#define EFX_DMA_CHANNEL_FIFO 0x40
#define EFX_DMA_CHANNEL_PRIORITY 0x44
#define EFX_DMA_CHANNEL_INTERRUPT_ENABLE 0x50
#define EFX_DMA_CHANNEL_INTERRUPT_PENDING 0x54
// Interrupt at the end of each descriptor
#define EFX_DMA_CHANNEL_INTERRUPT_DESCRIPTOR_COMPLETION_MASK (1 << 0)
// Interrupt at the middle of each descriptor, require the half_completion_interrpt
// option to be enabled for the channel
#define EFX_DMA_CHANNEL_INTERRUPT_DESCRIPTOR_COMPLETION_HALF_MASK (1 << 1)
// Interrupt when the channel is going off (not busy anymore)
#define EFX_DMA_CHANNEL_INTERRUPT_CHANNEL_COMPLETION_MASK (1 << 2)
// Interrupt each time that a linked list's descriptor stats field is updated
#define EFX_DMA_CHANNEL_INTERRUPT_LINKED_LIST_UPDATE_MASK (1 << 3)
// Interrupt each time a S -> M channel has done transferring a packet into the memory
#define EFX_DMA_CHANNEL_INTERRUPT_INPUT_PACKET_MASK (1 << 4)
#define EFX_DMA_CHANNEL_PROGRESS_BYTES 0x60
#define EFX_DMA_CHANNEL_LINKED_LIST_HEAD 0x70
#define EFX_DMA_CHANNEL_LINKED_LIST_FROM_SG_BUS 0x78
#define EFX_DMA_DESCRIPTOR_CONTROL_BYTES 0x7FFFFFF
#define EFX_DMA_DESCRIPTOR_CONTROL_END_OF_PACKET (1 << 30)
#define EFX_DMA_DESCRIPTOR_NO_COMPLETION (1 << 31)
#define EFX_DMA_DESCRIPTOR_STATUS_BYTES 0x7FFFFFF
#define EFX_DMA_DESCRIPTOR_STATUS_END_OF_PACKET (1 << 30)
#define EFX_DMA_DESCRIPTOR_STATUS_COMPLETED (1 << 31)
// DMA Hardware descriptor
struct efx_dma_hw_desc {
u32 status;
u32 control;
u64 src_addr;
u64 dst_addr;
u64 next; // physical address of next descriptor
} __aligned(64);
// Per transfer descriptor
struct efx_dma_desc {
struct virt_dma_desc vdesc;
struct efx_dma_hw_desc *hw_desc; // Pointer to the struct efx_dma_hw_desc
dma_addr_t dma_handle; // DMA address of struct efx_dma_hw_desc
struct list_head node; // List of struct efx_dma_hw_desc
size_t segments; // Number of DMA descriptor
bool cyclic; // True for cyclic transfer
enum dma_transfer_direction direction;
struct scatterlist *sg;
struct page **pages;
};
// DMA channel specific data
struct efx_dma_chan {
const char *name; // Channel name
void __iomem *reg; // based address of DMA channel
size_t chan_id; // Channel ID
struct virt_dma_chan vchan;
struct efx_dma_priv *priv; // Pointer to the DMA controller
struct dma_slave_config *cfg; // Channel specific configuration
u32 priority; // Priority number of DMA channel
size_t irq; // IRQ number used by the DMA channel
struct efx_dma_desc *head_desc; // First DMA descriptor
struct list_head pending_list; // List of all DMA descriptor
spinlock_t lock; // Lock for manipulating pending_list
};
// DMA controller private data
struct efx_dma_priv {
struct device *dev;
void __iomem *base; // DMA controller based address
struct dma_device dma_dev;
struct efx_dma_chan *dchan; // Pointer to the DMA channel
u32 chan_count; // Number of DMA channel
};
static inline struct efx_dma_chan *to_efx_dma_chan(struct dma_chan *chan)
{
return container_of(chan, struct efx_dma_chan, vchan.chan);
}
static inline struct efx_dma_priv *to_efx_dma_priv(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
return dchan->priv;
}
static void efx_dma_input_memory(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_hw_desc *hw_desc = dchan->head_desc->hw_desc;
u32 byte_per_burst = 0;
if (dchan->cfg)
byte_per_burst = dchan->cfg->src_maxburst;
else
byte_per_burst = chan->device->max_burst;
iowrite32(hw_desc->src_addr, dchan->reg + EFX_DMA_CHANNEL_INPUT_ADDRESS);
iowrite32(EFX_DMA_CHANNEL_INPUT_CONFIG_MEMORY | ((byte_per_burst - 1) & 0xFFF),
dchan->reg + EFX_DMA_CHANNEL_INPUT_CONFIG);
}
static void efx_dma_output_memory(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_hw_desc *hw_desc = dchan->head_desc->hw_desc;
u32 len = hw_desc->control & 0x1FFFFFF;
iowrite32(hw_desc->dst_addr, dchan->reg + EFX_DMA_CHANNEL_OUTPUT_ADDRESS);
iowrite32(EFX_DMA_CHANNEL_OUTPUT_CONFIG_MEMORY | ((len - 1) & 0xFFF),
dchan->reg + EFX_DMA_CHANNEL_OUTPUT_CONFIG);
}
static void efx_dma_input_stream(struct dma_chan *chan, u32 wait_on_packet, u32 completion_on_packet)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
completion_on_packet = completion_on_packet ? EFX_DMA_CHANNEL_INPUT_CONFIG_COMPLETION_ON_PACKET : 0;
wait_on_packet = wait_on_packet ? EFX_DMA_CHANNEL_INPUT_CONFIG_WAIT_ON_PACKET : 0;
iowrite32(0, dchan->reg + EFX_DMA_CHANNEL_INPUT_STREAM);
iowrite32(EFX_DMA_CHANNEL_INPUT_CONFIG_STREAM
| completion_on_packet | wait_on_packet,
dchan->reg + EFX_DMA_CHANNEL_INPUT_CONFIG);
}
static void efx_dma_output_stream(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
iowrite32(0, dchan->reg + EFX_DMA_CHANNEL_OUTPUT_STREAM);
iowrite32(EFX_DMA_CHANNEL_OUTPUT_CONFIG_LAST | EFX_DMA_CHANNEL_OUTPUT_CONFIG_STREAM,
dchan->reg + EFX_DMA_CHANNEL_OUTPUT_CONFIG);
}
static void efx_dma_linked_list_start(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
iowrite32(dchan->head_desc->dma_handle, dchan->reg + EFX_DMA_CHANNEL_LINKED_LIST_HEAD);
iowrite32(0, dchan->reg + EFX_DMA_CHANNEL_LINKED_LIST_FROM_SG_BUS);
iowrite32(EFX_DMA_CHANNEL_STATUS_LINKED_LIST_START, dchan->reg + EFX_DMA_CHANNEL_STATUS);
}
static void efx_dma_stop_channel(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
iowrite32(EFX_DMA_CHANNEL_STATUS_STOP, dchan->reg + EFX_DMA_CHANNEL_STATUS);
}
static bool efx_dma_busy(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
u32 busy;
busy = readl(dchan->reg + EFX_DMA_CHANNEL_STATUS) & EFX_DMA_CHANNEL_STATUS_BUSY;
return busy ? true : false;
}
static inline void efx_dma_interrupt_pending_clear(struct dma_chan *chan, u32 mask)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
iowrite32(mask, dchan->reg + EFX_DMA_CHANNEL_INTERRUPT_PENDING);
}
static void efx_dma_interrupt_config(struct dma_chan *chan, u32 mask)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
efx_dma_interrupt_pending_clear(chan, 0xFFFFFFFF);
iowrite32(mask, dchan->reg + EFX_DMA_CHANNEL_INTERRUPT_ENABLE);
}
static void efx_dma_set_channel_priority(struct efx_dma_chan *dchan)
{
iowrite32(dchan->priority, dchan->reg + EFX_DMA_CHANNEL_PRIORITY);
}
static void efx_dma_configure_registers(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_desc *desc = dchan->head_desc;
if (desc->direction == DMA_DEV_TO_MEM) {
efx_dma_output_memory(chan);
efx_dma_input_stream(chan, 1, 0);
} else if (desc->direction == DMA_MEM_TO_DEV) {
efx_dma_input_memory(chan);
efx_dma_output_stream(chan);
} else if (desc->direction == DMA_MEM_TO_MEM) {
efx_dma_input_memory(chan);
efx_dma_output_memory(chan);
}
}
static void efx_dma_start_transfer(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct virt_dma_desc *vdesc;
vdesc = vchan_next_desc(&dchan->vchan);
if (!vdesc)
return;
efx_dma_configure_registers(chan);
efx_dma_linked_list_start(chan);
}
static irqreturn_t efx_dma_interrupt_handler(int irq, void *dev_id)
{
struct efx_dma_chan *dchan = (struct efx_dma_chan *)dev_id;
struct efx_dma_desc *desc = dchan->head_desc;
struct dma_chan *chan = &dchan->vchan.chan;
unsigned long flags;
u32 pending;
spin_lock_irqsave(&dchan->vchan.lock, flags);
pending = readl(dchan->reg + EFX_DMA_CHANNEL_INTERRUPT_PENDING);
if (pending & EFX_DMA_CHANNEL_INTERRUPT_DESCRIPTOR_COMPLETION_MASK)
efx_dma_interrupt_pending_clear(chan, EFX_DMA_CHANNEL_INTERRUPT_DESCRIPTOR_COMPLETION_MASK);
if (pending & EFX_DMA_CHANNEL_INTERRUPT_CHANNEL_COMPLETION_MASK)
efx_dma_interrupt_pending_clear(chan, EFX_DMA_CHANNEL_INTERRUPT_CHANNEL_COMPLETION_MASK);
vchan_cookie_complete(&desc->vdesc);
// Unmask the interrupt
efx_dma_interrupt_pending_clear(chan, 0x0);
pending = readl(dchan->reg + EFX_DMA_CHANNEL_INTERRUPT_PENDING);
spin_unlock_irqrestore(&dchan->vchan.lock, flags);
return IRQ_HANDLED;
}
static int efx_dma_alloc_chan_resources(struct dma_chan *chan)
{
return 0;
}
static void efx_dma_free_chan_resources(struct dma_chan *chan)
{
}
static void efx_dma_dump_pending_list(struct dma_chan *chan)
{
struct efx_dma_priv *priv = to_efx_dma_priv(chan);
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_desc *desc;
struct device *dev = priv->dev;
int i = 0, count = 0;
int limit = 5;
// Count number of descriptor in the pending_list
spin_lock(&dchan->lock);
list_for_each_entry(desc, &dchan->pending_list, node)
count++;
spin_unlock(&dchan->lock);
if (count == 0) {
dev_dbg(dev, "Pending list is empty\n");
return;
}
dev_dbg(dev, "Pending list has %d descriptors\n", count);
// Print first 5 and last 5 descriptors
spin_lock(&dchan->lock);
list_for_each_entry(desc, &dchan->pending_list, node) {
if (i < limit || i >= count - limit) {
pr_debug("%s: Descriptor %d:\n", __func__, i);
pr_debug(" desc=%p, hw_desc=%p, dma_handle=0x%llx\n",
desc, desc->hw_desc, (unsigned long long)desc->dma_handle);
if (desc->hw_desc) {
pr_debug(" status=0x%x, control=0x%x\n",
desc->hw_desc->status, desc->hw_desc->control);
pr_debug(" src_addr=0x%llx, dst_addr=0x%llx, next=0x%llx\n",
(unsigned long long)desc->hw_desc->src_addr,
(unsigned long long)desc->hw_desc->dst_addr,
(unsigned long long)desc->hw_desc->next);
}
pr_debug(" cyclic=%d, direction=%d, segments=%zu\n",
desc->cyclic, desc->direction, desc->segments);
}
i++;
}
spin_unlock(&dchan->lock);
}
static void efx_dma_free_hw_desc_chain(struct dma_chan *chan)
{
struct efx_dma_priv *priv = to_efx_dma_priv(chan);
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_desc *desc, *tmp;
struct device *dev = priv->dev;
int count = 0;
int limit = 5, i = 0;
// Count number of descriptor in the pending_list
spin_lock(&dchan->lock);
list_for_each_entry(desc, &dchan->pending_list, node)
count++;
spin_unlock(&dchan->lock);
if (count == 0) {
dev_info(dev, "Pending list is empty\n");
return;
}
dev_info(dev, "Freeing %d descriptors\n", count);
spin_lock(&dchan->lock);
list_for_each_entry_safe(desc, tmp, &dchan->pending_list, node) {
list_del(&desc->node);
if (desc->hw_desc) {
dma_free_coherent(dev, sizeof(struct efx_dma_hw_desc),
desc->hw_desc, desc->dma_handle);
if (i < limit || i >= count - limit) {
pr_debug(" Freeing descriptor: %d\n", i);
pr_debug(" free hw_desc=%p, dma_handle=0x%llx\n",desc->hw_desc,
(unsigned long long)desc->dma_handle);
}
}
kfree(desc);
if (i < limit || i >= count - limit)
pr_debug(" desc=%p\n",desc);
i++;
}
dchan->head_desc = NULL;
spin_unlock(&dchan->lock);
}
static void efx_dma_desc_free(struct virt_dma_desc *vd)
{
struct dma_chan *chan = vd->tx.chan;
efx_dma_free_hw_desc_chain(chan);
}
static void efx_dma_hw_desc_address_control(struct efx_dma_chan *dchan,
struct efx_dma_desc *desc, dma_addr_t src_addr, dma_addr_t dst_addr,
size_t period_len, struct scatterlist *sg, size_t i)
{
struct efx_dma_hw_desc *hw_desc = desc->hw_desc;
dma_addr_t segment_addr;
u32 buf_len;
u32 control = desc->cyclic ? EFX_DMA_DESCRIPTOR_NO_COMPLETION
: EFX_DMA_DESCRIPTOR_CONTROL_END_OF_PACKET;
hw_desc->status = 0;
if (sg) {
buf_len = sg_dma_len(sg);
segment_addr = sg_dma_address(sg);
} else {
buf_len = period_len - 1;
segment_addr = src_addr + i * period_len;
}
hw_desc->control = (u32)(control | buf_len);
// Configure last hw_desc
if (i == (desc->segments - 1)) {
if (desc->cyclic) {
hw_desc->next = dchan->head_desc->dma_handle;
} else {
hw_desc->status = EFX_DMA_DESCRIPTOR_STATUS_COMPLETED;
}
}
if (desc->direction == DMA_DEV_TO_MEM) {
hw_desc->src_addr = 0;
hw_desc->dst_addr = segment_addr;
} else if (desc->direction == DMA_MEM_TO_DEV) {
hw_desc->src_addr = segment_addr;
hw_desc->dst_addr = 0;
} else if (desc->direction == DMA_MEM_TO_MEM) {
hw_desc->src_addr = src_addr + i * period_len;
hw_desc->dst_addr = dst_addr + i * period_len;
}
}
static int efx_dma_hw_desc_init(struct dma_chan *chan, dma_addr_t src_addr, dma_addr_t dst_addr,
struct scatterlist *sg, size_t len, size_t period_len,
enum dma_transfer_direction direction, bool cyclic)
{
struct efx_dma_priv *priv = to_efx_dma_priv(chan);
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_desc *desc = NULL, *prev_desc = NULL;
struct efx_dma_hw_desc *hw_desc = NULL;
struct device *dev = priv->dev;
struct scatterlist *sgl;
size_t i;
int ret = 0;
for (i = 0; i < len; i++) {
// Allocate memory for desc
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
dev_err(dev, "Failed to allocate memory for descriptor\n");
goto err_free_desc_init;
}
desc->cyclic = cyclic;
desc->segments = len;
desc->direction = direction;
INIT_LIST_HEAD(&desc->node);
// Allocate memory for hw_desc
hw_desc = dma_alloc_coherent(dev, sizeof(*hw_desc),
&desc->dma_handle, GFP_KERNEL);
if (!hw_desc) {
dev_err(dev, "Failed to allocate memory for hw descriptor\n");
kfree(desc);
ret = -ENOMEM;
goto err_free_desc_init;
}
desc->hw_desc = hw_desc;
// Save head descriptor
if (i == 0)
dchan->head_desc = desc;
// Set the scatterlist pointer for sg mode. For cylic, sgl is NULL
sgl = sg ? &sg[i] : NULL;
// Configure the hw descriptor addresses and control
efx_dma_hw_desc_address_control(dchan, desc, src_addr, dst_addr, period_len, sgl, i);
// Chain descriptor
if (prev_desc)
prev_desc->hw_desc->next = desc->dma_handle;
prev_desc = desc;
// Add to pending list
spin_lock(&dchan->lock);
list_add_tail(&desc->node, &dchan->pending_list);
spin_unlock(&dchan->lock);
}
// Debugging
efx_dma_dump_pending_list(chan);
return 0;
err_free_desc_init:
efx_dma_free_hw_desc_chain(chan);
return ret;
}
static struct dma_async_tx_descriptor *efx_dma_prep_slave_sg(
struct dma_chan *chan,
struct scatterlist *sg, unsigned int sg_len,
enum dma_transfer_direction direction,
unsigned long flags, void *context)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct device *dev = dchan->priv->dev;
struct efx_dma_desc *desc = NULL;
int ret;
bool cyclic = false;
// Validate input
if (unlikely(!chan || !sg || sg_len <= 0))
return NULL;
// sg_len +1 to inlcude the last status of the descriptor
sg_len += 1;
if (flags & DMA_CTRL_REUSE) {
cyclic = true;
sg_len -= 1; // Cyclic transfer does not require additional descriptor
flags &= ~DMA_CTRL_REUSE;
}
// Initialize hardware descriptor. Set cyclic to false
ret = efx_dma_hw_desc_init(chan, 0, 0, sg, sg_len, 0, direction, cyclic);
if (ret) {
dev_err(dev, "Failed to initialize hardware descriptor\n");
return NULL;
}
// Get head descriptor
desc = dchan->head_desc;
if (!desc) {
dev_err(dev, "No head descriptor found\n");
return NULL;
}
// Configure interrupt
if (flags & DMA_PREP_INTERRUPT)
efx_dma_interrupt_config(chan, EFX_DMA_CHANNEL_INTERRUPT_CHANNEL_COMPLETION_MASK);
// Prepare virtual DMA descriptor
dchan->vchan.cyclic = &desc->vdesc;
return vchan_tx_prep(&dchan->vchan, &desc->vdesc, flags);
}
static struct dma_async_tx_descriptor *efx_dma_prep_cyclic(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
unsigned long flags)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct device *dev = dchan->priv->dev;
struct efx_dma_desc *desc = NULL;
size_t segments = buf_len / period_len;
int ret;
if (unlikely(!chan || !buf_addr || buf_len == 0 || period_len > buf_len))
return NULL;
ret = efx_dma_hw_desc_init(chan, buf_addr, 0, NULL, segments, period_len, direction, true);
if (ret)
return NULL;
// Get head descriptor
desc = dchan->head_desc;
if (!desc) {
dev_err(dev, "No head descriptor found\n");
return NULL;
}
dchan->vchan.cyclic = &desc->vdesc;
return vchan_tx_prep(&dchan->vchan, &desc->vdesc, flags);
}
static struct dma_async_tx_descriptor *efx_dma_prep_dma_memcpy(struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr, size_t len,
unsigned long flags)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct efx_dma_desc *desc = NULL;
struct device *dev = dchan->priv->dev;
size_t segments;
int ret;
// Validate input
if (unlikely(!chan || !dst_addr || !src_addr || !len))
return NULL;
if (len < PAGE_SIZE)
segments = 1;
else
segments = (len / PAGE_SIZE);
// Increase segments by 1 to include the last status of the descriptor
segments += 1;
ret = efx_dma_hw_desc_init(chan, src_addr, dst_addr, NULL, len, segments, DMA_MEM_TO_MEM, false);
if (ret) {
return NULL;
}
// Get head descriptor
desc = dchan->head_desc;
if (!desc) {
dev_err(dev, "No head descriptor found\n");
return NULL;
}
dchan->vchan.cyclic = &desc->vdesc;
return vchan_tx_prep(&dchan->vchan, &desc->vdesc, flags);
}
static void efx_dma_issue_pending(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
unsigned long flags;
spin_lock_irqsave(&dchan->vchan.lock, flags);
if (vchan_issue_pending(&dchan->vchan))
efx_dma_start_transfer(chan);
spin_unlock_irqrestore(&dchan->vchan.lock, flags);
}
static int efx_dma_device_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
dchan->cfg = config;
return 0;
}
static enum dma_status efx_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
if (efx_dma_busy(chan))
return DMA_IN_PROGRESS;
if (cookie < DMA_MIN_COOKIE)
return DMA_ERROR;
if (chan->completed_cookie == cookie)
return DMA_COMPLETE;
return DMA_COMPLETE;
}
static int efx_dma_terminate_all(struct dma_chan *chan)
{
struct efx_dma_chan *dchan = to_efx_dma_chan(chan);
struct virt_dma_chan *vchan = &dchan->vchan;
struct device *dev = dchan->priv->dev;
if (dchan) {
// Stop all transfer of the DMA channels
efx_dma_stop_channel(&dchan->vchan.chan);
// Disabled interrupt
efx_dma_interrupt_config(chan, 0);
// Terminate all virtual DMA descriptors
//vchan_free_chan_resources(vchan);
// Free all pending descriptors
if (dchan->head_desc)
efx_dma_free_hw_desc_chain(&dchan->vchan.chan);
// Clear cyclic pointer
vchan->cyclic = NULL;
dchan->head_desc = NULL;
dev_info(dev, "DMA channel %s terminated and cleaned up\n", dchan->name);
}
return 0;
}
static void efx_dma_release(struct dma_device *dev)
{
}
static int efx_dma_chan_probe(struct platform_device *pdev)
{
struct efx_dma_priv *priv = platform_get_drvdata(pdev);
struct efx_dma_chan *dchan;
struct device_node *node = pdev->dev.of_node;
struct device_node *child = pdev->dev.of_node;
size_t i = 0;
int ret;
priv->dchan = devm_kzalloc(&pdev->dev, sizeof(struct efx_dma_chan) * priv->chan_count, GFP_KERNEL);
if (!priv->dchan) {
dev_err(&pdev->dev, "Failed to allocated memory for DMA channels\n");
return -ENOMEM;
}
for_each_child_of_node(node, child) {
dchan = &priv->dchan[i];
if (of_property_read_string(child, "dma-names", &dchan->name)) {
dev_warn(&pdev->dev, "Failed to get dma-names for channel %zu\n", i);
return -EINVAL;
}
dev_info(&pdev->dev, "Initialize DMA channel %zu for %s\n", i, dchan->name);
dchan->priv = priv;
vchan_init(&dchan->vchan, &priv->dma_dev);
dchan->chan_id = i;
dchan->reg = priv->base + i * 0x80;
dchan->cfg = NULL;
dchan->vchan.desc_free = &efx_dma_desc_free;
dchan->irq = irq_of_parse_and_map(node, i);
if (dchan->irq <= 0) {
dev_warn(&pdev->dev, "Failed to get interrupt number\n");
}
ret = devm_request_irq(&pdev->dev, dchan->irq, efx_dma_interrupt_handler,
IRQF_SHARED, dchan->name, dchan);
if (ret) {
dev_warn(&pdev->dev, "Warning: Failed to register interrupt handler\n");
}
efx_dma_stop_channel(&dchan->vchan.chan);
efx_dma_interrupt_pending_clear(&dchan->vchan.chan, 0xFFFFFFFF);
ret = of_property_read_u32(child, "chan-priority", &dchan->priority);
if (ret) {
dev_warn(&pdev->dev, "'chan-priority' is not found in the DMA device tree node. Use the default priority\n");
dchan->priority = 0;
}
dev_info(&pdev->dev, " channel address %px, priority %u\n", dchan->reg, dchan->priority);
efx_dma_set_channel_priority(dchan);
// initialize dchan->lock
spin_lock_init(&dchan->lock);
INIT_LIST_HEAD(&dchan->pending_list);
i++;
}
return 0;
}
static struct dma_chan *of_dma_efx_dma_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
struct efx_dma_priv *priv = ofdma->of_dma_data;
struct efx_dma_chan *dchan;
struct dma_chan *chan = NULL;
size_t chan_id;
chan_id = dma_spec->args[0];
if (chan_id < 0 || chan_id > priv->chan_count)
return NULL;
dchan = &priv->dchan[chan_id];
chan = dma_get_slave_channel(&dchan->vchan.chan);
if (!chan) {
dev_err(priv->dev, "Failed to get DMA slave channel %zu\n", chan_id);
return NULL;
}
dev_info(priv->dev, "Found DMA slave '%s' at channel %zu\n", dchan->name, chan_id);
return chan;
}
static int efx_dma_probe(struct platform_device *pdev)
{
struct efx_dma_priv *priv;
struct resource *res;
struct device_node *node = pdev->dev.of_node;
struct device_node *child = pdev->dev.of_node;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
dma_cap_zero(priv->dma_dev.cap_mask);
dma_cap_set(DMA_SLAVE, priv->dma_dev.cap_mask);
dma_cap_set(DMA_CYCLIC, priv->dma_dev.cap_mask);
dma_cap_set(DMA_MEMCPY, priv->dma_dev.cap_mask);
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask, falling back to 32-bit\n");
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "Failed to set 32-bit DMA mask\n");
return ret;
}
}
priv->dma_dev.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
priv->dma_dev.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
priv->dma_dev.max_burst = 16;
priv->dma_dev.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_MEM);
INIT_LIST_HEAD(&priv->dma_dev.channels);
// Initialize and register the DMA engine
priv->dma_dev.dev = &pdev->dev;
priv->dma_dev.device_alloc_chan_resources = efx_dma_alloc_chan_resources;
priv->dma_dev.device_free_chan_resources = efx_dma_free_chan_resources;
priv->dma_dev.device_prep_slave_sg = efx_dma_prep_slave_sg;
priv->dma_dev.device_prep_dma_cyclic = efx_dma_prep_cyclic;
priv->dma_dev.device_issue_pending = efx_dma_issue_pending;
priv->dma_dev.device_config = efx_dma_device_config;
priv->dma_dev.device_tx_status = efx_dma_tx_status;
priv->dma_dev.device_prep_dma_memcpy = efx_dma_prep_dma_memcpy;
priv->dma_dev.device_release = efx_dma_release;
priv->dma_dev.device_terminate_all = efx_dma_terminate_all;
platform_set_drvdata(pdev, priv);
priv->chan_count = 0;
ret = of_property_read_u32(node, "dma-channels", &priv->chan_count);
if (ret) {
dev_warn(&pdev->dev, "`dma-channels` not found in the DMA device tree node. Try to auto detect the number of channels\n");
for_each_child_of_node(node, child) {
priv->chan_count++;
}
}
dev_info(&pdev->dev, "Found %u DMA channels\n", priv->chan_count);
// Initialize DMA channels
ret = efx_dma_chan_probe(pdev);
if (ret)
return ret;
dev_info(&pdev->dev, "Register DMA controller\n");
ret = dma_async_device_register(&priv->dma_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to register DMA controller\n");
return ret;
}
// Register DMA controller with device tree framework
ret = of_dma_controller_register(node, of_dma_efx_dma_xlate, priv);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to register DMA controller to DT\n");
dma_async_device_unregister(&priv->dma_dev);
return ret;
}
dev_info(&pdev->dev, "DMA controller registered\n");
return 0;
}
static int efx_dma_remove(struct platform_device *pdev)
{
struct efx_dma_priv *priv = platform_get_drvdata(pdev);
dma_async_device_unregister(&priv->dma_dev);
return 0;
}
static const struct of_device_id efx_dma_of_match[] = {
{ .compatible = "efx,dma-controller" },
{ },
};
MODULE_DEVICE_TABLE(of, efx_dma_of_match);
static struct platform_driver efx_dma_driver = {
.probe = efx_dma_probe,
.remove = efx_dma_remove,
.driver = {
.name = "efx-dma",
.of_match_table = efx_dma_of_match,
},
};
module_platform_driver(efx_dma_driver);
MODULE_AUTHOR("Alim Hussin <mnalim@efinixinc.com>");
MODULE_DESCRIPTION("Efinix DMA driver");
MODULE_LICENSE("GPL v2");