From 42066f774425afb196dc0f8f1ad40f450da34115 Mon Sep 17 00:00:00 2001 From: Juan Castillo Date: Sun, 21 Feb 2016 20:29:01 +0000 Subject: [PATCH] Use DMA Engine API for DMA transactions From Linux branch 4.1, TI has removed the EDMA APIs in favour of the DMA Engine framework. This patch updates the Logi kernel module to program DMA transactions using the DMA Engine API. LOGI_USE_DMAENGINE can be used at build time to select the DMA Engine API or the EDMA API for backwards compatibility. All DMA related functions have been moved to a separate file. Tested with: https://github.com/beagleboard/linux/tree/4.1 --- beaglebone-black/common/drvr.h | 15 +- beaglebone-black/common/logi_dma.c | 194 ++++++++++++++++++++++++++ beaglebone-black/common/main_dma.c | 108 ++------------ beaglebone-black/logibone_r1/Makefile | 2 +- beaglebone-black/logibone_r1/config.h | 1 + 5 files changed, 224 insertions(+), 96 deletions(-) create mode 100644 beaglebone-black/common/logi_dma.c diff --git a/beaglebone-black/common/drvr.h b/beaglebone-black/common/drvr.h index 5b93b1d..17656f7 100644 --- a/beaglebone-black/common/drvr.h +++ b/beaglebone-black/common/drvr.h @@ -5,12 +5,19 @@ #define DBG_LOG(fmt, args...) printk(KERN_INFO DEVICE_NAME ": " fmt, ## args) +struct dma_chan; enum drvr_type { prog, mem }; +struct drvr_dma { + void * buf; + int dma_chan; + struct dma_chan * chan; +}; + struct drvr_prog { struct i2c_client * i2c_io; }; @@ -18,8 +25,7 @@ struct drvr_prog { struct drvr_mem { unsigned short * base_addr; unsigned short * virt_addr; - unsigned char * dma_buf; - int dma_chan; + struct drvr_dma dma; }; union drvr_data { @@ -34,4 +40,9 @@ struct drvr_device { unsigned char opened; }; +int logi_dma_init(struct drvr_mem* mem_dev, dma_addr_t *physbuf); +void logi_dma_release(struct drvr_mem* mem_dev); +int logi_dma_copy(struct drvr_mem* mem_dev, unsigned long trgt_addr, + unsigned long src_addr, int count); + #endif diff --git a/beaglebone-black/common/logi_dma.c b/beaglebone-black/common/logi_dma.c new file mode 100644 index 0000000..fe362ae --- /dev/null +++ b/beaglebone-black/common/logi_dma.c @@ -0,0 +1,194 @@ +/* + * logi_dma.c - DMA Engine API support for Logi-kernel + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "config.h" +#include "drvr.h" +#include "generic.h" + +static volatile int irqraised1; +static dma_addr_t dmaphysbuf; +static struct completion dma_comp; +#if LOGI_USE_DMAENGINE +static dma_cookie_t cookie; +#endif + +#if LOGI_USE_DMAENGINE +static void dma_callback(void *param) +{ + struct drvr_mem *mem_dev = (struct drvr_mem*) param; + struct dma_chan *chan = mem_dev->dma.chan; + enum dma_status status; + + status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); + switch (status) { + case DMA_COMPLETE: + irqraised1 = 1; + break; + + case DMA_ERROR: + irqraised1 = -1; + break; + + default: + irqraised1 = -1; + break; + } + + complete(&dma_comp); +} +#else +static void dma_callback(unsigned lch, u16 ch_status, void *data) +{ + switch (ch_status) { + case EDMA_DMA_COMPLETE: + irqraised1 = 1; + break; + + case EDMA_DMA_CC_ERROR: + irqraised1 = -1; + break; + + default: + irqraised1 = -1; + break; + } + + complete(&dma_comp); +} +#endif /* LOGI_USE_DMAENGINE */ + +int logi_dma_init(struct drvr_mem* mem_dev, dma_addr_t *physbuf) +{ +#if LOGI_USE_DMAENGINE + struct dma_slave_config conf; + dma_cap_mask_t mask; +#endif + + /* Allocate DMA buffer */ + mem_dev->dma.buf = dma_alloc_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES, + &dmaphysbuf, 0); + if (!mem_dev->dma.buf) { + DBG_LOG("failed to allocate DMA buffer\n"); + return -ENOMEM; + } + *physbuf = dmaphysbuf; + +#if LOGI_USE_DMAENGINE + /* Allocate DMA channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + mem_dev->dma.chan = dma_request_channel(mask, NULL, NULL); + if (!mem_dev->dma.chan) + return -ENODEV; + + /* Configure DMA channel */ + conf.direction = DMA_MEM_TO_MEM; + /*conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;*/ + dmaengine_slave_config(mem_dev->dma.chan, &conf); +#else + mem_dev->dma.dma_chan = edma_alloc_channel(EDMA_CHANNEL_ANY, dma_callback, + NULL, EVENTQ_0); + if (mem_dev->dma.dma_chan < 0) { + DBG_LOG("edma_alloc_channel failed for dma_ch, error: %d\n", + mem_dev->dma.dma_chan); + return mem_dev->dma.dma_chan; + } + + DBG_LOG("EDMA channel %d reserved\n", mem_dev->dma.dma_chan); +#endif /* LOGI_USE_DMAENGINE */ + + init_completion(&dma_comp); + return 0; +} + +void logi_dma_release(struct drvr_mem* mem_dev) +{ +#if LOGI_USE_DMAENGINE + dma_release_channel(mem_dev->dma.chan); +#else + edma_free_channel(mem_dev->dma.dma_chan); +#endif /* LOGI_USE_DMAENGINE */ + dma_free_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES, mem_dev->dma.buf, + dmaphysbuf); +} + +int logi_dma_copy(struct drvr_mem* mem_dev, unsigned long trgt_addr, + unsigned long src_addr, int count) +{ + int result = 0; + +#if LOGI_USE_DMAENGINE + struct dma_chan *chan; + struct dma_device *dev; + struct dma_async_tx_descriptor *tx; + unsigned long flags; + + chan = mem_dev->dma.chan; + dev = chan->device; + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + tx = dev->device_prep_dma_memcpy(chan, trgt_addr, src_addr, count, flags); + if (!tx) { + DBG_LOG("device_prep_dma_memcpy failed\n"); + return -ENODEV; + } + + irqraised1 = 0u; + dma_comp.done = 0; + /* set the callback and submit the transaction */ + tx->callback = dma_callback; + tx->callback_param = mem_dev; + cookie = dmaengine_submit(tx); + dma_async_issue_pending(chan); +#else + struct edmacc_param param_set; + int dma_ch = mem_dev->dma.dma_chan; + + edma_set_src(dma_ch, src_addr, INCR, W256BIT); + edma_set_dest(dma_ch, trgt_addr, INCR, W256BIT); + edma_set_src_index(dma_ch, 1, 1); + edma_set_dest_index(dma_ch, 1, 1); + /* A Sync Transfer Mode */ + edma_set_transfer_params(dma_ch, count, 1, 1, 1, ASYNC);//one block of one frame of one array of count bytes + + /* Enable the Interrupts on Channel 1 */ + edma_read_slot(dma_ch, ¶m_set); + param_set.opt |= ITCINTEN; + param_set.opt |= TCINTEN; + param_set.opt |= EDMA_TCC(EDMA_CHAN_SLOT(dma_ch)); + edma_write_slot(dma_ch, ¶m_set); + irqraised1 = 0u; + dma_comp.done = 0; + result = edma_start(dma_ch); + if (result != 0) { + DBG_LOG("edma copy failed\n"); + return result; + } + +#endif /* LOGI_USE_DMAENGINE */ + + wait_for_completion(&dma_comp); + + /* Check the status of the completed transfer */ + if (irqraised1 < 0) { + DBG_LOG("edma copy: Event Miss Occured!!!\n"); +#if LOGI_USE_DMAENGINE + dmaengine_terminate_all(chan); +#else + edma_stop(dma_ch); +#endif /* LOGI_USE_DMAENGINE */ + result = -EAGAIN; + } + + return result; +} + diff --git a/beaglebone-black/common/main_dma.c b/beaglebone-black/common/main_dma.c index 55425da..b206f94 100644 --- a/beaglebone-black/common/main_dma.c +++ b/beaglebone-black/common/main_dma.c @@ -6,8 +6,6 @@ #include #include #include -#include -#include #include #include @@ -16,7 +14,6 @@ #include #include #include -#include #include "generic.h" #include "config.h" #include "drvr.h" @@ -35,9 +32,6 @@ static int dm_open(struct inode *inode, struct file *filp); static int dm_release(struct inode *inode, struct file *filp); static ssize_t dm_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos); static ssize_t dm_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); -static int edma_memtomemcpy(int count, unsigned long src_addr, unsigned long trgt_addr, int dma_ch); -static void dma_callback(unsigned lch, u16 ch_status, void *data); - static struct i2c_board_info io_exp_info= { I2C_BOARD_INFO("fpga_ctrl", I2C_IO_EXP_ADDR), @@ -52,14 +46,11 @@ static struct file_operations dm_ops = { .release = dm_release, }; - static dma_addr_t dmaphysbuf = 0; -static volatile int irqraised1 = 0; static unsigned char gDrvrMajor = 0; static struct device * prog_device; static struct class * drvr_class; static struct drvr_device * drvr_devices; -static struct completion dma_comp; #ifdef PROFILE @@ -114,7 +105,7 @@ static inline ssize_t writeMem(struct file *filp, const char *buf, size_t count, src_addr = (unsigned long) dmaphysbuf; - if (copy_from_user(mem_to_write->dma_buf, buf, transfer_size)) { + if (copy_from_user(mem_to_write->dma.buf, buf, transfer_size)) { return -EFAULT; } @@ -126,8 +117,8 @@ static inline ssize_t writeMem(struct file *filp, const char *buf, size_t count, start_profile(); #endif - res = edma_memtomemcpy(transfer_size, src_addr, trgt_addr, mem_to_write->dma_chan); - + res = logi_dma_copy(mem_to_write, trgt_addr, src_addr, + transfer_size); if (res < 0) { DBG_LOG("write: Failed to trigger EDMA transfer\n"); @@ -148,7 +139,7 @@ static inline ssize_t writeMem(struct file *filp, const char *buf, size_t count, transfer_size = MAX_DMA_TRANSFER_IN_BYTES; } - if (copy_from_user(mem_to_write->dma_buf, &buf[transferred], transfer_size)) { + if (copy_from_user(mem_to_write->dma.buf, &buf[transferred], transfer_size)) { return -EFAULT; } } @@ -194,15 +185,15 @@ static inline ssize_t readMem(struct file *filp, char *buf, size_t count, loff_t start_profile(); #endif - res = edma_memtomemcpy(transfer_size, src_addr, trgt_addr, mem_to_read->dma_chan); - + res = logi_dma_copy(mem_to_read, trgt_addr, src_addr, + transfer_size); if (res < 0) { DBG_LOG("read: Failed to trigger EDMA transfer\n"); return res; } - if (copy_to_user(&buf[transferred], mem_to_read->dma_buf, transfer_size)) { + if (copy_to_user(&buf[transferred], mem_to_read->dma.buf, transfer_size)) { return -EFAULT; } @@ -227,6 +218,7 @@ static inline ssize_t readMem(struct file *filp, char *buf, size_t count, loff_t static int dm_open(struct inode *inode, struct file *filp) { struct drvr_device* dev = container_of(inode->i_cdev, struct drvr_device, cdev); + int result; filp->private_data = dev; /* for other methods */ @@ -242,22 +234,10 @@ static int dm_open(struct inode *inode, struct file *filp) request_mem_region((unsigned long) mem_dev->base_addr, FPGA_MEM_SIZE, DEVICE_NAME); mem_dev->virt_addr = ioremap_nocache(((unsigned long) mem_dev->base_addr), FPGA_MEM_SIZE); - mem_dev->dma_chan = edma_alloc_channel(EDMA_CHANNEL_ANY, dma_callback, NULL, EVENTQ_0); - - if (mem_dev->dma_chan < 0) { - DBG_LOG("edma_alloc_channel failed for dma_ch, error: %d\n", mem_dev->dma_chan); - - return mem_dev->dma_chan; - } - - DBG_LOG("EDMA channel %d reserved\n", mem_dev->dma_chan); - mem_dev->dma_buf = (unsigned char *) dma_alloc_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES, &dmaphysbuf, 0); - if (mem_dev->dma_buf == NULL) { - DBG_LOG("failed to allocate DMA buffer\n"); - - return -ENOMEM; - } + result = logi_dma_init(mem_dev, &dmaphysbuf); + if (result) + return result; DBG_LOG("mem interface opened\n"); } @@ -271,14 +251,14 @@ static int dm_open(struct inode *inode, struct file *filp) static int dm_release(struct inode *inode, struct file *filp) { struct drvr_device* dev = container_of(inode->i_cdev, struct drvr_device, cdev); + struct drvr_mem* mem_dev = &((dev->data).mem); if (dev->opened != 0) { if (dev->type == mem) { - iounmap((dev->data.mem).virt_addr); - release_mem_region(((unsigned long) (dev->data.mem).base_addr), FPGA_MEM_SIZE); + iounmap(mem_dev->virt_addr); + release_mem_region(((unsigned long) mem_dev->base_addr), FPGA_MEM_SIZE); + logi_dma_release(mem_dev); DBG_LOG("module released\n"); - dma_free_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES, (dev->data.mem).dma_buf, dmaphysbuf); - edma_free_channel((dev->data.mem).dma_chan); } dev->opened = 0; @@ -418,68 +398,10 @@ static int dm_init(void) (drvr_devices[1].cdev).ops = &dm_ops; cdev_add(&(drvr_devices[1].cdev), devno, 1); drvr_devices[1].opened = 0; - init_completion(&dma_comp); return ioctl_init(); } -static inline int edma_memtomemcpy(int count, unsigned long src_addr, unsigned long trgt_addr, int dma_ch) -{ - int result = 0; - struct edmacc_param param_set; - - edma_set_src(dma_ch, src_addr, INCR, W256BIT); - edma_set_dest(dma_ch, trgt_addr, INCR, W256BIT); - edma_set_src_index(dma_ch, 1, 1); - edma_set_dest_index(dma_ch, 1, 1); - /* A Sync Transfer Mode */ - edma_set_transfer_params(dma_ch, count, 1, 1, 1, ASYNC);//one block of one frame of one array of count bytes - - /* Enable the Interrupts on Channel 1 */ - edma_read_slot(dma_ch, ¶m_set); - param_set.opt |= ITCINTEN; - param_set.opt |= TCINTEN; - param_set.opt |= EDMA_TCC(EDMA_CHAN_SLOT(dma_ch)); - edma_write_slot(dma_ch, ¶m_set); - irqraised1 = 0u; - dma_comp.done = 0; - result = edma_start(dma_ch); - - if (result != 0) { - DBG_LOG("edma copy failed\n"); - } - - wait_for_completion(&dma_comp); - - /* Check the status of the completed transfer */ - if (irqraised1 < 0) { - DBG_LOG("edma copy: Event Miss Occured!!!\n"); - edma_stop(dma_ch); - result = -EAGAIN; - } - - return result; -} - -static void dma_callback(unsigned lch, u16 ch_status, void *data) -{ - switch (ch_status) { - case EDMA_DMA_COMPLETE: - irqraised1 = 1; - break; - - case EDMA_DMA_CC_ERROR: - irqraised1 = -1; - break; - - default: - irqraised1 = -1; - break; - } - - complete(&dma_comp); -} - static const struct of_device_id drvr_of_match[] = { { .compatible = DEVICE_NAME, }, { }, diff --git a/beaglebone-black/logibone_r1/Makefile b/beaglebone-black/logibone_r1/Makefile index ea4066b..57e45d6 100644 --- a/beaglebone-black/logibone_r1/Makefile +++ b/beaglebone-black/logibone_r1/Makefile @@ -7,7 +7,7 @@ EXTRA_CFLAGS=-I$(PWD) -Wall -O2 # kernel build system and can use its language. ifneq ($(KERNELRELEASE),) obj-m := logibone_r1_dma.o - logibone_r1_dma-objs := ../common/main_dma.o config.o ioctl.o + logibone_r1_dma-objs := ../common/main_dma.o ../common/logi_dma.o config.o ioctl.o obj-m += logibone_r1_dm.o logibone_r1_dm-objs := ../common/main_dm.o config.o ioctl.o diff --git a/beaglebone-black/logibone_r1/config.h b/beaglebone-black/logibone_r1/config.h index 6c95f09..ddfa951 100644 --- a/beaglebone-black/logibone_r1/config.h +++ b/beaglebone-black/logibone_r1/config.h @@ -11,6 +11,7 @@ #define I2C_ADAPTER 1 #endif +#define LOGI_USE_DMAENGINE 0 int loadBitFile(struct i2c_client * io_cli, const unsigned char * bitBuffer_user, const unsigned int length);