diff options
Diffstat (limited to 'drivers/dma/xilinx')
-rw-r--r-- | drivers/dma/xilinx/xilinx_dma.c | 45 | ||||
-rw-r--r-- | drivers/dma/xilinx/xilinx_dpdma.c | 218 | ||||
-rw-r--r-- | drivers/dma/xilinx/zynqmp_dma.c | 8 |
3 files changed, 229 insertions, 42 deletions
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 5429497d3560..ecff35402860 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -1044,11 +1044,11 @@ static void xilinx_dma_chan_desc_cleanup(struct xilinx_dma_chan *chan) /** * xilinx_dma_do_tasklet - Schedule completion tasklet - * @data: Pointer to the Xilinx DMA channel structure + * @t: Pointer to the Xilinx DMA channel structure */ -static void xilinx_dma_do_tasklet(unsigned long data) +static void xilinx_dma_do_tasklet(struct tasklet_struct *t) { - struct xilinx_dma_chan *chan = (struct xilinx_dma_chan *)data; + struct xilinx_dma_chan *chan = from_tasklet(chan, t, tasklet); xilinx_dma_chan_desc_cleanup(chan); } @@ -2536,13 +2536,8 @@ static int axidma_clk_init(struct platform_device *pdev, struct clk **axi_clk, *tmp_clk = NULL; *axi_clk = devm_clk_get(&pdev->dev, "s_axi_lite_aclk"); - if (IS_ERR(*axi_clk)) { - err = PTR_ERR(*axi_clk); - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get axi_aclk (%d)\n", - err); - return err; - } + if (IS_ERR(*axi_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(*axi_clk), "failed to get axi_aclk\n"); *tx_clk = devm_clk_get(&pdev->dev, "m_axi_mm2s_aclk"); if (IS_ERR(*tx_clk)) @@ -2603,22 +2598,12 @@ static int axicdma_clk_init(struct platform_device *pdev, struct clk **axi_clk, *tmp2_clk = NULL; *axi_clk = devm_clk_get(&pdev->dev, "s_axi_lite_aclk"); - if (IS_ERR(*axi_clk)) { - err = PTR_ERR(*axi_clk); - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get axi_clk (%d)\n", - err); - return err; - } + if (IS_ERR(*axi_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(*axi_clk), "failed to get axi_aclk\n"); *dev_clk = devm_clk_get(&pdev->dev, "m_axi_aclk"); - if (IS_ERR(*dev_clk)) { - err = PTR_ERR(*dev_clk); - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get dev_clk (%d)\n", - err); - return err; - } + if (IS_ERR(*dev_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(*dev_clk), "failed to get dev_clk\n"); err = clk_prepare_enable(*axi_clk); if (err) { @@ -2647,13 +2632,8 @@ static int axivdma_clk_init(struct platform_device *pdev, struct clk **axi_clk, int err; *axi_clk = devm_clk_get(&pdev->dev, "s_axi_lite_aclk"); - if (IS_ERR(*axi_clk)) { - err = PTR_ERR(*axi_clk); - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get axi_aclk (%d)\n", - err); - return err; - } + if (IS_ERR(*axi_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(*axi_clk), "failed to get axi_aclk\n"); *tx_clk = devm_clk_get(&pdev->dev, "m_axi_mm2s_aclk"); if (IS_ERR(*tx_clk)) @@ -2866,8 +2846,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, } /* Initialize the tasklet */ - tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet, - (unsigned long)chan); + tasklet_setup(&chan->tasklet, xilinx_dma_do_tasklet); /* * Initialize the DMA channel and add it to the DMA engine channels diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index b37197c772aa..55df63dead8d 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -10,6 +10,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/dmapool.h> @@ -267,6 +268,210 @@ struct xilinx_dpdma_device { }; /* ----------------------------------------------------------------------------- + * DebugFS + */ + +#ifdef CONFIG_DEBUG_FS + +#define XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE 32 +#define XILINX_DPDMA_DEBUGFS_UINT16_MAX_STR "65535" + +/* Match xilinx_dpdma_testcases vs dpdma_debugfs_reqs[] entry */ +enum xilinx_dpdma_testcases { + DPDMA_TC_INTR_DONE, + DPDMA_TC_NONE +}; + +struct xilinx_dpdma_debugfs { + enum xilinx_dpdma_testcases testcase; + u16 xilinx_dpdma_irq_done_count; + unsigned int chan_id; +}; + +static struct xilinx_dpdma_debugfs dpdma_debugfs; +struct xilinx_dpdma_debugfs_request { + const char *name; + enum xilinx_dpdma_testcases tc; + ssize_t (*read)(char *buf); + int (*write)(char *args); +}; + +static void xilinx_dpdma_debugfs_desc_done_irq(struct xilinx_dpdma_chan *chan) +{ + if (chan->id == dpdma_debugfs.chan_id) + dpdma_debugfs.xilinx_dpdma_irq_done_count++; +} + +static ssize_t xilinx_dpdma_debugfs_desc_done_irq_read(char *buf) +{ + size_t out_str_len; + + dpdma_debugfs.testcase = DPDMA_TC_NONE; + + out_str_len = strlen(XILINX_DPDMA_DEBUGFS_UINT16_MAX_STR); + out_str_len = min_t(size_t, XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE, + out_str_len); + snprintf(buf, out_str_len, "%d", + dpdma_debugfs.xilinx_dpdma_irq_done_count); + + return 0; +} + +static int xilinx_dpdma_debugfs_desc_done_irq_write(char *args) +{ + char *arg; + int ret; + u32 id; + + arg = strsep(&args, " "); + if (!arg || strncasecmp(arg, "start", 5)) + return -EINVAL; + + arg = strsep(&args, " "); + if (!arg) + return -EINVAL; + + ret = kstrtou32(arg, 0, &id); + if (ret < 0) + return ret; + + if (id < ZYNQMP_DPDMA_VIDEO0 || id > ZYNQMP_DPDMA_AUDIO1) + return -EINVAL; + + dpdma_debugfs.testcase = DPDMA_TC_INTR_DONE; + dpdma_debugfs.xilinx_dpdma_irq_done_count = 0; + dpdma_debugfs.chan_id = id; + + return 0; +} + +/* Match xilinx_dpdma_testcases vs dpdma_debugfs_reqs[] entry */ +static struct xilinx_dpdma_debugfs_request dpdma_debugfs_reqs[] = { + { + .name = "DESCRIPTOR_DONE_INTR", + .tc = DPDMA_TC_INTR_DONE, + .read = xilinx_dpdma_debugfs_desc_done_irq_read, + .write = xilinx_dpdma_debugfs_desc_done_irq_write, + }, +}; + +static ssize_t xilinx_dpdma_debugfs_read(struct file *f, char __user *buf, + size_t size, loff_t *pos) +{ + enum xilinx_dpdma_testcases testcase; + char *kern_buff; + int ret = 0; + + if (*pos != 0 || size <= 0) + return -EINVAL; + + kern_buff = kzalloc(XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE, GFP_KERNEL); + if (!kern_buff) { + dpdma_debugfs.testcase = DPDMA_TC_NONE; + return -ENOMEM; + } + + testcase = READ_ONCE(dpdma_debugfs.testcase); + if (testcase != DPDMA_TC_NONE) { + ret = dpdma_debugfs_reqs[testcase].read(kern_buff); + if (ret < 0) + goto done; + } else { + strlcpy(kern_buff, "No testcase executed", + XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE); + } + + size = min(size, strlen(kern_buff)); + if (copy_to_user(buf, kern_buff, size)) + ret = -EFAULT; + +done: + kfree(kern_buff); + if (ret) + return ret; + + *pos = size + 1; + return size; +} + +static ssize_t xilinx_dpdma_debugfs_write(struct file *f, + const char __user *buf, size_t size, + loff_t *pos) +{ + char *kern_buff, *kern_buff_start; + char *testcase; + unsigned int i; + int ret; + + if (*pos != 0 || size <= 0) + return -EINVAL; + + /* Supporting single instance of test as of now. */ + if (dpdma_debugfs.testcase != DPDMA_TC_NONE) + return -EBUSY; + + kern_buff = kzalloc(size, GFP_KERNEL); + if (!kern_buff) + return -ENOMEM; + kern_buff_start = kern_buff; + + ret = strncpy_from_user(kern_buff, buf, size); + if (ret < 0) + goto done; + + /* Read the testcase name from a user request. */ + testcase = strsep(&kern_buff, " "); + + for (i = 0; i < ARRAY_SIZE(dpdma_debugfs_reqs); i++) { + if (!strcasecmp(testcase, dpdma_debugfs_reqs[i].name)) + break; + } + + if (i == ARRAY_SIZE(dpdma_debugfs_reqs)) { + ret = -EINVAL; + goto done; + } + + ret = dpdma_debugfs_reqs[i].write(kern_buff); + if (ret < 0) + goto done; + + ret = size; + +done: + kfree(kern_buff_start); + return ret; +} + +static const struct file_operations fops_xilinx_dpdma_dbgfs = { + .owner = THIS_MODULE, + .read = xilinx_dpdma_debugfs_read, + .write = xilinx_dpdma_debugfs_write, +}; + +static void xilinx_dpdma_debugfs_init(struct xilinx_dpdma_device *xdev) +{ + struct dentry *dent; + + dpdma_debugfs.testcase = DPDMA_TC_NONE; + + dent = debugfs_create_file("testcase", 0444, xdev->common.dbg_dev_root, + NULL, &fops_xilinx_dpdma_dbgfs); + if (IS_ERR(dent)) + dev_err(xdev->dev, "Failed to create debugfs testcase file\n"); +} + +#else +static void xilinx_dpdma_debugfs_init(struct xilinx_dpdma_device *xdev) +{ +} + +static void xilinx_dpdma_debugfs_desc_done_irq(struct xilinx_dpdma_chan *chan) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +/* ----------------------------------------------------------------------------- * I/O Accessors */ @@ -842,6 +1047,8 @@ static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan) spin_lock_irqsave(&chan->lock, flags); + xilinx_dpdma_debugfs_desc_done_irq(chan); + if (active) vchan_cyclic_callback(&active->vdesc); else @@ -1251,15 +1458,15 @@ static void xilinx_dpdma_disable_irq(struct xilinx_dpdma_device *xdev) /** * xilinx_dpdma_chan_err_task - Per channel tasklet for error handling - * @data: tasklet data to be casted to DPDMA channel structure + * @t: pointer to the tasklet associated with this handler * * Per channel error handling tasklet. This function waits for the outstanding * transaction to complete and triggers error handling. After error handling, * re-enable channel error interrupts, and restart the channel if needed. */ -static void xilinx_dpdma_chan_err_task(unsigned long data) +static void xilinx_dpdma_chan_err_task(struct tasklet_struct *t) { - struct xilinx_dpdma_chan *chan = (struct xilinx_dpdma_chan *)data; + struct xilinx_dpdma_chan *chan = from_tasklet(chan, t, err_task); struct xilinx_dpdma_device *xdev = chan->xdev; unsigned long flags; @@ -1348,8 +1555,7 @@ static int xilinx_dpdma_chan_init(struct xilinx_dpdma_device *xdev, spin_lock_init(&chan->lock); init_waitqueue_head(&chan->wait_to_stop); - tasklet_init(&chan->err_task, xilinx_dpdma_chan_err_task, - (unsigned long)chan); + tasklet_setup(&chan->err_task, xilinx_dpdma_chan_err_task); chan->vchan.desc_free = xilinx_dpdma_chan_free_tx_desc; vchan_init(&chan->vchan, &xdev->common); @@ -1477,6 +1683,8 @@ static int xilinx_dpdma_probe(struct platform_device *pdev) xilinx_dpdma_enable_irq(xdev); + xilinx_dpdma_debugfs_init(xdev); + dev_info(&pdev->dev, "Xilinx DPDMA engine is probed\n"); return 0; diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index ff253696d183..d8419565b92c 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -742,11 +742,11 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data) /** * zynqmp_dma_do_tasklet - Schedule completion tasklet - * @data: Pointer to the ZynqMP DMA channel structure + * @t: Pointer to the ZynqMP DMA channel structure */ -static void zynqmp_dma_do_tasklet(unsigned long data) +static void zynqmp_dma_do_tasklet(struct tasklet_struct *t) { - struct zynqmp_dma_chan *chan = (struct zynqmp_dma_chan *)data; + struct zynqmp_dma_chan *chan = from_tasklet(chan, t, tasklet); u32 count; unsigned long irqflags; @@ -908,7 +908,7 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev, chan->is_dmacoherent = of_property_read_bool(node, "dma-coherent"); zdev->chan = chan; - tasklet_init(&chan->tasklet, zynqmp_dma_do_tasklet, (ulong)chan); + tasklet_setup(&chan->tasklet, zynqmp_dma_do_tasklet); spin_lock_init(&chan->lock); INIT_LIST_HEAD(&chan->active_list); INIT_LIST_HEAD(&chan->pending_list); |