/*
* TI EDMA DMA engine driver
*
* Copyright 2012 Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/platform_data/edma.h>
#include "dmaengine.h"
#include "virt-dma.h"
/*
* This will go away when the private EDMA API is folded
* into this driver and the platform device(s) are
* instantiated in the arch code. We can only get away
* with this simplification because DA8XX may not be built
* in the same kernel image with other DaVinci parts. This
* avoids having to sprinkle dmaengine driver platform devices
* and data throughout all the existing board files.
*/
#ifdef CONFIG_ARCH_DAVINCI_DA8XX
#define EDMA_CTLRS 2
#define EDMA_CHANS 32
#else
#define EDMA_CTLRS 1
#define EDMA_CHANS 64
#endif /* CONFIG_ARCH_DAVINCI_DA8XX */
/*
* Max of 20 segments per channel to conserve PaRAM slots
* Also note that MAX_NR_SG should be atleast the no.of periods
* that are required for ASoC, otherwise DMA prep calls will
* fail. Today davinci-pcm is the only user of this driver and
* requires atleast 17 slots, so we setup the default to 20.
*/
#define MAX_NR_SG 20
#define EDMA_MAX_SLOTS MAX_NR_SG
#define EDMA_DESCRIPTORS 16
struct edma_pset {
u32 len;
dma_addr_t addr;
struct edmacc_param param;
};
struct edma_desc {
struct virt_dma_desc vdesc;
struct list_head node;
enum dma_transfer_direction direction;
int cyclic;
int absync;
int pset_nr;
struct edma_chan *echan;
int processed;
/*
* The following 4 elements are used for residue accounting.
*
* - processed_stat: the number of SG elements we have traversed
* so far to cover accounting. This is updated directly to processed
* during edma_callback and is always <= processed, because processed
* refers to the number of pending transfer (programmed to EDMA
* controller), where as processed_stat tracks number of transfers
* accounted for so far.
*
* - residue: The amount of bytes we have left to transfer for this desc
*
* - residue_stat: The residue in bytes of data we have covered
* so far for accounting. This is updated directly to residue
* during callbacks to keep it current.
*
* - sg_len: Tracks the length of the current intermediate transfer,
* this is required to update the residue during intermediate transfer
* completion callback.
*/
int processed_stat;
u32 sg_len;
u32 residue;
u32 residue_stat;
struct edma_pset pset[0];
};
struct edma_cc;
struct edma_chan {
struct virt_dma_chan vchan;
struct list_head node;
struct edma_desc *edesc;
struct edma_cc *ecc;
int ch_num;
bool alloced;
int slot[EDMA_MAX_SLOTS];
int missed;
struct dma_slave_config cfg;
};
struct edma_cc {
int ctlr;
struct dma_device dma_slave;
struct edma_chan slave_chans[EDMA_CHANS];
int num_slave_chans;
int dummy_slot;
};
static inline struct edma_cc *to_edma_cc(struct dma_device *d)
{
return container_of(d, struct edma_cc, dma_slave);
}
static inline struct edma_chan *to_edma_chan(struct dma_chan *c)
{
return container_of(c, struct edma_chan, vchan.chan);
}
static inline struct edma_desc
*to_edma_desc(struct dma_async_tx_descriptor *tx)
{
return container_of(tx, struct edma_desc, vdesc.tx);
}
static void edma_desc_free(struct virt_dma_desc *vdesc)
{
kfree(container_of(vdesc, struct edma_desc, vdesc));
}
/* Dispatch a queued descriptor to the controller (caller holds lock) */
static void edma_execute(struct edma_chan *echan)
{
struct virt_dma_desc *vdesc;
struct edma_desc *edesc;
struct device *dev = echan->vchan.chan.device->dev;
int i, j, left, nslots;
/* If either we processed all psets or we're still not started */
if (!echan->edesc ||
echan->edesc->pset_nr == echan->edesc->processed) {
/* Get next vdesc */
vdesc = vchan_next_desc(&echan->vchan);
if (!vdesc) {
echan->edesc = NULL;
return;
}
list_del(&vdesc->node);
echan->edesc = to_edma_desc(&vdesc->tx);
}
edesc = echan->edesc;
/* Find out how many left */
left = edesc->pset_nr - edesc->processed;
nslots = min(MAX_NR_SG, left);
edesc->sg_len = 0;
/* Write descriptor PaRAM set(s) */
for (i = 0; i < nslots; i++) {
j = i + edesc->processed;
edma_write_slot(echan->slot[i], &edesc->pset[j].param);
edesc->sg_len += edesc->pset[j].len;
dev_vdbg(echan->vchan.chan.device->dev,
"\n pset[%d]:\n"
" chnum\t%d\n"
" slot\t%d\n"
" opt\t%08x\n"
" src\t%08x\n"
" dst\t%08x\n"
" abcnt\t%08x\n"
" ccnt\t%08x\n"
" bidx\t%08x\n"
" cidx\t%08x\n"
" lkrld\t%08x\n",
j, echan->ch_num, echan->slot[i],
edesc->pset[j].param.opt,
edesc->pset[j].param.src,
edesc->pset[j].param.dst,
edesc->pset[j].param.a_b_cnt,
edesc->pset[j].param.ccnt,
edesc->pset[j].param.src_dst_bidx,
edesc->pset[j].param.src_dst_cidx,
edesc->pset[j].param.link_bcntrld);
/* Link to the previous slot if not the last set */
if (i != (nslots - 1))
edma_link(echan->slot[i], echan->slot[i+1]);
}
edesc->processed += nslots;
/*
* If this is either the last set in a set of SG-list transactions
* then setup a link to the dummy slot, this results in all future
* events being absorbed and that's OK because we're done
*/
if (edesc->processed == edesc->pset_nr) {
if (edesc->cyclic)
edma_link(echan->slot[nslots-1], echan->slot[1]);
else
edma_link(echan->slot[nslots-1],
echan->ecc->dummy_slot);
}
if (edesc->processed <= MAX_NR_SG) {
dev_dbg(dev, "first transfer starting on channel %d\n",
echan->ch_num);
edma_start(echan->ch_num);
} else {
dev_dbg(dev, "chan: %d: completed %d elements, resuming\n",
echan->ch_num, edesc->processed);
edma_resume(echan->ch_num);
}
/*
* This happens due to setup times between intermediate transfers
* in long SG lists which have to be broken up into transfers of
* MAX_NR_SG
*/
if (echan->missed) {
dev_dbg(dev, "missed event on channel %d\n", echan->ch_num);
edma_clean_channel(echan->ch_num);
edma_stop(echan->ch_num);
edma_start(echan->ch_num);
edma_trigger_channel(echan->ch_num);
echan->missed = 0;
}
}
static int edma_terminate_all(struct edma_chan *echan)
{
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&echan->vchan.lock, flags);
/*
* Stop DMA activity: we assume the callback will not be called
* after edma_dma() returns (even if it does, it will see
* echan->edesc is NULL and exit.)
*/
if (echan->edesc) {
int cyclic = echan->edesc->cyclic;
echan->edesc = NULL;
edma_stop(echan->ch_num);
/* Move the cyclic channel back to default queue */
if (cyclic)
edma_assign_channel_eventq(echan->ch_num,
EVENTQ_DEFAULT);
}
vchan_get_all_descriptors(&echan->vchan, &head);
spin_unlock_irqrestore(&echan->vchan.lock, flags);
vchan_dma_desc_free_list(&echan->vchan, &head);
return 0;
}
static int edma_slave_config(struct edma_chan *echan,
struct dma_slave_config *cfg)
{
if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
return -EINVAL;
memcpy(&echan->cfg, cfg, sizeof(echan->cfg));
return 0;
}
static int edma_dma_pause(struct edma_chan *echan)
{
/* Pause/Resume only allowed with cyclic mode */
if (!echan->edesc || !echan->edesc->cyclic)
return -EINVAL;
edma_pause(e
周楷雯
- 粉丝: 98
- 资源: 1万+
最新资源
- 风力发电MPPT并网模型的策略模块封装及步长选择策略:变步长与固定步长两种策略对比,风力发电mppt并网模型,策略模块已mask封装,可以选定步长和变步长2种策略 ,核心关键词:风力发电; MPP
- 基于51单片机的Proteus仿真:人数检测显示与报警系统实战应用,基于51单片机的人数检测显示及报警检测Proteus仿真 ,基于51单片机;人数检测显示;报警检测;Proteus仿真,"基于51单
- 基于STM32F系列开发的高效稳定企业级变频器方案-原理图与硬件设计说明大全,基于STM32开发变频器-企业级成熟量产方案 包含:变频控制板-原理图、PCB 变频逆变器功率板-原理图 PCB 控制板
- 基于事件触发模型的倒立摆控制仿真实验-Simulink模型与详细参考文献,【有参考文献】事件触发模型 可实现倒立摆控制仿真实验 simulink模型可直接运行 含详细参考文献 ,关键词:事件触发模
- 智能小车路径规划算法:融合RRT与Dubins及A*方法的创新路径规划策略,智能小车路径规划 算法:RRT与Dubins相结合的方法,混合A*与Dubins相结合的方法 实现智能小车最短路径规划
- 基于FOC、SMO与PLL融合技术的Simlink仿真模型研究,FOC+SMO+PLL的Simlink仿真模型 ,关键词:FOC(Field Oriented Control);SMO(Smooth
- 三相异步电机矢量控制仿真模型的构建与性能分析,三相异步电机矢量控制仿真模型 ,核心关键词:三相异步电机;矢量控制;仿真模型;分号分隔的关键词为:三相异步电机;矢量控制技术;仿真模型构建 ,三相异步电
- 模拟IC设计学习与实战:探索知名大厂DCDC电路,高转换效率标准单元库器件与Cadence环境下的原理图实战应用,模拟IC模拟IC设计,集成电路,知名大厂的逆向DCDC电路(buck-boost功能)
- Boost电路电压闭环仿真研究:包含PI控制与零极点补偿器的模型及其性能分析,boost电路电压闭环仿真 有pi控制和零极点补偿器两种 仿真误差0.00705,仿真波形如图二所示 所搭建的模型输入电压
- TCR+FC型SVC无功补偿双封装Simulink仿真模型详解与实例资料集锦,TCR+FC型svc无功补偿simulink仿真模型,一共两个仿真,如下图所示,两个其实大致内容差不多,只是封装不同,有详
- COMSOL模拟动水条件下裂隙注浆扩散过程:粘度时变影响研究,COMSOL模拟动水条件联系裂隙注浆扩散,考虑粘度时变 ,核心关键词:COMSOL模拟; 动水条件; 裂隙注浆扩散; 粘度时变; 注浆扩散
- 分布式电源接入对配电网的潮流影响分析:Matlab程序模拟的探索与探索 ,分布式电源接入对配电网的影响(matlab程序) 分布式电源的接入使得配电系统从放射状无源网络变为分布有中小型电源的有源网络
- XC7V2000T与TMS320C6678设计文件集:原理图、PCB等齐全,验证通过,直接生产使用,XC7V2000T+TMS320C6678设计文件,包含原理图,PCB等文件,已验证,可直接生产
- 基于STM32G431主控的最新磁链观测器优化方案:无感foc浮点运算,弦波pmsm无刷电机控制源代码集,卓越效果无库文件原理图解,磁链观测器,无感foc ,弦波 pmsm 无刷电机控制方案 最新优
- 基于Matlab的无线充电仿真:LCC谐振器与不同拓扑的磁耦合谐振无线电能传输系统解析与建模,无线充电仿真 simulink 磁耦合谐振 无线电能传输 MCR WPT lcc ss llc拓扑补偿
- 直流无刷电机:高效磨头加工利器,直径38mm,转速高达25000rpm,功率达200W,直流无刷电机,直径38mm,径向长23.8mm,转速25000rpm,功率200W,可用于磨头加工 ,核心关键
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈