summaryrefslogtreecommitdiff
path: root/drivers/dma/fsl-qdma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/fsl-qdma.c')
-rw-r--r--drivers/dma/fsl-qdma.c165
1 files changed, 104 insertions, 61 deletions
diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c
index 95cc0256b387..6ace5bf80c40 100644
--- a/drivers/dma/fsl-qdma.c
+++ b/drivers/dma/fsl-qdma.c
@@ -13,10 +13,10 @@
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
#include "virt-dma.h"
#include "fsldma.h"
@@ -56,7 +56,7 @@
/* Registers for bit and genmask */
#define FSL_QDMA_CQIDR_SQT BIT(15)
-#define QDMA_CCDF_FOTMAT BIT(29)
+#define QDMA_CCDF_FORMAT BIT(29)
#define QDMA_CCDF_SER BIT(30)
#define QDMA_SG_FIN BIT(30)
#define QDMA_SG_LEN_MASK GENMASK(29, 0)
@@ -109,9 +109,21 @@
#define FSL_QDMA_CMD_WTHROTL_OFFSET 20
#define FSL_QDMA_CMD_DSEN_OFFSET 19
#define FSL_QDMA_CMD_LWC_OFFSET 16
+#define FSL_QDMA_CMD_PF BIT(17)
+
+/* Field definition for Descriptor status */
+#define QDMA_CCDF_STATUS_RTE BIT(5)
+#define QDMA_CCDF_STATUS_WTE BIT(4)
+#define QDMA_CCDF_STATUS_CDE BIT(2)
+#define QDMA_CCDF_STATUS_SDE BIT(1)
+#define QDMA_CCDF_STATUS_DDE BIT(0)
+#define QDMA_CCDF_STATUS_MASK (QDMA_CCDF_STATUS_RTE | \
+ QDMA_CCDF_STATUS_WTE | \
+ QDMA_CCDF_STATUS_CDE | \
+ QDMA_CCDF_STATUS_SDE | \
+ QDMA_CCDF_STATUS_DDE)
/* Field definition for Descriptor offset */
-#define QDMA_CCDF_STATUS 20
#define QDMA_CCDF_OFFSET 20
#define QDMA_SDDF_CMD(x) (((u64)(x)) << 32)
@@ -136,7 +148,10 @@
* @__reserved1: Reserved field.
* @cfg8b_w1: Compound descriptor command queue origin produced
* by qDMA and dynamic debug field.
- * @data Pointer to the memory 40-bit address, describes DMA
+ * @__reserved2: Reserved field.
+ * @cmd: Command for QDMA (see FSL_QDMA_CMD_RWTTYPE and
+ * others).
+ * @data: Pointer to the memory 40-bit address, describes DMA
* source information and DMA destination information.
*/
struct fsl_qdma_format {
@@ -149,6 +164,10 @@ struct fsl_qdma_format {
u8 __reserved1[2];
u8 cfg8b_w1;
} __packed;
+ struct {
+ __le32 __reserved2;
+ __le32 cmd;
+ } __packed;
__le64 data;
};
} __packed;
@@ -243,13 +262,14 @@ qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf)
static inline void
qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset)
{
- ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset);
+ ccdf->cfg = cpu_to_le32(QDMA_CCDF_FORMAT |
+ (offset << QDMA_CCDF_OFFSET));
}
static inline int
qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf)
{
- return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS;
+ return (le32_to_cpu(ccdf->status) & QDMA_CCDF_STATUS_MASK);
}
static inline void
@@ -342,7 +362,6 @@ static void fsl_qdma_free_chan_resources(struct dma_chan *chan)
static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
dma_addr_t dst, dma_addr_t src, u32 len)
{
- u32 cmd;
struct fsl_qdma_format *sdf, *ddf;
struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest;
@@ -371,14 +390,11 @@ static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
/* This entry is the last entry. */
qdma_csgf_set_f(csgf_dest, len);
/* Descriptor Buffer */
- cmd = cpu_to_le32(FSL_QDMA_CMD_RWTTYPE <<
- FSL_QDMA_CMD_RWTTYPE_OFFSET);
- sdf->data = QDMA_SDDF_CMD(cmd);
-
- cmd = cpu_to_le32(FSL_QDMA_CMD_RWTTYPE <<
- FSL_QDMA_CMD_RWTTYPE_OFFSET);
- cmd |= cpu_to_le32(FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET);
- ddf->data = QDMA_SDDF_CMD(cmd);
+ sdf->cmd = cpu_to_le32((FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET) |
+ FSL_QDMA_CMD_PF);
+
+ ddf->cmd = cpu_to_le32((FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET) |
+ (FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET));
}
/*
@@ -502,11 +518,11 @@ static struct fsl_qdma_queue
queue_temp = queue_head + i + (j * queue_num);
queue_temp->cq =
- dma_alloc_coherent(&pdev->dev,
- sizeof(struct fsl_qdma_format) *
- queue_size[i],
- &queue_temp->bus_addr,
- GFP_KERNEL);
+ dmam_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ queue_size[i],
+ &queue_temp->bus_addr,
+ GFP_KERNEL);
if (!queue_temp->cq)
return NULL;
queue_temp->block_base = fsl_qdma->block_base +
@@ -551,15 +567,14 @@ static struct fsl_qdma_queue
/*
* Buffer for queue command
*/
- status_head->cq = dma_alloc_coherent(&pdev->dev,
- sizeof(struct fsl_qdma_format) *
- status_size,
- &status_head->bus_addr,
- GFP_KERNEL);
- if (!status_head->cq) {
- devm_kfree(&pdev->dev, status_head);
+ status_head->cq = dmam_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ status_size,
+ &status_head->bus_addr,
+ GFP_KERNEL);
+ if (!status_head->cq)
return NULL;
- }
+
status_head->n_cq = status_size;
status_head->virt_head = status_head->cq;
status_head->virt_tail = status_head->cq;
@@ -613,11 +628,12 @@ static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma)
static int
fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
- void *block,
+ __iomem void *block,
int id)
{
bool duplicate;
u32 reg, i, count;
+ u8 completion_status;
struct fsl_qdma_queue *temp_queue;
struct fsl_qdma_format *status_addr;
struct fsl_qdma_comp *fsl_comp = NULL;
@@ -677,6 +693,8 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
}
list_del(&fsl_comp->list);
+ completion_status = qdma_ccdf_get_status(status_addr);
+
reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
reg |= FSL_QDMA_BSQMR_DI;
qdma_desc_addr_set64(status_addr, 0x0);
@@ -686,6 +704,31 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
spin_unlock(&temp_queue->queue_lock);
+ /* The completion_status is evaluated here
+ * (outside of spin lock)
+ */
+ if (completion_status) {
+ /* A completion error occurred! */
+ if (completion_status & QDMA_CCDF_STATUS_WTE) {
+ /* Write transaction error */
+ fsl_comp->vdesc.tx_result.result =
+ DMA_TRANS_WRITE_FAILED;
+ } else if (completion_status & QDMA_CCDF_STATUS_RTE) {
+ /* Read transaction error */
+ fsl_comp->vdesc.tx_result.result =
+ DMA_TRANS_READ_FAILED;
+ } else {
+ /* Command/source/destination
+ * description error
+ */
+ fsl_comp->vdesc.tx_result.result =
+ DMA_TRANS_ABORTED;
+ dev_err(fsl_qdma->dma_dev.dev,
+ "DMA status descriptor error %x\n",
+ completion_status);
+ }
+ }
+
spin_lock(&fsl_comp->qchan->vchan.lock);
vchan_cookie_complete(&fsl_comp->vdesc);
fsl_comp->qchan->status = DMA_COMPLETE;
@@ -700,11 +743,22 @@ static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
unsigned int intr;
struct fsl_qdma_engine *fsl_qdma = dev_id;
void __iomem *status = fsl_qdma->status_base;
+ unsigned int decfdw0r;
+ unsigned int decfdw1r;
+ unsigned int decfdw2r;
+ unsigned int decfdw3r;
intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
- if (intr)
- dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
+ if (intr) {
+ decfdw0r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW0R);
+ decfdw1r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW1R);
+ decfdw2r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW2R);
+ decfdw3r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW3R);
+ dev_err(fsl_qdma->dma_dev.dev,
+ "DMA transaction error! (%x: %x-%x-%x-%x)\n",
+ intr, decfdw0r, decfdw1r, decfdw2r, decfdw3r);
+ }
qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
return IRQ_HANDLED;
@@ -754,7 +808,7 @@ fsl_qdma_irq_init(struct platform_device *pdev,
int i;
int cpu;
int ret;
- char irq_name[20];
+ char irq_name[32];
fsl_qdma->error_irq =
platform_get_irq_byname(pdev, "qdma-error");
@@ -1068,7 +1122,6 @@ static int fsl_qdma_probe(struct platform_device *pdev)
int ret, i;
int blk_num, blk_off;
u32 len, chans, queues;
- struct resource *res;
struct fsl_qdma_chan *fsl_chan;
struct fsl_qdma_engine *fsl_qdma;
struct device_node *np = pdev->dev.of_node;
@@ -1132,28 +1185,21 @@ static int fsl_qdma_probe(struct platform_device *pdev)
if (!fsl_qdma->status[i])
return -ENOMEM;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+ fsl_qdma->ctrl_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(fsl_qdma->ctrl_base))
return PTR_ERR(fsl_qdma->ctrl_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res);
+ fsl_qdma->status_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(fsl_qdma->status_base))
return PTR_ERR(fsl_qdma->status_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res);
+ fsl_qdma->block_base = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(fsl_qdma->block_base))
return PTR_ERR(fsl_qdma->block_base);
fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma);
if (!fsl_qdma->queue)
return -ENOMEM;
- ret = fsl_qdma_irq_init(pdev, fsl_qdma);
- if (ret)
- return ret;
-
fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0");
if (fsl_qdma->irq_base < 0)
return fsl_qdma->irq_base;
@@ -1184,20 +1230,27 @@ static int fsl_qdma_probe(struct platform_device *pdev)
fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize;
fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all;
- dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+ if (ret) {
+ dev_err(&pdev->dev, "dma_set_mask failure.\n");
+ return ret;
+ }
platform_set_drvdata(pdev, fsl_qdma);
- ret = dma_async_device_register(&fsl_qdma->dma_dev);
+ ret = fsl_qdma_reg_init(fsl_qdma);
if (ret) {
- dev_err(&pdev->dev,
- "Can't register NXP Layerscape qDMA engine.\n");
+ dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
return ret;
}
- ret = fsl_qdma_reg_init(fsl_qdma);
+ ret = fsl_qdma_irq_init(pdev, fsl_qdma);
+ if (ret)
+ return ret;
+
+ ret = dma_async_device_register(&fsl_qdma->dma_dev);
if (ret) {
- dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
+ dev_err(&pdev->dev, "Can't register NXP Layerscape qDMA engine.\n");
return ret;
}
@@ -1215,10 +1268,8 @@ static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev)
}
}
-static int fsl_qdma_remove(struct platform_device *pdev)
+static void fsl_qdma_remove(struct platform_device *pdev)
{
- int i;
- struct fsl_qdma_queue *status;
struct device_node *np = pdev->dev.of_node;
struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev);
@@ -1226,13 +1277,6 @@ static int fsl_qdma_remove(struct platform_device *pdev)
fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev);
of_dma_controller_free(np);
dma_async_device_unregister(&fsl_qdma->dma_dev);
-
- for (i = 0; i < fsl_qdma->block_number; i++) {
- status = fsl_qdma->status[i];
- dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
- status->n_cq, status->cq, status->bus_addr);
- }
- return 0;
}
static const struct of_device_id fsl_qdma_dt_ids[] = {
@@ -1252,6 +1296,5 @@ static struct platform_driver fsl_qdma_driver = {
module_platform_driver(fsl_qdma_driver);
-MODULE_ALIAS("platform:fsl-qdma");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver");