mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 04:04:06 +00:00
drivers/dma: Add dw-axi-dmac-starfive driver for JH7100
This commit is contained in:
parent
4f49a36502
commit
09bc355ef1
8 changed files with 774 additions and 20 deletions
|
@ -181,6 +181,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 DW_AXI_DMAC_STARFIVE
|
||||
tristate "Synopsys DesignWare AXI DMA support for StarFive SOC"
|
||||
depends on SOC_STARFIVE_VIC7100
|
||||
help
|
||||
Enable support for Synopsys DesignWare AXI DMA controller.
|
||||
NOTE: It's for StarFive SOC.
|
||||
|
||||
config EP93XX_DMA
|
||||
bool "Cirrus Logic EP93xx DMA support"
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
|
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
|
|||
obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
|
||||
obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
|
||||
obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
|
||||
obj-$(CONFIG_DW_AXI_DMAC_STARFIVE) += dw-axi-dmac-starfive/
|
||||
obj-$(CONFIG_DW_DMAC_CORE) += dw/
|
||||
obj-$(CONFIG_DW_EDMA) += dw-edma/
|
||||
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
|
||||
|
|
2
drivers/dma/dw-axi-dmac-starfive/Makefile
Normal file
2
drivers/dma/dw-axi-dmac-starfive/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_DW_AXI_DMAC_STARFIVE) += starfive_dmaengine_memcpy.o dw-axi-dmac-starfive-misc.o
|
322
drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c
Normal file
322
drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* Copyright 2020 StarFive, Inc <samin.guo@starfivetech.com>
|
||||
*
|
||||
* DW AXI dma driver for StarFive SoC VIC7100.
|
||||
*
|
||||
* 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 in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <soc/starfive/vic7100.h>
|
||||
|
||||
#define DRIVER_NAME "dwaxidma"
|
||||
#define AXIDMA_IOC_MAGIC 'A'
|
||||
#define AXIDMA_IOCGETCHN _IO(AXIDMA_IOC_MAGIC, 0)
|
||||
#define AXIDMA_IOCCFGANDSTART _IO(AXIDMA_IOC_MAGIC, 1)
|
||||
#define AXIDMA_IOCGETSTATUS _IO(AXIDMA_IOC_MAGIC, 2)
|
||||
#define AXIDMA_IOCRELEASECHN _IO(AXIDMA_IOC_MAGIC, 3)
|
||||
|
||||
#define AXI_DMA_MAX_CHANS 20
|
||||
|
||||
#define DMA_CHN_UNUSED 0
|
||||
#define DMA_CHN_USED 1
|
||||
#define DMA_STATUS_UNFINISHED 0
|
||||
#define DMA_STATUS_FINISHED 1
|
||||
|
||||
/* for DEBUG*/
|
||||
//#define DW_DMA_CHECK_RESULTS
|
||||
//#define DW_DMA_PRINT_MEM
|
||||
//#define DW_DMA_FLUSH_DESC
|
||||
|
||||
struct axidma_chncfg {
|
||||
unsigned long src_addr; /*dma addr*/
|
||||
unsigned long dst_addr; /*dma addr*/
|
||||
unsigned long virt_src; /*mmap src addr*/
|
||||
unsigned long virt_dst; /*mmap dst addr*/
|
||||
unsigned long phys; /*desc phys addr*/
|
||||
unsigned int len; /*transport lenth*/
|
||||
int mem_fd; /*fd*/
|
||||
unsigned char chn_num; /*dma channels number*/
|
||||
unsigned char status; /*dma transport status*/
|
||||
};
|
||||
|
||||
struct axidma_chns {
|
||||
struct dma_chan *dma_chan;
|
||||
unsigned char used;
|
||||
unsigned char status;
|
||||
unsigned char reserve[2];
|
||||
};
|
||||
|
||||
struct axidma_chns channels[AXI_DMA_MAX_CHANS];
|
||||
#ifdef DW_DMA_PRINT_MEM
|
||||
void print_in_line_u64(u8 *p_name, u64 *p_buf, u32 len)
|
||||
{
|
||||
u32 i, j;
|
||||
u32 line;
|
||||
u32* ptmp;
|
||||
u32 len_tmp;
|
||||
u32 rest = len / 4;
|
||||
|
||||
printk("%s: 0x%#llx, 0x%x\n",
|
||||
p_name, dw_virt_to_phys((void *)p_buf), len);
|
||||
|
||||
if(len >= 0x1000)
|
||||
len_tmp = 0x1000 / 32; //print 128 size of memory.
|
||||
else
|
||||
len_tmp = len / 8; //print real 100% size of memory.
|
||||
|
||||
rest = len / 4; //one line print 8 u32
|
||||
|
||||
for (i = 0; i < len_tmp; i += 4, rest -= line) {
|
||||
if (!(i % 4))
|
||||
printk(KERN_CONT KERN_INFO" %#llx: ",
|
||||
dw_virt_to_phys((void *)(p_buf + i)));
|
||||
|
||||
ptmp = (u32*)(p_buf + i);
|
||||
line = (rest > 8) ? 8 : rest;
|
||||
|
||||
for (j = 0; j < line; j++)
|
||||
printk(KERN_CONT KERN_INFO "%08x ", *(ptmp + j));
|
||||
|
||||
printk(KERN_CONT KERN_INFO"\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int axidma_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/*Open: do nothing*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axidma_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* Release: do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t axidma_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
/* Write: do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_complete_func(void *status)
|
||||
{
|
||||
*(char *)status = DMA_STATUS_FINISHED;
|
||||
}
|
||||
|
||||
static long axidma_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int i, ret;
|
||||
dma_cap_mask_t mask;
|
||||
dma_cookie_t cookie;
|
||||
struct dma_device *dma_dev;
|
||||
struct axidma_chncfg chncfg;
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
|
||||
#ifdef DW_DMA_FLUSH_DESC
|
||||
void *des_chncfg = &chncfg;
|
||||
chncfg.phys = dw_virt_to_phys(des_chncfg);
|
||||
#endif
|
||||
memset(&chncfg, 0, sizeof(struct axidma_chncfg));
|
||||
|
||||
switch(cmd) {
|
||||
case AXIDMA_IOCGETCHN:
|
||||
for(i = 0; i < AXI_DMA_MAX_CHANS; i++) {
|
||||
if(DMA_CHN_UNUSED == channels[i].used)
|
||||
break;
|
||||
}
|
||||
if(AXI_DMA_MAX_CHANS == i) {
|
||||
printk("Get dma chn failed, because no idle channel\n");
|
||||
goto error;
|
||||
} else {
|
||||
channels[i].used = DMA_CHN_USED;
|
||||
channels[i].status = DMA_STATUS_UNFINISHED;
|
||||
chncfg.status = DMA_STATUS_UNFINISHED;
|
||||
chncfg.chn_num = i;
|
||||
}
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_MEMCPY, mask);
|
||||
channels[i].dma_chan = dma_request_channel(mask, NULL, NULL);
|
||||
if(!channels[i].dma_chan) {
|
||||
printk("dma request channel failed\n");
|
||||
channels[i].used = DMA_CHN_UNUSED;
|
||||
goto error;
|
||||
}
|
||||
ret = copy_to_user((void __user *)arg, &chncfg,
|
||||
sizeof(struct axidma_chncfg));
|
||||
if(ret) {
|
||||
printk("Copy to user failed\n");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case AXIDMA_IOCCFGANDSTART:
|
||||
#ifdef DW_DMA_CHECK_RESULTS
|
||||
void *src,*dst;
|
||||
#endif
|
||||
ret = copy_from_user(&chncfg, (void __user *)arg,
|
||||
sizeof(struct axidma_chncfg));
|
||||
if(ret) {
|
||||
printk("Copy from user failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if((chncfg.chn_num >= AXI_DMA_MAX_CHANS) ||
|
||||
(!channels[chncfg.chn_num].dma_chan)) {
|
||||
printk("chn_num[%d] is invalid\n", chncfg.chn_num);
|
||||
goto error;
|
||||
}
|
||||
dma_dev = channels[chncfg.chn_num].dma_chan->device;
|
||||
#ifdef DW_DMA_FLUSH_DESC
|
||||
starfive_flush_dcache(chncfg.phys,sizeof(chncfg));
|
||||
#endif
|
||||
#ifdef DW_DMA_CHECK_RESULTS
|
||||
src = dw_phys_to_virt(chncfg.src_addr);
|
||||
dst = dw_phys_to_virt(chncfg.dst_addr);
|
||||
#endif
|
||||
starfive_flush_dcache(chncfg.src_addr, chncfg.len);
|
||||
|
||||
tx = dma_dev->device_prep_dma_memcpy(
|
||||
channels[chncfg.chn_num].dma_chan,
|
||||
chncfg.dst_addr, chncfg.src_addr, chncfg.len,
|
||||
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
|
||||
if(!tx){
|
||||
printk("Failed to prepare DMA memcpy\n");
|
||||
goto error;
|
||||
}
|
||||
channels[chncfg.chn_num].status = DMA_STATUS_UNFINISHED;
|
||||
tx->callback_param = &channels[chncfg.chn_num].status;
|
||||
tx->callback = dma_complete_func;
|
||||
cookie = tx->tx_submit(tx);
|
||||
if(dma_submit_error(cookie)) {
|
||||
printk("Failed to dma tx_submit\n");
|
||||
goto error;
|
||||
}
|
||||
dma_async_issue_pending(channels[chncfg.chn_num].dma_chan);
|
||||
/*flush dcache*/
|
||||
starfive_flush_dcache(chncfg.dst_addr, chncfg.len);
|
||||
#ifdef DW_DMA_PRINT_MEM
|
||||
print_in_line_u64((u8 *)"src", (u64 *)src, chncfg.len);
|
||||
print_in_line_u64((u8 *)"dst", (u64 *)dst, chncfg.len);
|
||||
#endif
|
||||
#ifdef DW_DMA_CHECK_RESULTS
|
||||
if(memcmp(src, dst, chncfg.len))
|
||||
printk("check data faild.\n");
|
||||
else
|
||||
printk("check data ok.\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case AXIDMA_IOCGETSTATUS:
|
||||
ret = copy_from_user(&chncfg, (void __user *)arg,
|
||||
sizeof(struct axidma_chncfg));
|
||||
if(ret) {
|
||||
printk("Copy from user failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(chncfg.chn_num >= AXI_DMA_MAX_CHANS) {
|
||||
printk("chn_num[%d] is invalid\n", chncfg.chn_num);
|
||||
goto error;
|
||||
}
|
||||
|
||||
chncfg.status = channels[chncfg.chn_num].status;
|
||||
|
||||
ret = copy_to_user((void __user *)arg, &chncfg,
|
||||
sizeof(struct axidma_chncfg));
|
||||
if(ret) {
|
||||
printk("Copy to user failed\n");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case AXIDMA_IOCRELEASECHN:
|
||||
ret = copy_from_user(&chncfg, (void __user *)arg,
|
||||
sizeof(struct axidma_chncfg));
|
||||
if(ret) {
|
||||
printk("Copy from user failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if((chncfg.chn_num >= AXI_DMA_MAX_CHANS) ||
|
||||
(!channels[chncfg.chn_num].dma_chan)) {
|
||||
printk("chn_num[%d] is invalid\n", chncfg.chn_num);
|
||||
goto error;
|
||||
}
|
||||
|
||||
dma_release_channel(channels[chncfg.chn_num].dma_chan);
|
||||
channels[chncfg.chn_num].used = DMA_CHN_UNUSED;
|
||||
channels[chncfg.chn_num].status = DMA_STATUS_UNFINISHED;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("Don't support cmd [%d]\n", cmd);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel Interfaces
|
||||
*/
|
||||
static struct file_operations axidma_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = axidma_write,
|
||||
.unlocked_ioctl = axidma_unlocked_ioctl,
|
||||
.open = axidma_open,
|
||||
.release = axidma_release,
|
||||
};
|
||||
|
||||
static struct miscdevice axidma_miscdev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = DRIVER_NAME,
|
||||
.fops = &axidma_fops,
|
||||
};
|
||||
|
||||
static int __init axidma_init(void)
|
||||
{
|
||||
int ret = misc_register(&axidma_miscdev);
|
||||
if(ret) {
|
||||
printk (KERN_ERR "cannot register miscdev (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&channels, 0, sizeof(channels));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit axidma_exit(void)
|
||||
{
|
||||
misc_deregister(&axidma_miscdev);
|
||||
}
|
||||
|
||||
module_init(axidma_init);
|
||||
module_exit(axidma_exit);
|
||||
|
||||
MODULE_AUTHOR("samin.guo");
|
||||
MODULE_DESCRIPTION("DW Axi Dmac Driver");
|
||||
MODULE_LICENSE("GPL");
|
287
drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c
Normal file
287
drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Copyright 2020 StarFive, Inc <samin.guo@starfivetech.com>
|
||||
*
|
||||
* API for dma mem2mem.
|
||||
*
|
||||
* 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 in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <soc/starfive/vic7100.h>
|
||||
|
||||
static volatile int dma_finished = 0;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(wq);
|
||||
|
||||
u64 dw_virt_to_phys(void *vaddr)
|
||||
{
|
||||
u64 pfn_offset = ((u64)vaddr) & 0xfff;
|
||||
|
||||
return _dw_virt_to_phys((u64 *)vaddr) + pfn_offset;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_virt_to_phys);
|
||||
|
||||
void *dw_phys_to_virt(u64 phys)
|
||||
{
|
||||
u64 pfn_offset = phys & 0xfff;
|
||||
|
||||
return (void *)(_dw_phys_to_virt(phys) + pfn_offset);
|
||||
}
|
||||
EXPORT_SYMBOL(dw_phys_to_virt);
|
||||
|
||||
static void tx_callback(void *dma_async_param)
|
||||
{
|
||||
dma_finished = 1;
|
||||
wake_up_interruptible(&wq);
|
||||
}
|
||||
|
||||
static int _dma_async_alloc_buf(struct device *dma_dev,
|
||||
void **src, void **dst, size_t size,
|
||||
dma_addr_t *src_dma, dma_addr_t *dst_dma)
|
||||
{
|
||||
*src = dma_alloc_coherent(dma_dev, size, src_dma, GFP_KERNEL);
|
||||
if(!(*src)) {
|
||||
DMA_DEBUG("src alloc err.\n");
|
||||
goto _FAILED_ALLOC_SRC;
|
||||
}
|
||||
|
||||
*dst = dma_alloc_coherent(dma_dev, size, dst_dma, GFP_KERNEL);
|
||||
if(!(*dst)) {
|
||||
DMA_DEBUG("dst alloc err.\n");
|
||||
goto _FAILED_ALLOC_DST;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
_FAILED_ALLOC_DST:
|
||||
dma_free_coherent(dma_dev, size, *src, *src_dma);
|
||||
|
||||
_FAILED_ALLOC_SRC:
|
||||
dma_free_coherent(dma_dev, size, *dst, *dst_dma);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _dma_async_prebuf(void *src, void *dst, size_t size)
|
||||
{
|
||||
memset((u8 *)src, 0xff, size);
|
||||
memset((u8 *)dst, 0x00, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dma_async_check_data(void *src, void *dst, size_t size)
|
||||
{
|
||||
return memcmp(src, dst, size);
|
||||
}
|
||||
|
||||
static void _dma_async_release(struct dma_chan *chan)
|
||||
{
|
||||
dma_release_channel(chan);
|
||||
}
|
||||
|
||||
static struct dma_chan *_dma_get_channel(enum dma_transaction_type tx_type)
|
||||
{
|
||||
dma_cap_mask_t dma_mask;
|
||||
|
||||
dma_cap_zero(dma_mask);
|
||||
dma_cap_set(tx_type, dma_mask);
|
||||
|
||||
return dma_request_channel(dma_mask, NULL, NULL);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *_dma_async_get_desc(
|
||||
struct dma_chan *chan,
|
||||
dma_addr_t src_dma, dma_addr_t dst_dma,
|
||||
size_t size)
|
||||
{
|
||||
dma_finished = 0;
|
||||
return dmaengine_prep_dma_memcpy(chan, dst_dma, src_dma, size,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
}
|
||||
|
||||
static void _dma_async_do_start(struct dma_async_tx_descriptor *desc,
|
||||
struct dma_chan *chan)
|
||||
{
|
||||
dma_cookie_t dma_cookie = dmaengine_submit(desc);
|
||||
if (dma_submit_error(dma_cookie))
|
||||
DMA_DEBUG("Failed to do DMA tx_submit\n");
|
||||
|
||||
dma_async_issue_pending(chan);
|
||||
wait_event_interruptible(wq, dma_finished);
|
||||
}
|
||||
|
||||
int dw_dma_async_do_memcpy(void *src, void *dst, size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct device *dma_dev;
|
||||
struct dma_chan *chan;
|
||||
dma_addr_t src_dma, dst_dma;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
const struct iommu_ops *iommu;
|
||||
u64 dma_addr = 0, dma_size = 0;
|
||||
|
||||
dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL);
|
||||
if(!dma_dev){
|
||||
dev_err(dma_dev, "kmalloc error.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_dev->bus = NULL;
|
||||
dma_dev->coherent_dma_mask = 0xffffffff;
|
||||
|
||||
iort_dma_setup(dma_dev, &dma_addr, &dma_size);
|
||||
iommu = iort_iommu_configure_id(dma_dev, NULL);
|
||||
if (PTR_ERR(iommu) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true);
|
||||
|
||||
if(_dma_async_alloc_buf(dma_dev, &src, &dst, size, &src_dma, &dst_dma)) {
|
||||
dev_err(dma_dev, "Err alloc.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
DMA_DEBUG("src=%#llx, dst=%#llx\n", (u64)src, (u64)dst);
|
||||
DMA_DEBUG("dma_src=%#x dma_dst=%#x\n", (u32)src_dma, (u32)dst_dma);
|
||||
|
||||
_dma_async_prebuf(src, dst, size);
|
||||
|
||||
chan = _dma_get_channel(DMA_MEMCPY);
|
||||
if(!chan ){
|
||||
DMA_PRINTK("Err get chan.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
DMA_DEBUG("get chan ok.\n");
|
||||
|
||||
desc = _dma_async_get_desc(chan, src_dma, dst_dma, size);
|
||||
if(!desc){
|
||||
DMA_PRINTK("Err get desc.\n");
|
||||
dma_release_channel(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
DMA_DEBUG("get desc ok.\n");
|
||||
|
||||
desc->callback = tx_callback;
|
||||
|
||||
starfive_flush_dcache(src_dma, size);
|
||||
starfive_flush_dcache(dst_dma, size);
|
||||
|
||||
_dma_async_do_start(desc, chan);
|
||||
_dma_async_release(chan);
|
||||
|
||||
ret = _dma_async_check_data(src, dst, size);
|
||||
|
||||
dma_free_coherent(dma_dev, size, src, src_dma);
|
||||
dma_free_coherent(dma_dev, size, dst, dst_dma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_dma_async_do_memcpy);
|
||||
|
||||
/*
|
||||
* phys addr for dma.
|
||||
*/
|
||||
int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
struct device *dma_dev;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
const struct iommu_ops *iommu;
|
||||
u64 dma_addr = 0, dma_size = 0;
|
||||
|
||||
dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL);
|
||||
if(!dma_dev){
|
||||
DMA_PRINTK("kmalloc error.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_dev->bus = NULL;
|
||||
dma_dev->coherent_dma_mask = 0xffffffff;
|
||||
|
||||
iort_dma_setup(dma_dev, &dma_addr, &dma_size);
|
||||
iommu = iort_iommu_configure_id(dma_dev, NULL);
|
||||
if (PTR_ERR(iommu) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true);
|
||||
|
||||
chan = _dma_get_channel(DMA_MEMCPY);
|
||||
if(!chan){
|
||||
DMA_PRINTK("Error get chan.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
DMA_DEBUG("get chan ok.\n");
|
||||
|
||||
DMA_DEBUG("src_dma=%#llx, dst_dma=%#llx \n", src_dma, dst_dma);
|
||||
desc = _dma_async_get_desc(chan, src_dma, dst_dma, size);
|
||||
if(!desc){
|
||||
DMA_PRINTK("Error get desc.\n");
|
||||
dma_release_channel(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
DMA_DEBUG("get desc ok.\n");
|
||||
|
||||
desc->callback = tx_callback;
|
||||
|
||||
starfive_flush_dcache(src_dma, size);
|
||||
starfive_flush_dcache(dst_dma, size);
|
||||
|
||||
_dma_async_do_start(desc, chan);
|
||||
_dma_async_release(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_dma_memcpy_raw);
|
||||
|
||||
/*
|
||||
*virtl addr for cpu.
|
||||
*/
|
||||
int dw_dma_memcpy(void *src, void *dst, size_t size)
|
||||
{
|
||||
dma_addr_t src_dma, dst_dma;
|
||||
|
||||
src_dma = dw_virt_to_phys(src);
|
||||
dst_dma = dw_virt_to_phys(dst);
|
||||
|
||||
dw_dma_memcpy_raw(src_dma, dst_dma, size);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_dma_memcpy);
|
||||
|
||||
int dw_dma_mem2mem_test(void)
|
||||
{
|
||||
int ret;
|
||||
void *src = NULL;
|
||||
void *dst = NULL;
|
||||
size_t size = 256;
|
||||
|
||||
ret = dw_dma_async_do_memcpy(src, dst, size);
|
||||
if(ret){
|
||||
DMA_PRINTK("memcpy failed.\n");
|
||||
} else {
|
||||
DMA_PRINTK("memcpy ok.\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -32,6 +32,8 @@
|
|||
#include "../dmaengine.h"
|
||||
#include "../virt-dma.h"
|
||||
|
||||
#include <soc/starfive/vic7100.h>
|
||||
|
||||
/*
|
||||
* The set of bus widths supported by the DMA controller. DW AXI DMAC supports
|
||||
* master data bus width up to 512 bits (for both AXI master interfaces), but
|
||||
|
@ -148,24 +150,43 @@ static inline u32 axi_chan_irq_read(struct axi_dma_chan *chan)
|
|||
return axi_chan_ioread32(chan, CH_INTSTATUS);
|
||||
}
|
||||
|
||||
static inline bool axi_chan_get_nr8(struct axi_dma_chan *chan)
|
||||
{
|
||||
return chan->chip->flag->nr_chan_8;
|
||||
}
|
||||
|
||||
static inline void axi_chan_disable(struct axi_dma_chan *chan)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
if(axi_chan_get_nr8(chan)) {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN_8);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT_8);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT_8;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN_8, val);
|
||||
} else {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void axi_chan_enable(struct axi_dma_chan *chan)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT |
|
||||
BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
if(axi_chan_get_nr8(chan)) {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN_8);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT_8 |
|
||||
BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT_8;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN_8, val);
|
||||
} else {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT |
|
||||
BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan)
|
||||
|
@ -335,6 +356,7 @@ static void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set)
|
|||
static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
|
||||
struct axi_dma_desc *first)
|
||||
{
|
||||
struct axi_dma_desc *desc;
|
||||
u32 priority = chan->chip->dw->hdata->priority[chan->id];
|
||||
u32 reg, irq_mask;
|
||||
u8 lms = 0; /* Select AXI0 master for LLI fetching */
|
||||
|
@ -384,6 +406,23 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
|
|||
irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
|
||||
axi_chan_irq_set(chan, irq_mask);
|
||||
|
||||
/*flush all the desc */
|
||||
#ifdef CONFIG_SOC_STARFIVE_VIC7100
|
||||
if(chan->chip->flag->need_flush) {
|
||||
/*flush fisrt desc*/
|
||||
starfive_flush_dcache(first->vd.tx.phys, sizeof(*first));
|
||||
|
||||
list_for_each_entry(desc, &first->xfer_list, xfer_list) {
|
||||
starfive_flush_dcache(desc->vd.tx.phys, sizeof(*desc));
|
||||
|
||||
dev_dbg(chan->chip->dev,
|
||||
"sar:%#llx dar:%#llx llp:%#llx ctl:0x%x:%08x\n",
|
||||
desc->lli.sar, desc->lli.dar, desc->lli.llp,
|
||||
desc->lli.ctl_hi, desc->lli.ctl_lo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
axi_chan_enable(chan);
|
||||
}
|
||||
|
||||
|
@ -1070,8 +1109,10 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
|
|||
|
||||
if (status & DWAXIDMAC_IRQ_ALL_ERR)
|
||||
axi_chan_handle_err(chan, status);
|
||||
else if (status & DWAXIDMAC_IRQ_DMA_TRF)
|
||||
else if (status & DWAXIDMAC_IRQ_DMA_TRF) {
|
||||
axi_chan_block_xfer_complete(chan);
|
||||
dev_dbg(chip->dev, "axi_chan_block_xfer_complete.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-enable interrupts */
|
||||
|
@ -1126,10 +1167,17 @@ static int dma_chan_pause(struct dma_chan *dchan)
|
|||
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT |
|
||||
BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
if(axi_chan_get_nr8(chan)){
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHSUSP_8);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT_8 |
|
||||
BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT_8;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHSUSP_8, val);
|
||||
} else {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHSUSP);
|
||||
val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT |
|
||||
BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT;
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHSUSP, val);
|
||||
}
|
||||
|
||||
do {
|
||||
if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED)
|
||||
|
@ -1152,11 +1200,17 @@ static inline void axi_chan_resume(struct axi_dma_chan *chan)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT);
|
||||
val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT);
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
|
||||
|
||||
if(axi_chan_get_nr8(chan)){
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHSUSP_8);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT_8);
|
||||
val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT_8);
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHSUSP_8, val);
|
||||
} else {
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHSUSP);
|
||||
val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT);
|
||||
val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT);
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHSUSP, val);
|
||||
}
|
||||
chan->is_paused = false;
|
||||
}
|
||||
|
||||
|
@ -1248,6 +1302,13 @@ static int parse_device_properties(struct axi_dma_chip *chip)
|
|||
|
||||
chip->dw->hdata->nr_channels = tmp;
|
||||
|
||||
if(chip->dw->hdata->nr_channels > 8){
|
||||
chip->flag->nr_chan_8 = true;
|
||||
#ifdef CONFIG_SOC_STARFIVE_VIC7100
|
||||
chip->flag->need_flush = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(dev, "snps,dma-masters", &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1309,6 +1370,7 @@ static int dw_probe(struct platform_device *pdev)
|
|||
struct resource *mem;
|
||||
struct dw_axi_dma *dw;
|
||||
struct dw_axi_dma_hcfg *hdata;
|
||||
struct dw_dma_flag *flag;
|
||||
u32 i;
|
||||
int ret;
|
||||
|
||||
|
@ -1324,9 +1386,14 @@ static int dw_probe(struct platform_device *pdev)
|
|||
if (!hdata)
|
||||
return -ENOMEM;
|
||||
|
||||
flag = devm_kzalloc(&pdev->dev, sizeof(*flag), GFP_KERNEL);
|
||||
if (!flag)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dw = dw;
|
||||
chip->dev = &pdev->dev;
|
||||
chip->dw->hdata = hdata;
|
||||
chip->flag = flag;
|
||||
|
||||
chip->irq = platform_get_irq(pdev, 0);
|
||||
if (chip->irq < 0)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Synopsys DesignWare AXI DMA Controller driver.
|
||||
*
|
||||
* Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
* Samin.guo <samin.guo@starfivetech.com>
|
||||
* add support for (channels > 8). 2020.
|
||||
*/
|
||||
|
||||
#ifndef _AXI_DMA_PLATFORM_H
|
||||
|
@ -18,10 +20,17 @@
|
|||
|
||||
#include "../virt-dma.h"
|
||||
|
||||
#define DMAC_MAX_CHANNELS 8
|
||||
#define DMAC_MAX_CHANNELS 16
|
||||
#define DMAC_MAX_MASTERS 2
|
||||
#define DMAC_MAX_BLK_SIZE 0x200000
|
||||
|
||||
struct dw_dma_flag {
|
||||
bool nr_chan_8;
|
||||
#ifdef CONFIG_SOC_STARFIVE_VIC7100
|
||||
bool need_flush;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct dw_axi_dma_hcfg {
|
||||
u32 nr_channels;
|
||||
u32 nr_masters;
|
||||
|
@ -68,6 +77,7 @@ struct axi_dma_chip {
|
|||
struct clk *core_clk;
|
||||
struct clk *cfgr_clk;
|
||||
struct dw_axi_dma *dw;
|
||||
struct dw_dma_flag *flag;
|
||||
};
|
||||
|
||||
/* LLI == Linked List Item */
|
||||
|
@ -139,6 +149,15 @@ static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
|
|||
#define DMAC_CHEN 0x018 /* R/W DMAC Channel Enable */
|
||||
#define DMAC_CHEN_L 0x018 /* R/W DMAC Channel Enable 00-31 */
|
||||
#define DMAC_CHEN_H 0x01C /* R/W DMAC Channel Enable 32-63 */
|
||||
#define DMAC_CHSUSP 0x018 /* R/W DMAC Channel suspend */
|
||||
#define DMAC_CHABORT 0x018 /* R/W DMAC Channel Abort */
|
||||
|
||||
#define DMAC_CHEN_8 0x018 /* R/W DMAC Channel Enable */
|
||||
#define DMAC_CHEN_L_8 0x018 /* R/W DMAC Channel Enable */
|
||||
#define DMAC_CHEN_H_8 0x01C /* R/W DMAC Channel Enable */
|
||||
#define DMAC_CHSUSP_8 0x020 /* R/W DMAC Channel Suspend */
|
||||
#define DMAC_CHABORT_8 0x028 /* R/W DMAC Channel Abort */
|
||||
|
||||
#define DMAC_INTSTATUS 0x030 /* R DMAC Interrupt Status */
|
||||
#define DMAC_COMMON_INTCLEAR 0x038 /* W DMAC Interrupt Clear */
|
||||
#define DMAC_COMMON_INTSTATUS_ENA 0x040 /* R DMAC Interrupt Status Enable */
|
||||
|
@ -199,6 +218,19 @@ static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
|
|||
#define DMAC_CHAN_SUSP_SHIFT 16
|
||||
#define DMAC_CHAN_SUSP_WE_SHIFT 24
|
||||
|
||||
#define DMAC_CHAN_ABORT_SHIFT 32
|
||||
#define DMAC_CHAN_ABORT_WE_SHIFT 40
|
||||
|
||||
|
||||
#define DMAC_CHAN_EN_SHIFT_8 0
|
||||
#define DMAC_CHAN_EN_WE_SHIFT_8 16
|
||||
|
||||
#define DMAC_CHAN_SUSP_SHIFT_8 0
|
||||
#define DMAC_CHAN_SUSP_WE_SHIFT_8 16
|
||||
|
||||
#define DMAC_CHAN_ABORT_SHIFT_8 0
|
||||
#define DMAC_CHAN_ABORT_WE_SHIFT_8 16
|
||||
|
||||
/* CH_CTL_H */
|
||||
#define CH_CTL_H_ARLEN_EN BIT(6)
|
||||
#define CH_CTL_H_ARLEN_POS 7
|
||||
|
@ -255,7 +287,7 @@ enum {
|
|||
#define CH_CTL_L_SRC_MAST BIT(0)
|
||||
|
||||
/* CH_CFG_H */
|
||||
#define CH_CFG_H_PRIORITY_POS 17
|
||||
#define CH_CFG_H_PRIORITY_POS 15
|
||||
#define CH_CFG_H_HS_SEL_DST_POS 4
|
||||
#define CH_CFG_H_HS_SEL_SRC_POS 3
|
||||
enum {
|
||||
|
|
36
include/soc/starfive/vic7100.h
Normal file
36
include/soc/starfive/vic7100.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef STARFIVE_VIC7100_H
|
||||
#define STARFIVE_VIC7100_H
|
||||
#include <asm/io.h>
|
||||
#include <soc/sifive/sifive_l2_cache.h>
|
||||
|
||||
/*cache.c*/
|
||||
#define starfive_flush_dcache(start, len) \
|
||||
sifive_l2_flush64_range(start, len)
|
||||
|
||||
/*dma*/
|
||||
#define CONFIG_DW_DEBUG
|
||||
|
||||
#define DMA_PRINTK(fmt,...) \
|
||||
printk("[DW_DMA] %s():%d \n" fmt, __func__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#ifdef CONFIG_DW_DEBUG
|
||||
#define DMA_DEBUG(fmt,...) \
|
||||
printk("[DW_DMA_DEBUG] %s():%d \n" fmt, __func__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DMA_BEBUG(fmt,...)
|
||||
#endif
|
||||
|
||||
#define _dw_virt_to_phys(vaddr) (pfn_to_phys(virt_to_pfn(vaddr)))
|
||||
#define _dw_phys_to_virt(paddr) (page_to_virt(phys_to_page(paddr)))
|
||||
|
||||
void *dw_phys_to_virt(u64 phys);
|
||||
u64 dw_virt_to_phys(void *vaddr);
|
||||
|
||||
int dw_dma_async_do_memcpy(void *src, void *dst, size_t size);
|
||||
int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size);
|
||||
int dw_dma_memcpy(void *src, void *dst, size_t size);
|
||||
|
||||
int dw_dma_mem2mem_arry(void);
|
||||
int dw_dma_mem2mem_test(void);
|
||||
|
||||
#endif /*STARFIVE_VIC7100_H*/
|
Loading…
Add table
Reference in a new issue