Files
MilkV-Duo/linux_5.10/drivers/dma/cvitek/cvitek-dma.c
forum_service 7e1529d0dc linux-5.10: version release v4.1.0.3
0debf7abc [dma] When using DMA, if the DMA addr spans 128MB boundary   we have to split the DMA transfer into two so that each one     doesn't exceed the boundary.
4f7ed7449 Merge "[eth] change rxterm and vcm to link DianXin router" into v4.1.0
feb60bba5 Merge "[audio][lt9611]add drivers" into v4.1.0
a3dde371c [eth] change rxterm and vcm to link DianXin router
e615d3a43 [audio][lt9611]add drivers

Change-Id: If4fdcbc32df6aeaed04da0efbd23455376e2a53c
2023-12-21 16:51:57 +08:00

2201 lines
54 KiB
C

#include <linux/module.h>
#include <linux/device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/acpi.h>
#include <linux/acpi_dma.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include "../dmaengine.h"
#include "cvitek-dma.h"
#define DRV_NAME "dw_dmac"
/* #define CONFIG_SYSDMA_JIRA_BUG_BM1880_17 */
/*
* The dmaengine doc says that, defer most work to a tasklet is inefficient,
* and there may be schedule latency, and slow down the trans rate.
* so we should avoid this ?
*/
/* #define DO_NOT_USE_TASK */
static void dw_dma_off(struct dw_dma *dw);
static void dw_dma_on(struct dw_dma *dw);
static bool dw_dma_filter(struct dma_chan *chan, void *param);
static int dw_dma_cyclic_start(struct dma_chan *chan);
#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node)
#define DWC_DEFAULT_CTL(_chan) ({ \
struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \
struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
bool _is_slave = is_slave_direction(_dwc->direction); \
u8 _smsize = _is_slave ? _sconfig->src_maxburst : \
DW_DMA_MSIZE_32; \
u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \
DW_DMA_MSIZE_32; \
u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \
_dwc->dws.p_master : _dwc->dws.m_master; \
u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \
_dwc->dws.p_master : _dwc->dws.m_master; \
\
(DWC_CTL_DST_MSIZE(_dmsize) \
| DWC_CTL_SRC_MSIZE(_smsize) \
| DWC_CTL_DMS(_dms) \
| DWC_CTL_SMS(_sms)); \
})
static inline struct dw_dma_chan_regs __iomem *
__dwc_regs(struct dw_dma_chan *dwc)
{
return dwc->ch_regs;
}
static inline struct dw_dma_regs __iomem *
__dw_regs(struct dw_dma *dw)
{
return dw->regs;
}
static inline struct dw_desc *txd_to_dw_desc(struct dma_async_tx_descriptor *txd)
{
return container_of(txd, struct dw_desc, txd);
}
static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan)
{
return container_of(chan, struct dw_dma_chan, chan);
}
static inline struct dw_dma *to_dw_dma(struct dma_device *ddev)
{
return container_of(ddev, struct dw_dma, dma);
}
static struct device *chan2dev(struct dma_chan *chan)
{
return &chan->dev->device;
}
/*
* Fix sconfig's burst size according to dw_dmac. We need to convert them as:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
*
* NOTE: burst size 2 is not supported by controller.
*
* This can be done by finding least significant bit set: n & (n - 1)
*/
static inline void convert_burst(u32 *maxburst)
{
if (*maxburst > 1)
*maxburst = fls(*maxburst) - 2;
else
*maxburst = 0;
}
static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc)
{
dev_err(chan2dev(&dwc->chan),
" SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx CTL: 0x%llx BLOCK_TS: 0x%llx\n",
channel_readq(dwc, SAR),
channel_readq(dwc, DAR),
channel_readq(dwc, LLP),
channel_readq(dwc, CTL),
channel_readq(dwc, BLOCK_TS));
}
static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_desc *desc)
{
dev_err(chan2dev(&dwc->chan), " desc: s0x%llx d0x%llx l0x%llx c0x%llx b0x%llx\n",
lli_read(desc, sar),
lli_read(desc, dar),
lli_read(desc, llp),
lli_read(desc, ctl),
lli_read(desc, block_ts));
}
#if 0
#define ENABLE_ALL_REG_STATUS (0x3 << 0 | 0xFFF << 3 | 0x3F << 16 | 0x1F << 27)
static void dwc_interrupts_set(struct dw_dma_chan *dwc, bool val)
{
u64 int_val;
if (val) {
/* channel int signal enable*/
int_val = DWC_CH_INTSIG_DMA_TRA_DONE;
channel_writeq(dwc, INTSIGNAL_ENABLEREG, int_val);
/* channel int status enable in reg, all enable*/
channel_writeq(dwc, INTSTATUS_ENABLEREG, ENABLE_ALL_REG_STATUS);
} else {
/* channel int signal disable*/
int_val = channel_readq(dwc, INTSIGNAL_ENABLEREG);
int_val &= ~DWC_CH_INTSIG_DMA_TRA_DONE;
channel_writeq(dwc, INTSIGNAL_ENABLEREG, int_val);
/* channel int status disable in reg, all disable*/
int_val = channel_readq(dwc, INTSTATUS);
channel_writeq(dwc, INTCLEARREG, int_val);
int_val &= ~ENABLE_ALL_REG_STATUS;
channel_writeq(dwc, INTSTATUS_ENABLEREG, int_val);
}
}
#endif
static void dwc_prepare_clk(struct dw_dma *dw)
{
int err;
dw->clk_count++;
if (dw->clk_count == 1) {
err = clk_enable(dw->clk);
if (err)
dev_err(dw->dev, "CVITEK DMA enable clk_sdma_axi failed\n");
}
}
static void dwc_unprepare_clk(struct dw_dma *dw)
{
dw->clk_count--;
if (dw->clk_count == 0)
clk_disable(dw->clk);
if (dw->clk_count < 0)
dev_err(dw->dev, "CVITEK sysDMA clk count is invalid\n");
}
static void dwc_initialize(struct dw_dma_chan *dwc)
{
u64 cfg = 0, int_status_reg;
bool has_device_fc;
bool hs_polarity = dwc->dws.hs_polarity;
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
has_device_fc = sconfig->device_fc;
if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
return;
cfg = DWC_CFG_DST_PER((u64)dwc->dws.dst_id)
| DWC_CFG_SRC_PER((u64)dwc->dws.src_id)
| DWC_CFG_SRC_OSR_LMT((u64)DW_DMA_MAX_NR_REQUESTS - 1)
| DWC_CFG_DST_OSR_LMT((u64)DW_DMA_MAX_NR_REQUESTS - 1)
| DWC_CFG_CH_PRIOR((u64)dwc->priority)
| DWC_CFG_DST_MULTBLK_TYPE(LINK_LIST)
| DWC_CFG_SRC_MULTBLK_TYPE(LINK_LIST);
/* choose Flowcontrol and handshaking type */
switch (dwc->direction) {
case DMA_MEM_TO_MEM:
cfg |= DWC_CFG_TT_FC((u64)DW_DMA_FC_D_M2M);
break;
case DMA_MEM_TO_DEV:
cfg |= has_device_fc ? DWC_CFG_TT_FC((u64)DW_DMA_FC_DP_M2P)
: DWC_CFG_TT_FC((u64)DW_DMA_FC_D_M2P);
cfg |= DWC_CFG_HS_SEL_DST_HW;
break;
case DMA_DEV_TO_MEM:
cfg |= has_device_fc ? DWC_CFG_TT_FC((u64)DW_DMA_FC_SP_P2M)
: DWC_CFG_TT_FC((u64)DW_DMA_FC_D_P2M);
cfg |= DWC_CFG_HS_SEL_SRC_HW;
break;
default:
break;
}
/* Set polarity of handshake interface */
cfg |= hs_polarity ? DWC_CFG_SRC_HWHS_POL_H | DWC_CFG_DST_HWHS_POL_H : 0;
channel_writeq(dwc, CFG, cfg);
/* Enable interrupts */
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags))
int_status_reg = DWC_CH_INTSTA_BLOCK_TFR_DONE;
else {
int_status_reg = DWC_CH_INTSTA_DMA_TFR_DONE;
#if 0
| DWC_CH_INTSTA_BLOCK_TFR_DONE
| DWC_CH_INTSTA_SRC_TFR_COMP_EN
| DWC_CH_INTSTA_DST_TFR_COMP_EN
| DWC_CH_INTSTA_SRC_DEC_ERR_EN
| DWC_CH_INTSTA_DST_DEC_ERR_EN
| DWC_CH_INTSTA_LLI_RD_DEV_ERR_EN
| DWC_CH_INTSTA_LLI_WD_DEV_ERR_EN
| DWC_CH_INTSTA_LLI_RD_SLV_ERR_EN
| DWC_CH_INTSTA_LLI_WD_SLV_ERR_EN
| DWC_CH_INTSTA_CH_ABORTED_EN;
#endif
}
channel_writeq(dwc, INTSTATUS_ENABLEREG, int_status_reg);
set_bit(DW_DMA_IS_INITIALIZED, &dwc->flags);
dwc->status &= ~DWC_CH_INTSIG_DMA_TRA_DONE;
}
/* Called with dwc->lock held and bh disabled */
#define DWC_DMA_MAX_RETRY 3000
static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
{
u64 ch_en_reg;
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
u32 retry_count = 0;
/* ASSERT: channel is idle */
while (dma_readq(dw, CH_EN) & dwc->mask) {
if (retry_count >= DWC_DMA_MAX_RETRY) {
dev_err(chan2dev(&dwc->chan),
"%s: BUG: Attempted to start non-idle channel\n",
__func__);
//dwc_dump_chan_regs(dwc);
/* The tasklet will hopefully advance the queue... */
return;
}
retry_count++;
}
dwc_initialize(dwc);
channel_writeq(dwc, LLP, first->txd.phys);
ch_en_reg = dwc->mask << DW_DMAC_CH_EN_WE_OFFSET;
dma_writeq(dw, CH_EN, dwc->mask | ch_en_reg);
}
static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc)
{
return to_dw_desc(dwc->active_list.next);
}
static void dwc_dostart_first_queued(struct dw_dma_chan *dwc)
{
struct dw_desc *desc;
if (list_empty(&dwc->queue))
return;
list_move(dwc->queue.next, &dwc->active_list);
desc = dwc_first_active(dwc);
dwc_dostart(dwc, desc);
}
static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
{
unsigned long flags;
dma_cookie_t cookie;
struct dw_desc *desc = txd_to_dw_desc(tx);
struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan);
spin_lock_irqsave(&dwc->lock, flags);
cookie = dma_cookie_assign(tx);
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
spin_unlock_irqrestore(&dwc->lock, flags);
return cookie;
}
/*
* REVISIT: We should attempt to chain as many descriptors as
* possible, perhaps even appending to those already submitted
* for DMA. But this is hard to do in a race-free manner.
*/
list_add_tail(&desc->desc_node, &dwc->queue);
spin_unlock_irqrestore(&dwc->lock, flags);
return cookie;
}
static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
{
dma_addr_t phys;
struct dw_desc *desc;
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
desc = dma_pool_zalloc(dw->desc_pool, GFP_ATOMIC, &phys);
if (!desc)
return NULL;
dwc->descs_allocated++;
INIT_LIST_HEAD(&desc->tx_list);
dma_async_tx_descriptor_init(&desc->txd, &dwc->chan);
desc->txd.tx_submit = dwc_tx_submit;
desc->txd.flags = DMA_CTRL_ACK;
desc->txd.phys = phys;
return desc;
}
static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
{
struct dw_desc *child, *_next;
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
if (unlikely(!desc))
return;
list_for_each_entry_safe(child, _next, &desc->tx_list, desc_node) {
list_del(&child->desc_node);
dma_pool_free(dw->desc_pool, child, child->txd.phys);
dwc->descs_allocated--;
}
dma_pool_free(dw->desc_pool, desc, desc->txd.phys);
dwc->descs_allocated--;
}
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
static dma_addr_t fix_dma_bug_copy_get(struct dw_dma_chan *dwc,
struct dma_chan *chan, struct scatterlist *sgl,
int sg_index, int sg_len, int size)
{
int i;
dma_addr_t bug_addr;
unsigned int *bug_buf = NULL;
unsigned char *tmp_buf, *record;
unsigned int fix_size;
char dma_pool_name[32];
struct dbg_fix_sg *db_sg = dwc->bug_info;
if (!db_sg) {
db_sg = kzalloc(sizeof(*db_sg), GFP_ATOMIC);
if (!db_sg) {
dev_err(chan->device->dev,
"BUG: Alloc db_sg failed,No memory !\n");
return 0;
}
dwc->bug_info = db_sg;
}
db_sg->count = sg_len;
if (!db_sg->fix_buf) {
db_sg->fix_buf = kcalloc(sg_len, sizeof(struct dbg_fix_buf), GFP_ATOMIC);
if (!db_sg->fix_buf) {
dev_err(chan->device->dev,
"BUG: Alloc fix_buf failed,No memory !\n");
return 0;
}
}
if (!dwc->bug_fix_dma_pool) {
snprintf(dma_pool_name, sizeof(dma_pool_name), "%s_%x",
"dma_chan_buf", dwc->mask);
dwc->bug_fix_dma_pool = dma_pool_create(dma_pool_name, chan->device->dev,
PAGE_SIZE, BIT(2), 0);
if (!dwc->bug_fix_dma_pool) {
dev_err(chan->device->dev, "unable to allocate dma pool\n");
return 0;
}
}
record = tmp_buf = kzalloc(size, GFP_ATOMIC);
if (!tmp_buf) {
dev_err(chan->device->dev,
"BUG: Alloc tmp_buf failed,No memory !\n");
return 0;
}
fix_size = (size << 2);
if (unlikely(fix_size > PAGE_SIZE)) {
bug_buf = dma_alloc_coherent(chan->device->dev, fix_size,
&bug_addr, GFP_ATOMIC);
if (!bug_buf) {
dev_err(chan->device->dev,
"BUG: Alloc bug_buf failed,No DMA memory !\n");
return 0;
}
} else {
bug_buf = dma_pool_zalloc(dwc->bug_fix_dma_pool, GFP_ATOMIC, &bug_addr);
if (!bug_buf) {
dev_err(chan->device->dev,
"BUG: Alloc bug_buf failed,No DMA Pool memory !\n");
return 0;
}
}
db_sg->fix_buf[sg_index].id = sg_index;
db_sg->fix_buf[sg_index].cpu_addr = bug_buf;
db_sg->fix_buf[sg_index].dma_hanle = bug_addr;
db_sg->fix_buf[sg_index].size = fix_size;
db_sg->fix_buf[sg_index].state = 1;
//sg_copy_to_buffer(sgl, nents, tmp_buf, size);
memcpy((void *)tmp_buf, sg_virt(sgl), size);
for (i = 0; i < size; i++) {
memcpy((void *)bug_buf, (void *)tmp_buf, 1);
bug_buf++;
tmp_buf++;
}
kfree(record);
return bug_addr;
}
static void fix_dma_bug_copy_put(struct dw_dma_chan *dwc, struct dma_chan *chan)
{
int i;
struct dbg_fix_sg *db_sg;
struct dbg_fix_buf *db_buf;
if (!dwc->bug_info)
return;
if (!dwc->bug_info->fix_buf)
return;
db_sg = dwc->bug_info;
db_buf = db_sg->fix_buf;
for (i = 0; i < db_sg->count; i++) {
if (db_buf->size > PAGE_SIZE) {
dma_free_coherent(chan->device->dev, db_buf->size,
db_buf->cpu_addr, db_buf->dma_hanle);
} else {
dma_pool_free(dwc->bug_fix_dma_pool, db_buf->cpu_addr, db_buf->dma_hanle);
}
db_buf->state = 0;
db_buf++;
}
/* dump info */
db_buf = db_sg->fix_buf;
for (i = 0; i < db_sg->count; i++) {
if (db_buf->state == 1)
dev_warn(chan->device->dev,
"db_buf id: %d,cpu_addr %p, dma_hanle %pad, size 0x%x state %d\n",
db_buf->id, db_buf->cpu_addr, &(db_buf->dma_hanle), db_buf->size, db_buf->state);
}
kfree(db_sg->fix_buf);
db_sg->fix_buf = NULL;
}
#endif
static void dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
bool callback_required)
{
unsigned long flags;
struct dw_desc *child;
struct dmaengine_desc_callback cb;
struct dma_async_tx_descriptor *txd = &desc->txd;
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
spin_lock_irqsave(&dwc->lock, flags);
dma_cookie_complete(txd);
dwc->status &= ~DWC_CH_INTSIG_DMA_TRA_DONE;
if (callback_required)
dmaengine_desc_get_callback(txd, &cb);
else
memset(&cb, 0, sizeof(cb));
/* async_tx_ack */
list_for_each_entry(child, &desc->tx_list, desc_node)
async_tx_ack(&child->txd);
async_tx_ack(&desc->txd);
dwc_desc_put(dwc, desc);
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
fix_dma_bug_copy_put(dwc, &dwc->chan);
#endif
dmaengine_desc_callback_invoke(&cb, NULL);
dwc_unprepare_clk(dw);
spin_unlock_irqrestore(&dwc->lock, flags);
}
static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
{
unsigned long flags;
struct dw_desc *desc, *_desc;
LIST_HEAD(list);
spin_lock_irqsave(&dwc->lock, flags);
/*
* Submit queued descriptors ASAP, i.e. before we go through
* the completed ones.
*/
list_splice_init(&dwc->active_list, &list);
dwc_dostart_first_queued(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc, true);
}
/* Returns how many bytes were already received from source */
static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
{
u64 bts = channel_readq(dwc, BLOCK_TS);
u64 ctl = channel_readq(dwc, CTL);
return ((bts & DWC_BLOCK_TS_MASK) + 1) * (1 << (ctl >> 8 & 7));
}
static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc)
{
u64 dma_ch_en;
dma_ch_en = dma_readq(dw, CH_EN);
dma_ch_en |= (dwc->mask << DW_DMAC_CH_EN_WE_OFFSET);
dma_ch_en |= (dwc->mask << DW_DMAC_CH_ABORT_WE_OFFSET);
dma_ch_en |= (dwc->mask << DW_DMAC_CH_ABORT_OFFSET);
dma_ch_en &= ~dwc->mask;
dma_writeq(dw, CH_EN, dma_ch_en);
while (dma_readq(dw, CH_EN) & dwc->mask)
cpu_relax();
}
static int dwc_resume(struct dma_chan *chan)
{
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
spin_lock_irqsave(&dwc->lock, flags);
if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags)) {
dma_clear_bit(dw, CH_EN,
(1 << (__ffs(dwc->mask) + DW_DMAC_CH_PAUSE_OFFSET)));
clear_bit(DW_DMA_IS_PAUSED, &dwc->flags);
}
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
static int dwc_pause(struct dma_chan *chan)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
unsigned long flags;
unsigned int count = 20; /* timeout iterations */
u64 dma_ch_en;
spin_lock_irqsave(&dwc->lock, flags);
dma_set_bit(dw, CH_EN,
(1 << (__ffs(dwc->mask) + DW_DMAC_CH_PAUSE_OFFSET))
| (1 << (__ffs(dwc->mask) + DW_DMAC_CH_PAUSE_EN_OFFSET)));
while (!(dma_readq(dw, CH_EN)
& (1 << (__ffs(dwc->mask) + DW_DMAC_CH_PAUSE_OFFSET)))
&& count--)
udelay(2);
dma_ch_en = dma_readq(dw, CH_EN);
dma_ch_en |= (dwc->mask << DW_DMAC_CH_EN_WE_OFFSET);
dma_ch_en &= ~dwc->mask;
dma_writeq(dw, CH_EN, dma_ch_en);
set_bit(DW_DMA_IS_PAUSED, &dwc->flags);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
{
dma_addr_t llp;
unsigned long flags;
u64 status_xfer;
struct dw_desc *desc, *_desc;
struct dw_desc *child;
spin_lock_irqsave(&dwc->lock, flags);
llp = channel_readq(dwc, LLP);
status_xfer = dma_readq(dw, CH_EN);
if (~(status_xfer & 0xff) & dwc->mask) {
spin_unlock_irqrestore(&dwc->lock, flags);
dwc_complete_all(dw, dwc);
return;
}
if (list_empty(&dwc->active_list)) {
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
/* Initial residue value */
desc->residue = desc->total_len;
/* Check first descriptors addr */
if (desc->txd.phys == DWC_LLP_LOC(llp)) {
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
/* Check first descriptors llp */
if (lli_read(desc, llp) == llp) {
/* This one is currently in progress */
desc->residue -= dwc_get_sent(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
desc->residue -= desc->len;
list_for_each_entry(child, &desc->tx_list, desc_node) {
if (lli_read(child, llp) == llp) {
/* Currently in progress */
desc->residue -= dwc_get_sent(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
desc->residue -= child->len;
}
/*
* No descriptors so far seem to be in progress, i.e.
* this one must be done.
*/
spin_unlock_irqrestore(&dwc->lock, flags);
dwc_descriptor_complete(dwc, desc, true);
spin_lock_irqsave(&dwc->lock, flags);
}
dev_err(chan2dev(&dwc->chan),
"BUG: All descriptors done, but channel not idle!\n");
/* Try to continue after resetting the channel... */
dwc_chan_disable(dw, dwc);
dwc_dostart_first_queued(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
}
/*----------------------------------------------------------------------*/
static void dwc_issue_pending(struct dma_chan *chan)
{
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
dw_dma_cyclic_start(chan);
return;
}
spin_lock_irqsave(&dwc->lock, flags);
if (list_empty(&dwc->active_list))
dwc_dostart_first_queued(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
}
static int dwc_stop_cyclic_all(struct dw_dma_chan *dwc)
{
int i;
unsigned long flags;
struct dw_desc *desc;
struct dma_async_tx_descriptor *txd;
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
desc = dwc->cdesc->desc[0];
txd = &desc->txd;
spin_lock_irqsave(&dwc->lock, flags);
dma_cookie_complete(txd);
dwc->status &= ~DWC_CH_INTSIG_DMA_TRA_DONE;
clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
for (i = 0; i < dwc->cdesc->periods; i++)
dwc_desc_put(dwc, dwc->cdesc->desc[i]);
kfree(dwc->cdesc->desc);
kfree(dwc->cdesc);
dwc_unprepare_clk(dw);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
static int dwc_terminate_all(struct dma_chan *chan)
{
unsigned long flags;
struct dw_desc *desc, *_desc;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
LIST_HEAD(list);
spin_lock_irqsave(&dwc->lock, flags);
dwc_chan_disable(dw, dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
dwc_resume(chan);
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags))
return dwc_stop_cyclic_all(dwc);
/* active_list entries will end up before queued entries */
spin_lock_irqsave(&dwc->lock, flags);
list_splice_init(&dwc->queue, &list);
list_splice_init(&dwc->active_list, &list);
spin_unlock_irqrestore(&dwc->lock, flags);
/* Flush all pending and queued descriptors */
list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc, false);
return 0;
}
static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
/* Check if chan will be configured for slave transfers */
if (!is_slave_direction(sconfig->direction))
return -EINVAL;
memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
dwc->direction = sconfig->direction;
convert_burst(&dwc->dma_sconfig.src_maxburst);
convert_burst(&dwc->dma_sconfig.dst_maxburst);
return 0;
}
static struct dw_desc *dwc_find_desc(struct dw_dma_chan *dwc, dma_cookie_t c)
{
struct dw_desc *desc;
list_for_each_entry(desc, &dwc->active_list, desc_node)
if (desc->txd.cookie == c)
return desc;
return NULL;
}
static u32 dwc_get_residue(struct dw_dma_chan *dwc, dma_cookie_t cookie)
{
u32 residue;
unsigned long flags;
struct dw_desc *desc;
spin_lock_irqsave(&dwc->lock, flags);
desc = dwc_find_desc(dwc, cookie);
if (desc)
if (desc == dwc_first_active(dwc))
residue = desc->residue;
else
residue = desc->total_len;
else
residue = 0;
spin_unlock_irqrestore(&dwc->lock, flags);
return residue;
}
static enum dma_status dwc_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
enum dma_status ret;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_COMPLETE)
return ret;
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
if (cookie == dwc->cdesc->desc[0]->txd.cookie) {
dma_set_residue(txstate, dwc->cdesc->desc[0]->residue);
return ret;
}
dma_set_residue(txstate, dwc->cdesc->desc[0]->total_len);
return ret;
}
dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_COMPLETE)
return ret;
dma_set_residue(txstate, dwc_get_residue(dwc, cookie));
if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags) && ret == DMA_IN_PROGRESS)
return DMA_PAUSED;
return ret;
}
static struct dma_async_tx_descriptor *dwc_prep_dma_memcpy(struct dma_chan *chan,
dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags)
{
u8 m_master;
u64 ctl = 0;
unsigned int trans_width;
unsigned int data_width;
size_t offset = 0;
size_t xfer_count = 0;
struct dw_dma *dw = to_dw_dma(chan->device);
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_desc *desc;
struct dw_desc *first;
struct dw_desc *prev;
unsigned long spin_flags;
u32 trans_block;
m_master = dwc->dws.m_master;
data_width = dw->data_width[m_master];
trans_block = dwc->block_size;
#ifdef DBG_DMA
pr_err("%s trans_block %d, len 0x%x\n", __func__, trans_block, (int)len);
#endif
if (unlikely(!len))
return NULL;
spin_lock_irqsave(&dwc->lock, spin_flags);
dwc_prepare_clk(dw);
spin_unlock_irqrestore(&dwc->lock, spin_flags);
dwc->direction = DMA_MEM_TO_MEM;
/*
* for mem2mem type we defaultly set the src/dst width bits
* to max value 32(axi bus width) consider of performence,
* you can change it by dts.
*/
trans_width = __ffs(data_width | src | dest | len);
ctl = DWC_DEFAULT_CTL(chan)
| DWC_CTL_DST_WIDTH(trans_width)
| DWC_CTL_SRC_WIDTH(trans_width)
| DWC_CTL_DST_INC
| DWC_CTL_SRC_INC
| DWC_CTL_DST_STA_EN
| DWC_CTL_SRC_STA_EN;
/* axi bus max width is 32bits == 4 bytes
* dma max block ts is 32, for one dma transfer, the max data size
* can be transferred is 32 * 4 bytes
*/
prev = first = NULL;
for (offset = 0; offset < len; offset += xfer_count << trans_width) {
xfer_count = min_t(size_t, (len - offset) >> trans_width,
trans_block >> trans_width);
desc = dwc_desc_get(dwc);
if (!desc)
goto err_desc_get;
lli_write(desc, sar, src + offset);
lli_write(desc, dar, dest + offset);
lli_write(desc, ctl, ctl | DWC_CTL_SHADOWREG_OR_LLI_VALID);
lli_write(desc, block_ts, xfer_count - 1);
desc->len = xfer_count << trans_width;
if (!first) {
first = desc;
} else {
lli_write(prev, llp, desc->txd.phys);
list_add_tail(&desc->desc_node, &first->tx_list);
}
prev = desc;
}
/* set the final block in lli,
*dma end this transfer wen the current block completes
*/
lli_set(prev, ctl, DWC_CTL_SHADOWREG_OR_LLI_LAST);
if (flags & DMA_PREP_INTERRUPT) {
/* Trigger interrupt after last block */
lli_set(prev, ctl, DWC_CTL_IOC_BLT_EN);
}
prev->lli.llp = 0;
first->txd.flags = flags;
first->total_len = len;
return &first->txd;
err_desc_get:
dwc_desc_put(dwc, first);
return NULL;
}
static struct dma_async_tx_descriptor *dwc_prep_dma_slave_sg(struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context)
{
u8 m_master;
u32 src_max_burst;
u32 dst_max_burst;
u32 trans_block;
u64 ctl = 0;
unsigned int reg_width;
unsigned int mem_width;
unsigned int data_width;
unsigned int i;
size_t total_len = 0;
dma_addr_t reg;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_desc *prev;
struct dw_desc *first;
struct scatterlist *sg;
unsigned long spin_flags;
u64 tmp;
spin_lock_irqsave(&dwc->lock, spin_flags);
dwc_prepare_clk(dw);
spin_unlock_irqrestore(&dwc->lock, spin_flags);
m_master = dwc->dws.m_master;
src_max_burst = sconfig->src_maxburst;
dst_max_burst = sconfig->dst_maxburst;
trans_block = dwc->block_size;
data_width = dw->data_width[m_master];
if (unlikely(!is_slave_direction(direction) || !sg_len))
return NULL;
dwc->direction = direction;
prev = first = NULL;
switch (direction) {
case DMA_MEM_TO_DEV:
reg_width = __ffs(sconfig->dst_addr_width);
reg = sconfig->dst_addr;
ctl = (DWC_DEFAULT_CTL(chan)
| DWC_CTL_DST_WIDTH(reg_width)
| DWC_CTL_DST_FIX
| DWC_CTL_SRC_INC
| DWC_CTL_DST_STA_EN
| DWC_CTL_SRC_STA_EN);
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u64 mem, len, dlen, tmp_len;
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
/* bug fix axi fix 32bits m2d,
* reg_width <0: 8bits,1:16bits,2:32bits>
*/
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
if (reg_width != 0x3)
mem = (u64)fix_dma_bug_copy_get(dwc, chan, sg, i, sg_len, len);
tmp_len = len * (4 >> reg_width);
#endif
tmp_len = len;
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
mem_width = 2;
#else
mem_width = __ffs(data_width | mem | len);
#endif
slave_sg_todev_fill_desc:
desc = dwc_desc_get(dwc);
if (!desc)
goto err_desc_get;
lli_write(desc, sar, mem);
lli_write(desc, dar, reg);
lli_write(desc, ctl, ctl |
(DWC_CTL_SRC_WIDTH(mem_width) | DWC_CTL_SHADOWREG_OR_LLI_VALID));
if (tmp_len >> mem_width > trans_block) {
dlen = trans_block << mem_width;
mem += dlen;
tmp_len -= dlen;
} else {
dlen = tmp_len;
tmp_len = 0;
}
/* single trans when dst_max_burst == 0*/
lli_write(desc, block_ts, !dst_max_burst ? 0 : ((dlen >> mem_width) - 1));
//lli_write(desc, block_ts, dlen);
desc->len = dlen;
div_u64(desc->len, (4 >> reg_width));
if (!first) {
first = desc;
} else {
lli_write(prev, llp, desc->txd.phys);
list_add_tail(&desc->desc_node, &first->tx_list);
}
prev = desc;
tmp = dlen;
div_u64(tmp, (4 >> reg_width));
total_len += tmp;
if (tmp_len)
goto slave_sg_todev_fill_desc;
}
break;
case DMA_DEV_TO_MEM:
reg_width = __ffs(sconfig->src_addr_width);
reg = sconfig->src_addr;
ctl = (DWC_DEFAULT_CTL(chan)
| DWC_CTL_SRC_WIDTH(reg_width)
| DWC_CTL_DST_INC
| DWC_CTL_SRC_FIX
| DWC_CTL_DST_STA_EN
| DWC_CTL_SRC_STA_EN);
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u64 len = 0, dlen = 0, mem = 0;
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
mem_width = 2;
#else
mem_width = __ffs(data_width | mem | len);
#endif
slave_sg_fromdev_fill_desc:
desc = dwc_desc_get(dwc);
if (!desc)
goto err_desc_get;
lli_write(desc, sar, reg);
lli_write(desc, dar, mem);
ctl |= (DWC_CTL_DST_WIDTH(mem_width) | DWC_CTL_SHADOWREG_OR_LLI_VALID);
lli_write(desc, ctl, ctl);
if ((len >> reg_width) > trans_block) {
dlen = (trans_block << reg_width);
#ifdef CONFIG_SYSDMA_JIRA_BUG_BM1880_17
mem += dlen * (4 >> reg_width);
#else
mem += dlen;
#endif
len -= dlen;
} else {
dlen = len;
len = 0;
}
lli_write(desc, block_ts, !src_max_burst ? 0 : (dlen >> reg_width) - 1);
desc->len = dlen;
if (!first) {
first = desc;
} else {
lli_write(prev, llp, desc->txd.phys);
list_add_tail(&desc->desc_node, &first->tx_list);
}
prev = desc;
total_len += dlen;
if (len)
goto slave_sg_fromdev_fill_desc;
}
break;
default:
return NULL;
}
/* set the final block in lli,
* dma end this transfer wen the current block completes
*/
lli_set(prev, ctl, DWC_CTL_SHADOWREG_OR_LLI_LAST);
if (flags & DMA_PREP_INTERRUPT)
/* Trigger interrupt after last block */
lli_set(prev, ctl, DWC_CTL_IOC_BLT_EN);
prev->lli.llp = 0;
first->total_len = total_len;
return &first->txd;
err_desc_get:
dev_err(chan2dev(chan),
"not enough descriptors available. Direction %d\n", direction);
dwc_desc_put(dwc, first);
return NULL;
}
/* --------------------- Cyclic DMA API extensions -------------------- */
/**
* dw_dma_cyclic_start - start the cyclic DMA transfer
* @chan: the DMA channel to start
*
* Must be called with soft interrupts disabled. Returns zero on success or
* -errno on failure.
*/
static int dw_dma_cyclic_start(struct dma_chan *chan)
{
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n");
return -ENODEV;
}
spin_lock_irqsave(&dwc->lock, flags);
/* Enable interrupts to perform cyclic transfer */
//dwc_interrupts_set(dwc, true);
dwc_dostart(dwc, dwc->cdesc->desc[0]);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
/**
* dw_dma_cyclic_stop - stop the cyclic DMA transfer
* @chan: the DMA channel to stop
*
* Must be called with soft interrupts disabled.
*/
void dw_dma_cyclic_stop(struct dma_chan *chan)
{
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
spin_lock_irqsave(&dwc->lock, flags);
dwc_chan_disable(dw, dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
}
/**
* dw_dma_cyclic_prep - prepare the cyclic DMA transfer
* @chan: the DMA channel to prepare
* @buf_addr: physical DMA address where the buffer starts
* @buf_len: total number of bytes for the entire buffer
* @period_len: number of bytes for each period
* @direction: transfer direction, to or from device
*
* Must be called before trying to start the transfer. Returns a valid struct
* dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful.
*/
static struct dma_async_tx_descriptor *dwc_prep_dma_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 dma_flags)
{
unsigned int i;
unsigned int reg_width;
unsigned int periods;
unsigned long flags;
unsigned long was_cyclic;
struct dw_cyclic_desc *cdesc;
struct dw_cyclic_desc *retval = NULL;
struct dw_desc *desc;
struct dw_desc *last = NULL;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
unsigned int tmp_len;
size_t target_period_len;
u64 ctl_val = 0;
struct dw_dma *dw = to_dw_dma(chan->device);
spin_lock_irqsave(&dwc->lock, flags);
dwc_prepare_clk(dw);
if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) {
spin_unlock_irqrestore(&dwc->lock, flags);
dev_dbg(chan2dev(&dwc->chan),
"queue and/or active list are not empty\n");
return ERR_PTR(-EBUSY);
}
was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
spin_unlock_irqrestore(&dwc->lock, flags);
if (was_cyclic) {
dev_dbg(chan2dev(&dwc->chan),
"channel already prepared for cyclic DMA\n");
return ERR_PTR(-EBUSY);
}
retval = ERR_PTR(-EINVAL);
if (unlikely(!is_slave_direction(direction)))
goto out_err;
dwc->direction = direction;
if (direction == DMA_MEM_TO_DEV)
reg_width = __ffs(sconfig->dst_addr_width);
else
reg_width = __ffs(sconfig->src_addr_width);
/* Change period to 128 bytes due to the maximum LLI data length*/
/*of Bitmain sysDMA (32 bytes * 4 bytes bus width)*/
tmp_len = (1 << (__ffs(buf_len) - 1));
target_period_len = period_len;
if (period_len > BM_DMA_PERIOD_LEN)
period_len = BM_DMA_PERIOD_LEN;
if (tmp_len < period_len)
period_len = tmp_len;
periods = buf_len / period_len;
/* Check for too big/unaligned periods and unaligned DMA buffer. */
if (period_len > (dwc->block_size << reg_width))
goto out_err;
if (unlikely(period_len & ((1 << reg_width) - 1)))
goto out_err;
if (unlikely(buf_addr & ((1 << reg_width) - 1)))
goto out_err;
retval = ERR_PTR(-ENOMEM);
cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_ATOMIC);
if (!cdesc)
goto out_err;
cdesc->desc = kcalloc(periods, sizeof(struct dw_desc *), GFP_ATOMIC);
if (!cdesc->desc)
goto out_err_alloc;
for (i = 0; i < periods; i++) {
desc = dwc_desc_get(dwc);
if (!desc)
goto out_err_desc_get;
switch (direction) {
case DMA_MEM_TO_DEV:
lli_write(desc, dar, sconfig->dst_addr);
lli_write(desc, sar, buf_addr + period_len * i);
ctl_val = (DWC_DEFAULT_CTL(chan)
#if defined(CONFIG_ARCH_CVITEK)
| DWC_CTL_ARLEN_EN
| DWC_CTL_ARLEN(7UL)
| DWC_CTL_AWLEN_EN
| DWC_CTL_AWLEN(0UL)
#endif
| DWC_CTL_SHADOWREG_OR_LLI_VALID
| DWC_CTL_DST_WIDTH(reg_width)
| DWC_CTL_SRC_WIDTH(reg_width)
| DWC_CTL_DST_FIX
| DWC_CTL_SRC_INC);
lli_write(desc, ctl, ctl_val);
if ((period_len * (i + 1)) % target_period_len == 0)
lli_set(desc, ctl, DWC_CTL_IOC_BLT_EN);
break;
case DMA_DEV_TO_MEM:
lli_write(desc, dar, buf_addr + period_len * i);
lli_write(desc, sar, sconfig->src_addr);
ctl_val = (DWC_DEFAULT_CTL(chan)
#if defined(CONFIG_ARCH_CVITEK)
| DWC_CTL_ARLEN_EN
| DWC_CTL_ARLEN(0UL)
| DWC_CTL_AWLEN_EN
| DWC_CTL_AWLEN(7UL)
#endif
| DWC_CTL_SHADOWREG_OR_LLI_VALID
| DWC_CTL_SRC_WIDTH(reg_width)
| DWC_CTL_DST_WIDTH(reg_width)
| DWC_CTL_DST_INC
| DWC_CTL_SRC_FIX);
lli_write(desc, ctl, ctl_val);
if ((period_len * (i + 1)) % target_period_len == 0)
lli_set(desc, ctl, DWC_CTL_IOC_BLT_EN);
break;
default:
break;
}
lli_write(desc, block_ts, (period_len >> reg_width) - 1);
cdesc->desc[i] = desc;
if (last) {
lli_write(last, llp, desc->txd.phys);
last->total_len = target_period_len;
}
last = desc;
}
/* Let's make a cyclic list */
lli_write(last, llp, cdesc->desc[0]->txd.phys);
/* Trigger interrupt for last block */
lli_set(last, ctl, DWC_CTL_IOC_BLT_EN);
if (dw->log_on)
dev_info(chan2dev(&dwc->chan),
"cyclic prepared buf 0x%llx len %zu period %zu periods %d\n",
buf_addr, buf_len, period_len, periods);
cdesc->desc[0]->total_len = buf_len;
cdesc->desc[0]->hw_pos = 0;
cdesc->desc[0]->residue = buf_len;
cdesc->periods = periods;
cdesc->period_len = target_period_len;
cdesc->last_sent = 0;
dwc->cdesc = cdesc;
dwc->hw_pos = 0;
dwc->interrupt_count = 0;
return &cdesc->desc[0]->txd;
out_err_desc_get:
while (i--)
dwc_desc_put(dwc, cdesc->desc[i]);
out_err_alloc:
kfree(cdesc->desc);
out_err:
kfree(cdesc);
clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
return (struct dma_async_tx_descriptor *)retval;
}
static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc)
{
dma_addr_t llp;
int i;
u32 sent_total = 0;
struct dw_desc *desc, *first;
struct dmaengine_desc_callback cb;
u32 new_hw_pos = 0;
llp = channel_readq(dwc, LLP);
first = dwc->cdesc->desc[0];
sent_total += dwc->cdesc->last_sent;
if (first->txd.phys != DWC_LLP_LOC(llp)) {
for (i = 1; i < dwc->cdesc->periods; i++) {
desc = dwc->cdesc->desc[i];
new_hw_pos += dwc_get_sent(dwc);
if (desc->txd.phys == DWC_LLP_LOC(llp))
break;
}
} else
new_hw_pos = 0; /* back to ring buffer head */
if (new_hw_pos >= first->hw_pos)
sent_total += new_hw_pos - first->hw_pos;
else
sent_total += first->total_len + new_hw_pos - first->hw_pos;
first->hw_pos = new_hw_pos;
first->residue = first->total_len - (new_hw_pos - new_hw_pos % dwc->cdesc->period_len);
dwc->hw_pos = new_hw_pos;
dwc->interrupt_count++;
if (sent_total > dwc->cdesc->period_len) {
dmaengine_desc_get_callback(&first->txd, &cb);
dmaengine_desc_callback_invoke(&cb, NULL);
dwc->cdesc->last_sent = new_hw_pos % dwc->cdesc->period_len;
} else {
dwc->cdesc->last_sent = sent_total;
}
if (dw->log_on)
dev_info(chan2dev(&dwc->chan),
"SAR:0x%llx DAR:0x%llx residue:%d sent_total:%d ch_status:0x%llx int_status:0x%llx\n",
channel_readq(dwc, SAR), channel_readq(dwc, DAR), first->residue, sent_total,
channel_readq(dwc, STATUS), channel_readq(dwc, INTSTATUS));
/* TODO error resuem */
}
/**
* dw_dma_cyclic_free - free a prepared cyclic DMA transfer
* @chan: the DMA channel to free
*/
void dw_dma_cyclic_free(struct dma_chan *chan)
{
unsigned int i;
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
struct dw_cyclic_desc *cdesc = dwc->cdesc;
dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__);
if (!cdesc)
return;
spin_lock_irqsave(&dwc->lock, flags);
dwc_chan_disable(dw, dwc);
//dwc_interrupts_set(dwc, false);
spin_unlock_irqrestore(&dwc->lock, flags);
for (i = 0; i < cdesc->periods; i++)
dwc_desc_put(dwc, cdesc->desc[i]);
kfree(cdesc->desc);
kfree(cdesc);
dwc->cdesc = NULL;
clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
}
static void dwc_free_chan_resources(struct dma_chan *chan)
{
unsigned long flags;
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
LIST_HEAD(list);
/* ASSERT: channel is idle */
WARN_ON(!list_empty(&dwc->active_list));
WARN_ON(!list_empty(&dwc->queue));
WARN_ON(dma_readq(to_dw_dma(chan->device), CH_EN) & dwc->mask);
spin_lock_irqsave(&dwc->lock, flags);
/* Clear custom channel configuration */
memset(&dwc->dws, 0, sizeof(struct dw_dma_slave));
clear_bit(DW_DMA_IS_INITIALIZED, &dwc->flags);
/* Disable interrupts */
channel_writeq(dwc, INTSTATUS_ENABLEREG, 0);
//dwc_interrupts_set(dwc, false);
/* Disable controller in case it was a last user */
dw->in_use &= ~dwc->mask;
dwc->status &= ~DWC_CH_INTSIG_DMA_TRA_DONE;
if (!dw->in_use)
dw_dma_off(dw);
spin_unlock_irqrestore(&dwc->lock, flags);
}
static int dwc_alloc_chan_resources(struct dma_chan *chan)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
/* ASSERT: channel is idle */
if (dma_readq(dw, CH_EN) & dwc->mask)
return -EIO;
dma_cookie_init(chan);
/*
* NOTE: some controllers may have additional features that we
* need to initialize here, like "scatter-gather" (which
* doesn't mean what you think it means), and status writeback.
*/
/*
* We need controller-specific data to set up slave transfers.
*/
if (chan->private && !dw_dma_filter(chan, chan->private)) {
dev_warn(chan2dev(chan), "Wrong controller-specific data\n");
return -EINVAL;
}
/* Enable controller here if needed */
if (!dw->in_use)
dw_dma_on(dw);
dw->in_use |= dwc->mask;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
#ifndef DO_NOT_USE_TASK
static void dw_dma_tasklet(unsigned long data)
{
unsigned int i;
struct dw_dma *dw = (struct dw_dma *)data;
struct dw_dma_chan *dwc;
u64 ch_en;
ch_en = dma_readq(dw, CH_EN);
for (i = 0; i < dw->dma.chancnt; i++) {
dwc = &dw->chan[i];
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
if ((ch_en >> i) & 0x1)
dwc_handle_cyclic(dw, dwc);
} else{
if(dwc->status == DWC_CH_INTSIG_DMA_TRA_DONE)
dwc_scan_descriptors(dw, dwc);
}
//dwc_interrupts_set(dwc, true);
}
}
#endif
static void dw_dma_off(struct dw_dma *dw)
{
unsigned int i;
dma_writeq(dw, CFG, 0); /* disable dmac and interrupt */
/* Clear all interrupts on all channels. */
#if 0
for (i = 0; i < dw->dma.chancnt; i++)
dwc_interrupts_set(&dw->chan[i], false);
#endif
//while (dma_readq(dw, CFG) & DW_CFG_DMA_EN)
// cpu_relax();
for (i = 0; i < dw->dma.chancnt; i++)
clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags);
}
static void dw_dma_on(struct dw_dma *dw)
{
dma_writeq(dw, CFG, DW_CFG_DMA_EN | DW_CFG_DMA_INT_EN);
}
#ifdef DO_NOT_USE_TASK
static void instead_of_tasklet(struct dw_dma *dw)
{
unsigned int i;
struct dw_dma_chan *dwc;
u64 ch_en;
ch_en = dma_readq(dw, CH_EN);
for (i = 0; i < dw->dma.chancnt; i++) {
dwc = &dw->chan[i];
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
if ((ch_en >> i) & 0x1)
dwc_handle_cyclic(dw, dwc);
} else
dwc_scan_descriptors(dw, dwc);
//dwc_interrupts_set(dwc, true);
}
}
#endif
static irqreturn_t dw_dma_interrupt(int irq, void *dev_id)
{
int i;
u64 status,dwc_status;
struct dw_dma *dw = dev_id;
struct dw_dma_chan *dwc;
/* Check if we have any interrupt from the DMAC which is not in use */
if (!dw->in_use)
return IRQ_NONE;
status = dma_readq(dw, INTSTATUS);
/* Check if we have any interrupt from the DMAC */
if (!status)
return IRQ_NONE;
dma_writeq(dw, COMM_INTCLEAR, 0x10f); /* clear all common interrupts */
for (i = 0; i < dw->dma.chancnt; i++) {
dwc = &dw->chan[i];
dwc_status = channel_readq(dwc, INTSTATUS);
if(dwc_status == DWC_CH_INTSIG_DMA_TRA_DONE)
dwc->status = DWC_CH_INTSIG_DMA_TRA_DONE;
channel_writeq(dwc, INTCLEARREG, dwc_status);
//dwc_interrupts_set(dwc, false);
}
status = dma_readq(dw, INTSTATUS);
if (status) {
dev_dbg(dw->dma.dev,
"BUG: Unexpected interrupts pending: 0x%llx\n",
status);
}
#ifdef DO_NOT_USE_TASK
instead_of_tasklet(dw);
#else
tasklet_schedule(&dw->tasklet);
#endif
return IRQ_HANDLED;
}
static int dw_dma_channel_init(struct platform_device *pdev,
struct dw_dma *dw)
{
int i;
dw->chan = devm_kcalloc(dw->dev, dw->nr_channels,
sizeof(*dw->chan), GFP_KERNEL);
if (!dw->chan)
return -ENOMEM;
/* Create a pool of consistent memory blocks for hardware descriptors */
dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", dw->dev,
sizeof(struct dw_desc), 64, 0);
if (!dw->desc_pool) {
dev_err(dw->dev, "No memory for descriptors dma pool\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&dw->dma.channels);
for (i = 0; i < dw->nr_channels; i++) {
struct dw_dma_chan *dwc = &dw->chan[i];
dwc->chan.device = &dw->dma;
dma_cookie_init(&dwc->chan);
if (dw->chan_allocation_order == CHAN_ALLOCATION_ASCENDING)
list_add_tail(&dwc->chan.device_node, &dw->dma.channels);
else
list_add(&dwc->chan.device_node, &dw->dma.channels);
/* 7 is highest priority & 0 is lowest. */
if (dw->chan_priority == CHAN_PRIORITY_ASCENDING)
dwc->priority = dw->nr_channels - i - 1;
else
dwc->priority = i;
dwc->ch_regs = &__dw_regs(dw)->CHAN[i];
spin_lock_init(&dwc->lock);
dwc->mask = 1 << i;
channel_writeq(dwc, INTCLEARREG, 0xffffffff);
INIT_LIST_HEAD(&dwc->active_list);
INIT_LIST_HEAD(&dwc->queue);
dwc->direction = DMA_TRANS_NONE;
dwc->block_size = dw->block_size;
dwc->axi_tr_width = dw->block_size;
}
dma_clear_bit(dw, CH_EN, DW_DMA_CHAN_MASK);
/* channel en bit set */
dma_set_bit(dw, CH_EN, DW_DMA_CHAN_MASK << DW_DMAC_CH_EN_WE_OFFSET);
dma_set_bit(dw, CH_EN, DW_DMA_CHAN_MASK << DW_DMAC_CH_PAUSE_EN_OFFSET);
/* Set capabilities */
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
dma_cap_set(DMA_CYCLIC, dw->dma.cap_mask);
dw->dma.dev = dw->dev;
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
dw->dma.device_free_chan_resources = dwc_free_chan_resources;
dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy;
dw->dma.device_prep_slave_sg = dwc_prep_dma_slave_sg;
dw->dma.device_prep_dma_cyclic = dwc_prep_dma_cyclic;
dw->dma.device_config = dwc_config;
dw->dma.device_pause = dwc_pause;
dw->dma.device_resume = dwc_resume;
dw->dma.device_terminate_all = dwc_terminate_all;
dw->dma.device_tx_status = dwc_tx_status;
dw->dma.device_issue_pending = dwc_issue_pending;
/* DMA capabilities */
dw->dma.src_addr_widths = DW_DMA_BUSWIDTHS;
dw->dma.dst_addr_widths = DW_DMA_BUSWIDTHS;
dw->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) |
BIT(DMA_MEM_TO_MEM);
dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
return 0;
}
static int dw_dma_parse_dt(struct platform_device *pdev,
struct dw_dma *dw)
{
u32 tmp, arr[DW_DMA_MAX_NR_MASTERS];
struct device_node *np = pdev->dev.of_node;
if (!np) {
dev_err(&pdev->dev, "Missing DT data\n");
return -EINVAL;
}
/* set default config, can be override by dt */
dw->nr_masters = 2;
dw->is_private = true;
dw->is_memcpy = true;
dw->chan_allocation_order = CHAN_ALLOCATION_ASCENDING;
dw->chan_priority = CHAN_PRIORITY_ASCENDING;
of_property_read_u8(np, "dma-masters", &dw->nr_masters);
if (dw->nr_masters < 1 || dw->nr_masters > DW_DMA_MAX_NR_MASTERS)
return -EINVAL;
of_property_read_u8(np, "dma-channels", &dw->nr_channels);
if (dw->nr_channels > DW_DMA_MAX_NR_CHANNELS)
return -EINVAL;
if (of_property_read_bool(np, "is_private"))
dw->is_private = true;
of_property_read_u8(np, "chan_allocation_order",
&dw->chan_allocation_order);
of_property_read_u8(np, "chan_priority", &dw->chan_priority);
of_property_read_u32(np, "block_size", &dw->block_size);
of_property_read_u32(np, "block-ts", &dw->block_ts);
of_property_read_u32(np, "axi_tr_width", &dw->axi_tr_width);
if (!of_property_read_u32_array(np, "data-width", arr, dw->nr_masters)) {
for (tmp = 0; tmp < dw->nr_masters; tmp++)
dw->data_width[tmp] = arr[tmp];
}
return 0;
}
static int dw_dma_resource_init(struct platform_device *pdev,
struct dw_dma *dw)
{
int err;
struct resource *mem;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dw->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(dw->regs)) {
err = PTR_ERR(dw->regs);
goto err_resource;
}
dma_writeq(dw, RESET, 1); /* reset sysDMA */
dw->irq = platform_get_irq(pdev, 0);
if (dw->irq < 0)
return -EINVAL;
err = devm_request_irq(dw->dev, dw->irq, dw_dma_interrupt,
IRQF_SHARED, "dw_dmac", dw);
if (err)
return err;
spin_lock_init(&dw->lock);
err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err)
goto err_resource;
#ifndef DO_NOT_USE_TASK
tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw);
#endif
return 0;
err_resource:
return err;
}
static bool dw_dma_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_slave *dws = param;
if (dws->dma_dev != chan->device->dev)
return false;
/* We have to copy data since dws can be temporary storage */
memcpy(&dwc->dws, dws, sizeof(struct dw_dma_slave));
return true;
}
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
struct dw_dma *dw = ofdma->of_dma_data;
struct dw_dma_slave slave = {
.dma_dev = dw->dma.dev,
};
dma_cap_mask_t cap;
if (dma_spec->args_count != 3)
return NULL;
slave.src_id = dma_spec->args[0];
slave.dst_id = dma_spec->args[0];
slave.m_master = dma_spec->args[1];
slave.p_master = dma_spec->args[2];
if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS ||
slave.dst_id >= DW_DMA_MAX_NR_REQUESTS ||
slave.m_master >= dw->nr_masters ||
slave.p_master >= dw->nr_masters)) {
return NULL;
}
dma_cap_zero(cap);
dma_cap_set(DMA_SLAVE, cap);
/* TODO: there should be a simpler way to do this */
return dma_request_channel(cap, dw_dma_filter, &slave);
}
static ssize_t __show_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static DEVICE_ATTR(signal, 0644, __show_status, NULL);
static struct attribute *dbg_attrs[] = {
&dev_attr_signal.attr,
NULL
};
static struct attribute_group dma_group_attrs = {
.name = "info",
.attrs = dbg_attrs
};
static int dma_proc_show(struct seq_file *m, void *v)
{
struct dw_dma *dw = m->private;
if (!dma_readq(dw, CH_EN))
dwc_prepare_clk(dw);
seq_printf(m, "CFG: 0x%llx, CH_EN=0x%llx, INT_STATUS=0x%llx, COMM_INTSTATUS=0x%llx\n",
dma_readq(dw, CFG), dma_readq(dw, CH_EN), dma_readq(dw, INTSTATUS), dma_readq(dw, COMM_INTSTATUS));
if (!dma_readq(dw, CH_EN))
dwc_unprepare_clk(dw);
return 0;
}
static int seq_dma_open(struct inode *inode, struct file *file)
{
return single_open(file, dma_proc_show, PDE_DATA(inode));
}
static int ch_proc_show(struct seq_file *m, void *v)
{
struct dw_dma *dw = m->private;
int i;
if (!dma_readq(dw, CH_EN))
dwc_prepare_clk(dw);
for (i = 0; i < dw->nr_channels; i++) {
struct dw_dma_chan *dwc = &dw->chan[i];
seq_printf(m, "Ch%d - SAR:0x%llx DAR:0x%llx ch_status:0x%llx int_status:0x%llx hw_pos:%d intc:%d\n", i,
channel_readq(dwc, SAR), channel_readq(dwc, DAR),
channel_readq(dwc, STATUS), channel_readq(dwc, INTSTATUS),
dwc->hw_pos, dwc->interrupt_count);
}
if (!dma_readq(dw, CH_EN))
dwc_unprepare_clk(dw);
return 0;
}
static int seq_ch_open(struct inode *inode, struct file *file)
{
return single_open(file, ch_proc_show, PDE_DATA(inode));
}
static int log_proc_show(struct seq_file *m, void *v)
{
struct dw_dma *dw = m->private;
seq_printf(m, "sysDMA log is %s\n", dw->log_on ? "on" : "off");
return 0;
}
static int seq_log_open(struct inode *inode, struct file *file)
{
return single_open(file, log_proc_show, PDE_DATA(inode));
}
static ssize_t log_level_write(struct file *file, const char __user *data, size_t len, loff_t *off)
{
struct dw_dma *dw = PDE_DATA(file_inode(file));
char buf[3];
if (data && !copy_from_user(buf, data, len)) {
buf[len - 1] = '\0';
if (buf[0] == '1')
dw->log_on = 1;
else if (buf[0] == '0')
dw->log_on = 0;
else
dev_dbg(dw->dev, "Incorrect input for /proc/sysDMA/log_level\n");
}
return len;
}
static const struct proc_ops dma_proc_ops = {
.proc_open = seq_dma_open,
.proc_read = seq_read,
.proc_release = single_release,
};
static const struct proc_ops ch_proc_ops = {
.proc_open = seq_ch_open,
.proc_read = seq_read,
.proc_release = single_release,
};
static const struct proc_ops log_proc_ops = {
.proc_open = seq_log_open,
.proc_read = seq_read,
.proc_write = log_level_write,
.proc_release = single_release,
};
static int dw_dma_probe(struct platform_device *pdev)
{
int err;
struct dw_dma *dw;
struct device *dev = &pdev->dev;
struct proc_dir_entry *proc_dma_folder;
struct proc_dir_entry *proc_dma;
struct proc_dir_entry *proc_ch;
struct proc_dir_entry *proc_log;
dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
if (!dw)
return -ENOMEM;
dw->dev = dev;
err = dw_dma_resource_init(pdev, dw);
if (err) {
dev_err(dev,
"CVITEK DMA resource init error %d\n", err);
goto err_resource;
}
err = dw_dma_parse_dt(pdev, dw);
if (err) {
dev_err(dev,
"CVITEK DMA parse devicetree error %d\n", err);
goto err_dt;
}
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
dw_dma_off(dw);
err = dw_dma_channel_init(pdev, dw);
if (err) {
dev_err(dev,
"CVITEK DMA channel init error %d\n", err);
goto err_chan;
}
err = dma_async_device_register(&dw->dma);
if (err)
goto err_dma_register;
pm_runtime_put_sync_suspend(dev);
dw->clk = devm_clk_get(dw->dev, "clk_sdma_axi");
if (IS_ERR(dw->clk)) {
dev_err(dev, "CVITEK DMA get clk_dma_axi failed\n");
return PTR_ERR(dw->clk);
}
err = clk_prepare(dw->clk);
dw->clk_count = 0;
if (err)
return err;
platform_set_drvdata(pdev, dw);
if (pdev->dev.of_node) {
err = of_dma_controller_register(pdev->dev.of_node,
dw_dma_of_xlate, dw);
if (err) {
dev_err(&pdev->dev,
"could not register of_dma_controller\n");
goto err_of_dma;
}
}
if (sysfs_create_group(&pdev->dev.kobj, &dma_group_attrs))
dev_err(&pdev->dev, "Could not register attrs for dma\n");
dev_info(dev, "CVITEK DMA Controller, %d channels, probe done!\n",
dw->nr_channels);
proc_dma_folder = proc_mkdir("sysDMA", NULL);
if (!proc_dma_folder)
dev_err(&pdev->dev, "Error creating sysDMA proc folder entry\n");
proc_dma = proc_create_data("dma_status", 0664, proc_dma_folder, &dma_proc_ops, dw);
if (!proc_dma)
dev_err(&pdev->dev, "Create sysDMA status proc failed!\n");
proc_ch = proc_create_data("ch_status", 0664, proc_dma_folder, &ch_proc_ops, dw);
if (!proc_ch)
dev_err(&pdev->dev, "Create sysDMA channel proc failed!\n");
proc_log = proc_create_data("log_level", 0664, proc_dma_folder, &log_proc_ops, dw);
if (!proc_log)
dev_err(&pdev->dev, "Create sysDMA log level proc failed!\n");
dw->log_on = false;
return 0;
err_resource:
err_dt:
return err;
err_chan:
err_dma_register:
err_of_dma:
pm_runtime_disable(&pdev->dev);
return err;
}
static int __dw_dma_remove(struct dw_dma *dw)
{
struct dw_dma_chan *dwc, *_dwc;
pm_runtime_get_sync(dw->dev);
dw_dma_off(dw);
dma_async_device_unregister(&dw->dma);
free_irq(dw->irq, dw);
tasklet_kill(&dw->tasklet);
list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels,
chan.device_node) {
list_del(&dwc->chan.device_node);
dma_clear_bit(dw, CH_EN, dwc->mask);
}
pm_runtime_put_sync_suspend(dw->dev);
return 0;
}
static int dw_dma_remove(struct platform_device *pdev)
{
struct dw_dma *dw = platform_get_drvdata(pdev);
if (pdev->dev.of_node)
of_dma_controller_free(pdev->dev.of_node);
__dw_dma_remove(dw);
pm_runtime_disable(&pdev->dev);
clk_unprepare(dw->clk);
return 0;
}
static void dw_dma_shutdown(struct platform_device *pdev)
{
struct dw_dma *dw = platform_get_drvdata(pdev);
/*
* We have to call dw_dma_disable() to stop any ongoing transfer. On
* some platforms we can't do that since DMA device is powered off.
* Moreover we have no possibility to check if the platform is affected
* or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
* unconditionally. On the other hand we can't use
* pm_runtime_suspended() because runtime PM framework is not fully
* used by the driver.
*/
pm_runtime_get_sync(dw->dev);
dw_dma_off(dw);
pm_runtime_put_sync_suspend(dw->dev);
}
static const struct of_device_id dw_dma_of_id_table[] = {
{ .compatible = "snps,dmac-bm" },
{}
};
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
#ifdef CONFIG_PM_SLEEP
static int dw_dma_suspend_late(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma *dw = platform_get_drvdata(pdev);
dw_dma_off(dw);
return 0;
}
static int dw_dma_resume_early(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma *dw = platform_get_drvdata(pdev);
clk_prepare_enable(dw->clk);
dma_writeq(dw, CH_EN, 0xFF00);
dw_dma_on(dw);
return 0;
}
#else
#define dw_dma_suspend_late NULL
#define dw_dma_resume_early NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dw_dev_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_dma_suspend_late, dw_dma_resume_early)
};
static struct platform_driver dw_dma_driver = {
.probe = dw_dma_probe,
.remove = dw_dma_remove,
.shutdown = dw_dma_shutdown,
.driver = {
.name = DRV_NAME,
.pm = &dw_dev_pm_ops,
.of_match_table = of_match_ptr(dw_dma_of_id_table),
},
};
static int __init dw_dma_init(void)
{
return platform_driver_register(&dw_dma_driver);
}
subsys_initcall(dw_dma_init);
static void __exit dw_dma_exit(void)
{
platform_driver_unregister(&dw_dma_driver);
}
module_exit(dw_dma_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CVITEK DMA Controller platform driver");
MODULE_ALIAS("platform:" DRV_NAME);