* [v3,10/18] dmaengine: dma-jz4780: Enable Fast DMA to the AIC
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
With the fast DMA bit set, the DMA will transfer twice as much data
per clock period to the AIC, so there is little point not to set it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 88ce3f0157f6..3c9d3952e23a 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -52,6 +52,7 @@
#define JZ_DMA_DMAC_DMAE BIT(0)
#define JZ_DMA_DMAC_AR BIT(2)
#define JZ_DMA_DMAC_HLT BIT(3)
+#define JZ_DMA_DMAC_FAIC BIT(27)
#define JZ_DMA_DMAC_FMSC BIT(31)
#define JZ_DMA_DRT_AUTO 0x8
@@ -941,8 +942,8 @@ static int jz4780_dma_probe(struct platform_device *pdev)
* Also set the FMSC bit - it increases MSC performance, so it makes
* little sense not to enable it.
*/
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
- JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE |
+ JZ_DMA_DMAC_FAIC | JZ_DMA_DMAC_FMSC);
if (jzdma->version == ID_JZ4780)
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
^ permalink raw reply related
* [v3,09/18] dmaengine: dma-jz4780: Add support for the JZ4725B SoC
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The JZ4725B has one DMA core starring six DMA channels.
As for the JZ4770, each DMA channel's clock can be enabled with
a register write, the difference here being that once started, it
is not possible to turn it off.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
v2: - Add comments about channel enabling/disabling
- The documentation update is now in patch 01/17
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 084d4023637e..88ce3f0157f6 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -136,6 +136,7 @@ struct jz4780_dma_chan {
enum jz_version {
ID_JZ4740,
+ ID_JZ4725B,
ID_JZ4770,
ID_JZ4780,
};
@@ -209,8 +210,12 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
unsigned int chn)
{
- if (jzdma->version == ID_JZ4770)
+ if (jzdma->version == ID_JZ4770) {
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
+ } else if (jzdma->version == ID_JZ4725B) {
+ /* JZ4725B has no DCKES, it uses DCKE to enable channels */
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKE, BIT(chn));
+ }
}
static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
@@ -218,6 +223,8 @@ static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
{
if (jzdma->version == ID_JZ4770)
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
+
+ /* On JZ4725B it is not possible to stop a DMA channel once enabled */
}
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
@@ -805,12 +812,14 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
static const struct jz4780_dma_soc_data jz4780_dma_soc_data[] = {
[ID_JZ4740] = { .nb_channels = 6, .transfer_ord_max = 5, },
+ [ID_JZ4725B] = { .nb_channels = 6, .transfer_ord_max = 5, },
[ID_JZ4770] = { .nb_channels = 6, .transfer_ord_max = 6, },
[ID_JZ4780] = { .nb_channels = 32, .transfer_ord_max = 7, },
};
static const struct of_device_id jz4780_dma_dt_match[] = {
{ .compatible = "ingenic,jz4740-dma", .data = (void *)ID_JZ4740 },
+ { .compatible = "ingenic,jz4725b-dma", .data = (void *)ID_JZ4725B },
{ .compatible = "ingenic,jz4770-dma", .data = (void *)ID_JZ4770 },
{ .compatible = "ingenic,jz4780-dma", .data = (void *)ID_JZ4780 },
{},
^ permalink raw reply related
* [v3,08/18] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The JZ4740 SoC has a single DMA core starring six DMA channels.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 3 +++
1 file changed, 3 insertions(+)
v2: The documentation update is now in patch 01/17
v3: The Kconfig update was dropped thanks to patch 06/18
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index a5f4a8d54516..084d4023637e 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -135,6 +135,7 @@ struct jz4780_dma_chan {
};
enum jz_version {
+ ID_JZ4740,
ID_JZ4770,
ID_JZ4780,
};
@@ -803,11 +804,13 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
}
static const struct jz4780_dma_soc_data jz4780_dma_soc_data[] = {
+ [ID_JZ4740] = { .nb_channels = 6, .transfer_ord_max = 5, },
[ID_JZ4770] = { .nb_channels = 6, .transfer_ord_max = 6, },
[ID_JZ4780] = { .nb_channels = 32, .transfer_ord_max = 7, },
};
static const struct of_device_id jz4780_dma_dt_match[] = {
+ { .compatible = "ingenic,jz4740-dma", .data = (void *)ID_JZ4740 },
{ .compatible = "ingenic,jz4770-dma", .data = (void *)ID_JZ4770 },
{ .compatible = "ingenic,jz4780-dma", .data = (void *)ID_JZ4780 },
{},
^ permalink raw reply related
* [v3,07/18] dmaengine: dma-jz4780: Add support for the JZ4770 SoC
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The JZ4770 SoC has two DMA cores, each one featuring six DMA channels.
The major change is that each channel's clock can be enabled or disabled
through register writes.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 46 +++++++++++++++++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 7 deletions(-)
v2: - Move transfer_ord_max variable to the new jz4780_dma_soc_data
structure
- The documentation update is now in patch 01/17
v3: The Kconfig update was dropped thanks to patch 06/18
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 23e92d153919..a5f4a8d54516 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -29,6 +29,9 @@
#define JZ_DMA_REG_DIRQP 0x04
#define JZ_DMA_REG_DDR 0x08
#define JZ_DMA_REG_DDRS 0x0c
+#define JZ_DMA_REG_DCKE 0x10
+#define JZ_DMA_REG_DCKES 0x14
+#define JZ_DMA_REG_DCKEC 0x18
#define JZ_DMA_REG_DMACP 0x1c
#define JZ_DMA_REG_DSIRQP 0x20
#define JZ_DMA_REG_DSIRQM 0x24
@@ -132,11 +135,13 @@ struct jz4780_dma_chan {
};
enum jz_version {
+ ID_JZ4770,
ID_JZ4780,
};
struct jz4780_dma_soc_data {
unsigned int nb_channels;
+ unsigned int transfer_ord_max;
};
struct jz4780_dma_dev {
@@ -200,6 +205,20 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
writel(val, jzdma->ctrl_base + reg);
}
+static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
+ unsigned int chn)
+{
+ if (jzdma->version == ID_JZ4770)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
+}
+
+static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
+ unsigned int chn)
+{
+ if (jzdma->version == ID_JZ4770)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
+}
+
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
struct jz4780_dma_chan *jzchan, unsigned int count,
enum dma_transaction_type type)
@@ -234,8 +253,10 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
kfree(desc);
}
-static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
+static uint32_t jz4780_dma_transfer_size(struct jz4780_dma_chan *jzchan,
+ unsigned long val, uint32_t *shift)
{
+ struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
int ord = ffs(val) - 1;
/*
@@ -247,8 +268,8 @@ static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
*/
if (ord == 3)
ord = 2;
- else if (ord > 7)
- ord = 7;
+ else if (ord > jzdma->soc_data->transfer_ord_max)
+ ord = jzdma->soc_data->transfer_ord_max;
*shift = ord;
@@ -300,7 +321,7 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
* divisible by the transfer size, and we must not use more than the
* maximum burst specified by the user.
*/
- tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst),
+ tsz = jz4780_dma_transfer_size(jzchan, addr | len | (width * maxburst),
&jzchan->transfer_shift);
switch (width) {
@@ -429,7 +450,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
if (!desc)
return NULL;
- tsz = jz4780_dma_transfer_size(dest | src | len,
+ tsz = jz4780_dma_transfer_size(jzchan, dest | src | len,
&jzchan->transfer_shift);
jzchan->transfer_type = JZ_DMA_DRT_AUTO;
@@ -490,6 +511,9 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
(jzchan->curr_hwdesc + 1) % jzchan->desc->count;
}
+ /* Enable the channel's clock. */
+ jz4780_dma_chan_enable(jzdma, jzchan->id);
+
/* Use 4-word descriptors. */
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
@@ -537,6 +561,8 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
jzchan->desc = NULL;
}
+ jz4780_dma_chan_disable(jzdma, jzchan->id);
+
vchan_get_all_descriptors(&jzchan->vchan, &head);
spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
@@ -548,8 +574,10 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
static void jz4780_dma_synchronize(struct dma_chan *chan)
{
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
+ struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
vchan_synchronize(&jzchan->vchan);
+ jz4780_dma_chan_disable(jzdma, jzchan->id);
}
static int jz4780_dma_config(struct dma_chan *chan,
@@ -775,10 +803,12 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
}
static const struct jz4780_dma_soc_data jz4780_dma_soc_data[] = {
- [ID_JZ4780] = { .nb_channels = 32, },
+ [ID_JZ4770] = { .nb_channels = 6, .transfer_ord_max = 6, },
+ [ID_JZ4780] = { .nb_channels = 32, .transfer_ord_max = 7, },
};
static const struct of_device_id jz4780_dma_dt_match[] = {
+ { .compatible = "ingenic,jz4770-dma", .data = (void *)ID_JZ4770 },
{ .compatible = "ingenic,jz4780-dma", .data = (void *)ID_JZ4780 },
{},
};
@@ -901,7 +931,9 @@ static int jz4780_dma_probe(struct platform_device *pdev)
*/
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
+
+ if (jzdma->version == ID_JZ4780)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
INIT_LIST_HEAD(&dd->channels);
^ permalink raw reply related
* [v3,06/18] dmaengine: dma-jz4780: Don't depend on MACH_JZ4780
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
If we make this driver depend on MACH_JZ4780, that means it can be
enabled only if we're building a kernel specially crafted for a
JZ4780-based board, while most GNU/Linux distributions will want one
generic MIPS kernel that works on multiple boards.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
drivers/dma/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
v3: New patch
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680afa20a..0680e1eb0d73 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -143,7 +143,7 @@ config DMA_JZ4740
config DMA_JZ4780
tristate "JZ4780 DMA support"
- depends on MACH_JZ4780 || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
^ permalink raw reply related
* [v3,05/18] dmaengine: dma-jz4780: Use 4-word descriptors
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The only information we use in the 8-word version of the hardware DMA
descriptor that is not present in the 4-word version is the transfer
type, aka. the ID of the source or recipient device.
Since the transfer type will never change for a DMA channel in use,
we can just set it once for all in the corresponding DMA register
before starting any transfer.
This has several benefits:
* the driver will handle twice as many hardware DMA descriptors;
* the driver is closer to support the JZ4740, which only supports 4-word
hardware DMA descriptors;
* the JZ4770 SoC needs the transfer type to be set in the corresponding
DMA register anyway, even if 8-word descriptors are in use.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 2f17a0fb1e5c..23e92d153919 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -95,17 +95,12 @@
* @dtc: transfer count (number of blocks of the transfer size specified in DCM
* to transfer) in the low 24 bits, offset of the next descriptor from the
* descriptor base address in the upper 8 bits.
- * @sd: target/source stride difference (in stride transfer mode).
- * @drt: request type
*/
struct jz4780_dma_hwdesc {
uint32_t dcm;
uint32_t dsa;
uint32_t dta;
uint32_t dtc;
- uint32_t sd;
- uint32_t drt;
- uint32_t reserved[2];
};
/* Size of allocations for hardware descriptor blocks. */
@@ -286,7 +281,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
desc->dcm = JZ_DMA_DCM_SAI;
desc->dsa = addr;
desc->dta = config->dst_addr;
- desc->drt = jzchan->transfer_type;
width = config->dst_addr_width;
maxburst = config->dst_maxburst;
@@ -294,7 +288,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
desc->dcm = JZ_DMA_DCM_DAI;
desc->dsa = config->src_addr;
desc->dta = addr;
- desc->drt = jzchan->transfer_type;
width = config->src_addr_width;
maxburst = config->src_maxburst;
@@ -439,9 +432,10 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
tsz = jz4780_dma_transfer_size(dest | src | len,
&jzchan->transfer_shift);
+ jzchan->transfer_type = JZ_DMA_DRT_AUTO;
+
desc->desc[0].dsa = src;
desc->desc[0].dta = dest;
- desc->desc[0].drt = JZ_DMA_DRT_AUTO;
desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI |
tsz << JZ_DMA_DCM_TSZ_SHIFT |
JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT |
@@ -496,9 +490,12 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
(jzchan->curr_hwdesc + 1) % jzchan->desc->count;
}
- /* Use 8-word descriptors. */
- jz4780_dma_chn_writel(jzdma, jzchan->id,
- JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8);
+ /* Use 4-word descriptors. */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
+
+ /* Set transfer type. */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT,
+ jzchan->transfer_type);
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
@@ -508,7 +505,7 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
/* Enable the channel. */
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS,
- JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
+ JZ_DMA_DCS_CTE);
}
static void jz4780_dma_issue_pending(struct dma_chan *chan)
^ permalink raw reply related
* [v3,04/18] dmaengine: dma-jz4780: Separate chan/ctrl registers
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The register area of the JZ4780 DMA core can be split into different
sections for different purposes:
* one set of registers is used to perform actions at the DMA core level,
that will generally affect all channels;
* one set of registers per DMA channel, to perform actions at the DMA
channel level, that will only affect the channel in question.
The problem rises when trying to support new versions of the JZ47xx
Ingenic SoC. For instance, the JZ4770 has two DMA cores, each one
with six DMA channels, and the register sets are interleaved:
<DMA0 chan regs> <DMA1 chan regs> <DMA0 ctrl regs> <DMA1 ctrl regs>
By using one memory resource for the channel-specific registers and
one memory resource for the core-specific registers, we can support
the JZ4770, by initializing the driver once per DMA core with different
addresses.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 115 ++++++++++++++++++++++++++++++-----------------
1 file changed, 74 insertions(+), 41 deletions(-)
v2: - Add a fallback mechanism for JZ4780 if the second memory resource
was not supplied in the devicetree.
- The documentation update was moved to patch 01/17
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index a26107c85ee7..2f17a0fb1e5c 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -25,26 +25,26 @@
#include "virt-dma.h"
/* Global registers. */
-#define JZ_DMA_REG_DMAC 0x1000
-#define JZ_DMA_REG_DIRQP 0x1004
-#define JZ_DMA_REG_DDR 0x1008
-#define JZ_DMA_REG_DDRS 0x100c
-#define JZ_DMA_REG_DMACP 0x101c
-#define JZ_DMA_REG_DSIRQP 0x1020
-#define JZ_DMA_REG_DSIRQM 0x1024
-#define JZ_DMA_REG_DCIRQP 0x1028
-#define JZ_DMA_REG_DCIRQM 0x102c
+#define JZ_DMA_REG_DMAC 0x00
+#define JZ_DMA_REG_DIRQP 0x04
+#define JZ_DMA_REG_DDR 0x08
+#define JZ_DMA_REG_DDRS 0x0c
+#define JZ_DMA_REG_DMACP 0x1c
+#define JZ_DMA_REG_DSIRQP 0x20
+#define JZ_DMA_REG_DSIRQM 0x24
+#define JZ_DMA_REG_DCIRQP 0x28
+#define JZ_DMA_REG_DCIRQM 0x2c
/* Per-channel registers. */
#define JZ_DMA_REG_CHAN(n) (n * 0x20)
-#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n))
+#define JZ_DMA_REG_DSA 0x00
+#define JZ_DMA_REG_DTA 0x04
+#define JZ_DMA_REG_DTC 0x08
+#define JZ_DMA_REG_DRT 0x0c
+#define JZ_DMA_REG_DCS 0x10
+#define JZ_DMA_REG_DCM 0x14
+#define JZ_DMA_REG_DDA 0x18
+#define JZ_DMA_REG_DSD 0x1c
#define JZ_DMA_DMAC_DMAE BIT(0)
#define JZ_DMA_DMAC_AR BIT(2)
@@ -85,6 +85,8 @@
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+#define JZ4780_DMA_CTRL_OFFSET 0x1000
+
/**
* struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
* @dcm: value for the DCM (channel command) register
@@ -144,7 +146,8 @@ struct jz4780_dma_soc_data {
struct jz4780_dma_dev {
struct dma_device dma_device;
- void __iomem *base;
+ void __iomem *chn_base;
+ void __iomem *ctrl_base;
struct clk *clk;
unsigned int irq;
enum jz_version version;
@@ -178,16 +181,28 @@ static inline struct jz4780_dma_dev *jz4780_dma_chan_parent(
dma_device);
}
-static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma,
+static inline uint32_t jz4780_dma_chn_readl(struct jz4780_dma_dev *jzdma,
+ unsigned int chn, unsigned int reg)
+{
+ return readl(jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn));
+}
+
+static inline void jz4780_dma_chn_writel(struct jz4780_dma_dev *jzdma,
+ unsigned int chn, unsigned int reg, uint32_t val)
+{
+ writel(val, jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn));
+}
+
+static inline uint32_t jz4780_dma_ctrl_readl(struct jz4780_dma_dev *jzdma,
unsigned int reg)
{
- return readl(jzdma->base + reg);
+ return readl(jzdma->ctrl_base + reg);
}
-static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma,
+static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
unsigned int reg, uint32_t val)
{
- writel(val, jzdma->base + reg);
+ writel(val, jzdma->ctrl_base + reg);
}
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
@@ -482,17 +497,18 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
}
/* Use 8-word descriptors. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8);
+ jz4780_dma_chn_writel(jzdma, jzchan->id,
+ JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8);
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
(jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc));
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id));
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DDA, desc_phys);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id));
/* Enable the channel. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id),
- JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS,
+ JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
}
static void jz4780_dma_issue_pending(struct dma_chan *chan)
@@ -518,7 +534,7 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&jzchan->vchan.lock, flags);
/* Clear the DMA status and stop the transfer. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
if (jzchan->desc) {
vchan_terminate_vdesc(&jzchan->desc->vdesc);
jzchan->desc = NULL;
@@ -567,8 +583,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
residue += desc->desc[i].dtc << jzchan->transfer_shift;
if (next_sg != 0) {
- count = jz4780_dma_readl(jzdma,
- JZ_DMA_REG_DTC(jzchan->id));
+ count = jz4780_dma_chn_readl(jzdma, jzchan->id,
+ JZ_DMA_REG_DTC);
residue += count << jzchan->transfer_shift;
}
@@ -615,8 +631,8 @@ static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma,
spin_lock(&jzchan->vchan.lock);
- dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id));
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
+ dcs = jz4780_dma_chn_readl(jzdma, jzchan->id, JZ_DMA_REG_DCS);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
if (dcs & JZ_DMA_DCS_AR) {
dev_warn(&jzchan->vchan.chan.dev->device,
@@ -655,7 +671,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
uint32_t pending, dmac;
int i;
- pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP);
+ pending = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DIRQP);
for (i = 0; i < jzdma->soc_data->nb_channels; i++) {
if (!(pending & (1<<i)))
@@ -665,12 +681,12 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
}
/* Clear halt and address error status of all channels. */
- dmac = jz4780_dma_readl(jzdma, JZ_DMA_REG_DMAC);
+ dmac = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DMAC);
dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, dmac);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, dmac);
/* Clear interrupt pending status. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DIRQP, 0);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DIRQP, 0);
return IRQ_HANDLED;
}
@@ -809,9 +825,26 @@ static int jz4780_dma_probe(struct platform_device *pdev)
return -EINVAL;
}
- jzdma->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(jzdma->base))
- return PTR_ERR(jzdma->base);
+ jzdma->chn_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(jzdma->chn_base))
+ return PTR_ERR(jzdma->chn_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ jzdma->ctrl_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(jzdma->ctrl_base))
+ return PTR_ERR(jzdma->ctrl_base);
+ } else if (version == ID_JZ4780) {
+ /*
+ * On JZ4780, if the second memory resource was not supplied,
+ * assume we're using an old devicetree, and calculate the
+ * offset to the control registers.
+ */
+ jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET;
+ } else {
+ dev_err(dev, "failed to get I/O memory\n");
+ return -EINVAL;
+ }
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
@@ -869,9 +902,9 @@ static int jz4780_dma_probe(struct platform_device *pdev)
* Also set the FMSC bit - it increases MSC performance, so it makes
* little sense not to enable it.
*/
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC,
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
INIT_LIST_HEAD(&dd->channels);
^ permalink raw reply related
* [v3,03/18] dmaengine: dma-jz4780: Avoid hardcoding number of channels
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
As part of the work to support various other Ingenic JZ47xx SoC versions,
which don't feature the same number of DMA channels per core, we now
deduce the number of DMA channels available from the devicetree
compatible string.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 53 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 39 insertions(+), 14 deletions(-)
v2: - don't hardcode jz_version to ID_JZ4780 when not probed from DT,
because it cannot happen
- Put SoC-specific data into a jz4780_dma_soc_data structure
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 987899610b46..a26107c85ee7 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -23,8 +24,6 @@
#include "dmaengine.h"
#include "virt-dma.h"
-#define JZ_DMA_NR_CHANNELS 32
-
/* Global registers. */
#define JZ_DMA_REG_DMAC 0x1000
#define JZ_DMA_REG_DIRQP 0x1004
@@ -135,14 +134,24 @@ struct jz4780_dma_chan {
unsigned int curr_hwdesc;
};
+enum jz_version {
+ ID_JZ4780,
+};
+
+struct jz4780_dma_soc_data {
+ unsigned int nb_channels;
+};
+
struct jz4780_dma_dev {
struct dma_device dma_device;
void __iomem *base;
struct clk *clk;
unsigned int irq;
+ enum jz_version version;
+ const struct jz4780_dma_soc_data *soc_data;
uint32_t chan_reserved;
- struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS];
+ struct jz4780_dma_chan chan[];
};
struct jz4780_dma_filter_data {
@@ -648,7 +657,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
+ for (i = 0; i < jzdma->soc_data->nb_channels; i++) {
if (!(pending & (1<<i)))
continue;
@@ -728,7 +737,7 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
data.channel = dma_spec->args[1];
if (data.channel > -1) {
- if (data.channel >= JZ_DMA_NR_CHANNELS) {
+ if (data.channel >= jzdma->soc_data->nb_channels) {
dev_err(jzdma->dma_device.dev,
"device requested non-existent channel %u\n",
data.channel);
@@ -752,13 +761,27 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
}
}
+static const struct jz4780_dma_soc_data jz4780_dma_soc_data[] = {
+ [ID_JZ4780] = { .nb_channels = 32, },
+};
+
+static const struct of_device_id jz4780_dma_dt_match[] = {
+ { .compatible = "ingenic,jz4780-dma", .data = (void *)ID_JZ4780 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match);
+
static int jz4780_dma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(
+ jz4780_dma_dt_match, dev);
+ const struct jz4780_dma_soc_data *soc_data;
struct jz4780_dma_dev *jzdma;
struct jz4780_dma_chan *jzchan;
struct dma_device *dd;
struct resource *res;
+ enum jz_version version;
int i, ret;
if (!dev->of_node) {
@@ -766,10 +789,18 @@ static int jz4780_dma_probe(struct platform_device *pdev)
return -EINVAL;
}
- jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
+ version = (enum jz_version)of_id->data;
+ soc_data = &jz4780_dma_soc_data[version];
+
+ jzdma = devm_kzalloc(dev, sizeof(*jzdma)
+ + sizeof(*jzdma->chan) * soc_data->nb_channels,
+ GFP_KERNEL);
if (!jzdma)
return -ENOMEM;
+ jzdma->soc_data = soc_data;
+ jzdma->version = version;
+
platform_set_drvdata(pdev, jzdma);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -844,7 +875,7 @@ static int jz4780_dma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dd->channels);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
+ for (i = 0; i < soc_data->nb_channels; i++) {
jzchan = &jzdma->chan[i];
jzchan->id = i;
@@ -889,19 +920,13 @@ static int jz4780_dma_remove(struct platform_device *pdev)
free_irq(jzdma->irq, jzdma);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++)
+ for (i = 0; i < jzdma->soc_data->nb_channels; i++)
tasklet_kill(&jzdma->chan[i].vchan.task);
dma_async_device_unregister(&jzdma->dma_device);
return 0;
}
-static const struct of_device_id jz4780_dma_dt_match[] = {
- { .compatible = "ingenic,jz4780-dma", .data = NULL },
- {},
-};
-MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match);
-
static struct platform_driver jz4780_dma_driver = {
.probe = jz4780_dma_probe,
.remove = jz4780_dma_remove,
^ permalink raw reply related
* [v3,02/18] dmaengine: dma-jz4780: Return error if not probed from DT
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The driver calls clk_get() with the clock name set to NULL, which means
that the driver could only work when probed from devicetree. From now
on, we explicitly require the driver to be probed from devicetree.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 5 +++++
1 file changed, 5 insertions(+)
v2: New patch
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 85820a2d69d4..987899610b46 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -761,6 +761,11 @@ static int jz4780_dma_probe(struct platform_device *pdev)
struct resource *res;
int i, ret;
+ if (!dev->of_node) {
+ dev_err(dev, "This driver must be probed from devicetree\n");
+ return -EINVAL;
+ }
+
jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
if (!jzdma)
return -ENOMEM;
^ permalink raw reply related
* [v3,01/18] doc: dt-bindings: jz4780-dma: Update bindings to reflect driver changes
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The driver is now compatible with four SoCs: JZ4780, JZ4770, JZ4725B and
JZ4740.
Besides, it now expects the devicetree to supply a second memory
resource. This resource is mandatory on the newly supported SoCs.
For the JZ4780, new devicetree code must also provide it, although the
driver is still compatible with older devicetree binaries.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
Documentation/devicetree/bindings/dma/jz4780-dma.txt | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
v2: New patch in this series; regroups the changes made to the
jz4780-dma.txt doc file in the previous version of the patchset.
v3: Updated example to comply with devicetree specification
diff --git a/Documentation/devicetree/bindings/dma/jz4780-dma.txt b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
index f25feee62b15..14f33305e194 100644
--- a/Documentation/devicetree/bindings/dma/jz4780-dma.txt
+++ b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
@@ -2,8 +2,13 @@
Required properties:
-- compatible: Should be "ingenic,jz4780-dma"
-- reg: Should contain the DMA controller registers location and length.
+- compatible: Should be one of:
+ * ingenic,jz4740-dma
+ * ingenic,jz4725b-dma
+ * ingenic,jz4770-dma
+ * ingenic,jz4780-dma
+- reg: Should contain the DMA channel registers location and length, followed
+ by the DMA controller registers location and length.
- interrupts: Should contain the interrupt specifier of the DMA controller.
- interrupt-parent: Should be the phandle of the interrupt controller that
- clocks: Should contain a clock specifier for the JZ4780 PDMA clock.
@@ -20,9 +25,10 @@ Optional properties:
Example:
-dma: dma@13420000 {
+dma: dma-controller@13420000 {
compatible = "ingenic,jz4780-dma";
- reg = <0x13420000 0x10000>;
+ reg = <0x13420000 0x400
+ 0x13421000 0x40>;
interrupt-parent = <&intc>;
interrupts = <10>;
^ permalink raw reply related
* [RFC] dmaengine: Add metadat_ops for dma_async_tx_descriptor
From: Peter Ujfalusi @ 2018-07-20 13:42 UTC (permalink / raw)
To: Vinod
Cc: radheys, vinod.koul, lars, michal.simek, linux-kernel, dmaengine,
dan.j.williams, appanad, linux-arm-kernel
On 2018-07-19 12:22, Vinod wrote:
> Hi Peter,
>
> On 18-07-18, 13:06, Peter Ujfalusi wrote:
>
>>>> +struct dma_async_tx_descriptor;
>>>> +
>>>> +struct dma_descriptor_metadata_ops {
>>>> + int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
>>>> + size_t len);
>>>
>>> How does one detach?
>>
>> I have not thought about detach, but clients can just attach NULL I guess.
>
> So what are the implication of attach and detach here, should the data
> be deref by dmaengine driver and drop the ref.
It largely depends on the DMA driver, but I think we must have clear
definition on how clients (and thus DMA drivers) must handle the metadata.
I think the simpler rule would be that clients _must_ attach the
metadata buffer after _prepare() and before issue_pending() and they
must make sure that the buffer is valid (not freed up) before the
completion callback is called for the given descriptor.
About the detach: If clients detaches the metadata buffer then on
completion it is not going to receive back any metadata and I think the
DMA drivers should clean and disable the metadata sending as well if the
detach happens before issue_pending().
> Should anyone do refcounting?
Need to think about that.
>>
>>> When should the client free up the memory, IOW when
>>> does dma driver drop ref to data.
>>
>> The metadata is for the descriptor so the DMA driver might want to
>> access to it while the descriptor is valid.
>>
>> Typically clients can free up their metadata storage after the dma
>> completion callback. On DEV_TO_MEM the metadata is going to be placed in
>> the provided buffer when the transfer is completed.
>
> That sounds okay to me
>
>>>> + void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
>>>> + size_t *payload_len, size_t *max_len);
>>>
>>> so what is this supposed to do..?
>>
>> My issue with the attach in general is that it will need additional
>> memcpy to move the metadata from/to the client buffer to it's place.
>>
>> With get_ptr the client can get the pointer to the actual place where
>> the metadata resides and modify/read it in place w/o memcpy.
>>
>> I know, I know... We need to trust the clients, but with high throughput
>> peripherals the memcpy is taxing.
>
> Okay I am not sure I have understood fully, so with attach you set
> a pointer (containing metdata?) so why do you need additional one..
>
>>
>>>
>>>> + int (*set_len)(struct dma_async_tx_descriptor *desc,
>>>> + size_t payload_len);
>>>
>>> attach already has length, so how does this help?
>>
>> So, DMA drivers can implement either or both:
>> 1. attach()
>> 2. get_ptr() / set_len()
>
> Ah okay, what are the reasons for providing two methods and not a single
> one
For the HW I have it would be more efficient to grab pointer and do
in-place modification to metadata section (the part of the CPPI5
descriptor which is owned by the client driver).
Other vendors might have the metadata scattered, or in different way
which does not fit with the ptr mode for security or sanity point of
view - I don't want to give the whole descriptor to the client. I don't
trust ;)
>>
>> Clients must not mix the two way of handling the metadata.
>> The set_len() is intended to tell the DMA driver the client provided
>> metadata size (in MEM_TO_DEV case mostly).
>>
>> MEM_TO_DEV flow on client side:
>> get_ptr()
>> fill in the metadata to the pointer (not exceeding max_len)
>> set_len() to tell the DMA driver the amount of valid bytes written
>>
>> DEV_TO_MEM flow on client side:
>> In the completion callback, get_ptr()
>> the metadata is payload_len bytes and can be accessed in the return pointer.
>
> I would think to unify this..
I have tried it, but the attach mode and the pointer mode is hard to
handle with a generic API.
I will try to find a way to unify things in a sane way.
I have moved the metadata_ops to dma_async_tx_descriptor to emphasize
that it is per descriptor setting:
https://github.com/omap-audio/linux-audio/commit/02e095d1320a4bb3ae281ddb208ce82ead746f00#diff-92c0a79f414dc3be9dfc67a969c0dd71
>> BTW: The driver which is going to need this is now accessible in public:
>> https://git.ti.com/ti-linux-kernel/ti-linux-kernel/trees/ti-linux-4.14.y/drivers/dma/ti
>>
>> or in my wip tree:
>> https://github.com/omap-audio/linux-audio/tree/peter/ti-linux-4.14.y/wip/drivers/dma/ti
>>
>> prefixed with k3-*
>>
- Péter
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v2,2/3] dmaengine: imx-sdma: add memcpy interface
From: Robin Gong @ 2018-07-20 9:40 UTC (permalink / raw)
To: Sascha Hauer
Cc: vkoul@kernel.org, dan.j.williams@intel.com, shawnguo@kernel.org,
Fabio Estevam, linux@armlinux.org.uk,
linux-arm-kernel@lists.infradead.org, kernel@pengutronix.de,
dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
dl-linux-imx
> -----Original Message-----
> From: Sascha Hauer [mailto:s.hauer@pengutronix.de]
> Sent: 2018年7月13日 14:16
> To: Robin Gong <yibin.gong@nxp.com>
> Cc: vkoul@kernel.org; dan.j.williams@intel.com; shawnguo@kernel.org; Fabio
> Estevam <fabio.estevam@nxp.com>; linux@armlinux.org.uk;
> linux-arm-kernel@lists.infradead.org; kernel@pengutronix.de;
> dmaengine@vger.kernel.org; linux-kernel@vger.kernel.org; dl-linux-imx
> <linux-imx@nxp.com>
> Subject: Re: [PATCH v2 2/3] dmaengine: imx-sdma: add memcpy interface
>
> On Fri, Jul 13, 2018 at 09:08:46PM +0800, Robin Gong wrote:
> > Add MEMCPY capability for imx-sdma driver.
> >
> > Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> > ---
> > drivers/dma/imx-sdma.c | 95
> > ++++++++++++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 92 insertions(+), 3 deletions(-)
> >
> > @@ -1318,6 +1347,63 @@ static struct sdma_desc
> *sdma_transfer_init(struct sdma_channel *sdmac,
> > return NULL;
> > }
> >
> > +static struct dma_async_tx_descriptor *sdma_prep_memcpy(
> > + struct dma_chan *chan, dma_addr_t dma_dst,
> > + dma_addr_t dma_src, size_t len, unsigned long flags) {
> > + struct sdma_channel *sdmac = to_sdma_chan(chan);
> > + struct sdma_engine *sdma = sdmac->sdma;
> > + int channel = sdmac->channel;
> > + size_t count;
> > + int i = 0, param;
> > + struct sdma_buffer_descriptor *bd;
> > + struct sdma_desc *desc;
> > +
> > + if (!chan || !len)
> > + return NULL;
> > +
> > + dev_dbg(sdma->dev, "memcpy: %pad->%pad, len=%zu, channel=%d.\n",
> > + &dma_src, &dma_dst, len, channel);
> > +
> > + desc = sdma_transfer_init(sdmac, DMA_MEM_TO_MEM,
> > + len / SDMA_BD_MAX_CNT + 1);
> > + if (!desc)
> > + return NULL;
> > +
> > + do {
> > + count = min_t(size_t, len, SDMA_BD_MAX_CNT);
> > + bd = &desc->bd[i];
> > + bd->buffer_addr = dma_src;
> > + bd->ext_buffer_addr = dma_dst;
> > + bd->mode.count = count;
> > + desc->chn_count += count;
> > + /* align with sdma->dma_device.copy_align: 4bytes */
> > + bd->mode.command = 0;
> > +
> > + dma_src += count;
> > + dma_dst += count;
> > + len -= count;
> > + i++;
>
> NACK.
>
> Please actually look at your code and find out where you do unaligned DMA
> accesses. Hint: What happens when this loop body is executed more than
> once?
>
> Sascha
Actually internal sdma script has already take such 'align' matter like below:
Burst with words if source/dest address and data length are all aligned with words, burst with
Byte if not. So I will remove the comment '/* align with sdma->dma_device.copy_align: 4bytes */'
In v3. Besides, I have found another bug which introduced by my virt-dma patch. Will send
V3 later.
>
> > +
> > + param = BD_DONE | BD_EXTD | BD_CONT;
> > + /* last bd */
> > + if (!len) {
> > + param |= BD_INTR;
> > + param |= BD_LAST;
> > + param &= ~BD_CONT;
> > + }
> > +
> > + dev_dbg(sdma->dev, "entry %d: count: %zd dma: 0x%x %s%s\n",
> > + i, count, bd->buffer_addr,
> > + param & BD_WRAP ? "wrap" : "",
> > + param & BD_INTR ? " intr" : "");
> > +
> > + bd->mode.status = param;
> > + } while (len);
> > +
>
> --
> Pengutronix e.K. |
> |
> Industrial Linux Solutions |
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> pengutronix.de%2F&data=02%7C01%7Cyibin.gong%40nxp.com%7Cf78f9
> 91028e64b45cf7508d5e888310d%7C686ea1d3bc2b4c6fa92cd99c5c301635%7
> C0%7C0%7C636670594003030732&sdata=zyDkLHVl4VneD05f4VvaY82m
> ZITkybewrXyCj2YRu%2FI%3D&reserved=0 |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
> Amtsgericht Hildesheim, HRA 2686 | Fax:
> +49-5121-206917-5555 |
^ permalink raw reply
* [3/5] dmaengine: mv_xor_v2: convert callback to helper function
From: Vinod Koul @ 2018-07-20 9:29 UTC (permalink / raw)
To: hannah
Cc: dan.j.williams, dmaengine, thomas.petazzoni, linux-kernel, nadavh,
omrii, oferh, gregory.clement
On 17-07-18, 13:30, hannah@marvell.com wrote:
> From: Hanna Hawa <hannah@marvell.com>
>
> This is in preperation of moving to a callback that provides results to the
typo preperation
> callback for the transaction. The conversion will maintain current behavior
> and the driver must convert to new callback mechanism at a later time in
> order to receive results.
>
> Signed-off-by: Hanna Hawa <hannah@marvell.com>
> ---
> drivers/dma/mv_xor_v2.c | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c
> index e718498..14e2a7a 100644
> --- a/drivers/dma/mv_xor_v2.c
> +++ b/drivers/dma/mv_xor_v2.c
> @@ -589,9 +589,8 @@ static void mv_xor_v2_tasklet(unsigned long data)
> */
> dma_cookie_complete(&next_pending_sw_desc->async_tx);
>
> - if (next_pending_sw_desc->async_tx.callback)
> - next_pending_sw_desc->async_tx.callback(
> - next_pending_sw_desc->async_tx.callback_param);
> + dmaengine_desc_get_callback_invoke(
> + &next_pending_sw_desc->async_tx, NULL);
>
> dma_descriptor_unmap(&next_pending_sw_desc->async_tx);
> }
^ permalink raw reply
* [RFC] dmaengine: Add metadat_ops for dma_async_tx_descriptor
From: Vinod Koul @ 2018-07-19 9:22 UTC (permalink / raw)
To: Peter Ujfalusi
Cc: radheys, vinod.koul, lars, michal.simek, linux-kernel, dmaengine,
dan.j.williams, appanad, linux-arm-kernel
Hi Peter,
On 18-07-18, 13:06, Peter Ujfalusi wrote:
> >> +struct dma_async_tx_descriptor;
> >> +
> >> +struct dma_descriptor_metadata_ops {
> >> + int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
> >> + size_t len);
> >
> > How does one detach?
>
> I have not thought about detach, but clients can just attach NULL I guess.
So what are the implication of attach and detach here, should the data
be deref by dmaengine driver and drop the ref.
Should anyone do refcounting?
>
> > When should the client free up the memory, IOW when
> > does dma driver drop ref to data.
>
> The metadata is for the descriptor so the DMA driver might want to
> access to it while the descriptor is valid.
>
> Typically clients can free up their metadata storage after the dma
> completion callback. On DEV_TO_MEM the metadata is going to be placed in
> the provided buffer when the transfer is completed.
That sounds okay to me
> >> + void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
> >> + size_t *payload_len, size_t *max_len);
> >
> > so what is this supposed to do..?
>
> My issue with the attach in general is that it will need additional
> memcpy to move the metadata from/to the client buffer to it's place.
>
> With get_ptr the client can get the pointer to the actual place where
> the metadata resides and modify/read it in place w/o memcpy.
>
> I know, I know... We need to trust the clients, but with high throughput
> peripherals the memcpy is taxing.
Okay I am not sure I have understood fully, so with attach you set
a pointer (containing metdata?) so why do you need additional one..
>
> >
> >> + int (*set_len)(struct dma_async_tx_descriptor *desc,
> >> + size_t payload_len);
> >
> > attach already has length, so how does this help?
>
> So, DMA drivers can implement either or both:
> 1. attach()
> 2. get_ptr() / set_len()
Ah okay, what are the reasons for providing two methods and not a single
one
>
> Clients must not mix the two way of handling the metadata.
> The set_len() is intended to tell the DMA driver the client provided
> metadata size (in MEM_TO_DEV case mostly).
>
> MEM_TO_DEV flow on client side:
> get_ptr()
> fill in the metadata to the pointer (not exceeding max_len)
> set_len() to tell the DMA driver the amount of valid bytes written
>
> DEV_TO_MEM flow on client side:
> In the completion callback, get_ptr()
> the metadata is payload_len bytes and can be accessed in the return pointer.
I would think to unify this..
> BTW: The driver which is going to need this is now accessible in public:
> https://git.ti.com/ti-linux-kernel/ti-linux-kernel/trees/ti-linux-4.14.y/drivers/dma/ti
>
> or in my wip tree:
> https://github.com/omap-audio/linux-audio/tree/peter/ti-linux-4.14.y/wip/drivers/dma/ti
>
> prefixed with k3-*
>
^ permalink raw reply
* [4/4] MAINTAINERS: Add entry for Actions Semi Owl SoCs DMA driver
From: Manivannan Sadhasivam @ 2018-07-18 20:07 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add entry for Actions Semi Owl SoCs DMA driver under ARM/ACTIONS.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 09b54e9ebc6f..56d9c7715c2a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1145,12 +1145,14 @@ F: arch/arm/boot/dts/owl-*
F: arch/arm64/boot/dts/actions/
F: drivers/clk/actions/
F: drivers/clocksource/owl-*
+F: drivers/dma/owl-dma.c
F: drivers/pinctrl/actions/*
F: drivers/soc/actions/
F: include/dt-bindings/power/owl-*
F: include/linux/soc/actions/
F: Documentation/devicetree/bindings/arm/actions.txt
F: Documentation/devicetree/bindings/clock/actions,s900-cmu.txt
+F: Documentation/devicetree/bindings/dma/owl-dma.txt
F: Documentation/devicetree/bindings/pinctrl/actions,s900-pinctrl.txt
F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
F: Documentation/devicetree/bindings/timer/actions,owl-timer.txt
^ permalink raw reply related
* [3/4] dma: Add Actions Semi Owl family S900 DMA driver
From: Manivannan Sadhasivam @ 2018-07-18 20:07 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add Actions Semi Owl family S900 DMA driver.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/dma/Kconfig | 8 +
drivers/dma/Makefile | 1 +
drivers/dma/owl-dma.c | 1021 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1030 insertions(+)
create mode 100644 drivers/dma/owl-dma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680afa20a..92a278e6618c 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -413,6 +413,14 @@ config NBPFAXI_DMA
help
Support for "Type-AXI" NBPF DMA IPs from Renesas
+config OWL_DMA
+ tristate "Actions Semi Owl SoCs DMA support"
+ depends on ARCH_ACTIONS
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for the Actions Semi Owl SoCs DMA controller.
+
config PCH_DMA
tristate "Intel EG20T PCH / LAPIS Semicon IOH(ML7213/ML7223/ML7831) DMA"
depends on PCI && (X86_32 || COMPILE_TEST)
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 203a99d68315..c91702d88b95 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_MV_XOR_V2) += mv_xor_v2.o
obj-$(CONFIG_MXS_DMA) += mxs-dma.o
obj-$(CONFIG_MX3_IPU) += ipu/
obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
+obj-$(CONFIG_OWL_DMA) += owl-dma.o
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_PL330_DMA) += pl330.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c
new file mode 100644
index 000000000000..065d697edc19
--- /dev/null
+++ b/drivers/dma/owl-dma.c
@@ -0,0 +1,1021 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Actions Semi Owl SoCs DMA driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/slab.h>
+#include "virt-dma.h"
+
+#define OWL_DMA_FRAME_MAX_LENGTH 0xfffff
+
+/* Global DMA Controller Registers */
+#define OWL_DMA_IRQ_PD0 0x00
+#define OWL_DMA_IRQ_PD1 0x04
+#define OWL_DMA_IRQ_PD2 0x08
+#define OWL_DMA_IRQ_PD3 0x0C
+#define OWL_DMA_IRQ_EN0 0x10
+#define OWL_DMA_IRQ_EN1 0x14
+#define OWL_DMA_IRQ_EN2 0x18
+#define OWL_DMA_IRQ_EN3 0x1C
+#define OWL_DMA_SECURE_ACCESS_CTL 0x20
+#define OWL_DMA_NIC_QOS 0x24
+#define OWL_DMA_DBGSEL 0x28
+#define OWL_DMA_IDLE_STAT 0x2C
+
+/* Channel Registers */
+#define OWL_DMA_CHAN_BASE(i) (0x100 + (i) * 0x100)
+#define OWL_DMAX_MODE 0x00
+#define OWL_DMAX_SOURCE 0x04
+#define OWL_DMAX_DESTINATION 0x08
+#define OWL_DMAX_FRAME_LEN 0x0C
+#define OWL_DMAX_FRAME_CNT 0x10
+#define OWL_DMAX_REMAIN_FRAME_CNT 0x14
+#define OWL_DMAX_REMAIN_CNT 0x18
+#define OWL_DMAX_SOURCE_STRIDE 0x1C
+#define OWL_DMAX_DESTINATION_STRIDE 0x20
+#define OWL_DMAX_START 0x24
+#define OWL_DMAX_PAUSE 0x28
+#define OWL_DMAX_CHAINED_CTL 0x2C
+#define OWL_DMAX_CONSTANT 0x30
+#define OWL_DMAX_LINKLIST_CTL 0x34
+#define OWL_DMAX_NEXT_DESCRIPTOR 0x38
+#define OWL_DMAX_CURRENT_DESCRIPTOR_NUM 0x3C
+#define OWL_DMAX_INT_CTL 0x40
+#define OWL_DMAX_INT_STATUS 0x44
+#define OWL_DMAX_CURRENT_SOURCE_POINTER 0x48
+#define OWL_DMAX_CURRENT_DESTINATION_POINTER 0x4C
+
+/* OWL_DMAX_MODE Bits */
+#define OWL_DMA_MODE_TS(x) (((x) & 0x3f) << 0)
+#define OWL_DMA_MODE_ST(x) (((x) & 0x3) << 8)
+#define OWL_DMA_MODE_ST_DEV OWL_DMA_MODE_ST(0)
+#define OWL_DMA_MODE_ST_DCU OWL_DMA_MODE_ST(2)
+#define OWL_DMA_MODE_ST_SRAM OWL_DMA_MODE_ST(3)
+#define OWL_DMA_MODE_DT(x) (((x) & 0x3) << 10)
+#define OWL_DMA_MODE_DT_DEV OWL_DMA_MODE_DT(0)
+#define OWL_DMA_MODE_DT_DCU OWL_DMA_MODE_DT(2)
+#define OWL_DMA_MODE_DT_SRAM OWL_DMA_MODE_DT(3)
+#define OWL_DMA_MODE_SAM(x) (((x) & 0x3) << 16)
+#define OWL_DMA_MODE_SAM_CONST OWL_DMA_MODE_SAM(0)
+#define OWL_DMA_MODE_SAM_INC OWL_DMA_MODE_SAM(1)
+#define OWL_DMA_MODE_SAM_STRIDE OWL_DMA_MODE_SAM(2)
+#define OWL_DMA_MODE_DAM(x) (((x) & 0x3) << 18)
+#define OWL_DMA_MODE_DAM_CONST OWL_DMA_MODE_DAM(0)
+#define OWL_DMA_MODE_DAM_INC OWL_DMA_MODE_DAM(1)
+#define OWL_DMA_MODE_DAM_STRIDE OWL_DMA_MODE_DAM(2)
+#define OWL_DMA_MODE_PW(x) (((x) & 0x7) << 20)
+#define OWL_DMA_MODE_CB BIT(23)
+#define OWL_DMA_MODE_NDDBW(x) (((x) & 0x1) << 28)
+#define OWL_DMA_MODE_NDDBW_32BIT OWL_DMA_MODE_NDDBW(0)
+#define OWL_DMA_MODE_NDDBW_8BIT OWL_DMA_MODE_NDDBW(1)
+#define OWL_DMA_MODE_CFE BIT(29)
+#define OWL_DMA_MODE_LME BIT(30)
+#define OWL_DMA_MODE_CME BIT(31)
+
+/* OWL_DMAX_LINKLIST_CTL Bits */
+#define OWL_DMA_LLC_SAV(x) (((x) & 0x3) << 8)
+#define OWL_DMA_LLC_SAV_INC OWL_DMA_LLC_SAV(0)
+#define OWL_DMA_LLC_SAV_LOAD_NEXT OWL_DMA_LLC_SAV(1)
+#define OWL_DMA_LLC_SAV_LOAD_PREV OWL_DMA_LLC_SAV(2)
+#define OWL_DMA_LLC_DAV(x) (((x) & 0x3) << 10)
+#define OWL_DMA_LLC_DAV_INC OWL_DMA_LLC_DAV(0)
+#define OWL_DMA_LLC_DAV_LOAD_NEXT OWL_DMA_LLC_DAV(1)
+#define OWL_DMA_LLC_DAV_LOAD_PREV OWL_DMA_LLC_DAV(2)
+#define OWL_DMA_LLC_SUSPEND BIT(16)
+
+/* OWL_DMAX_INT_CTL Bits */
+#define OWL_DMA_INTCTL_BLOCK BIT(0)
+#define OWL_DMA_INTCTL_SUPER_BLOCK BIT(1)
+#define OWL_DMA_INTCTL_FRAME BIT(2)
+#define OWL_DMA_INTCTL_HALF_FRAME BIT(3)
+#define OWL_DMA_INTCTL_LAST_FRAME BIT(4)
+
+/* OWL_DMAX_INT_STATUS Bits */
+#define OWL_DMA_INTSTAT_BLOCK BIT(0)
+#define OWL_DMA_INTSTAT_SUPER_BLOCK BIT(1)
+#define OWL_DMA_INTSTAT_FRAME BIT(2)
+#define OWL_DMA_INTSTAT_HALF_FRAME BIT(3)
+#define OWL_DMA_INTSTAT_LAST_FRAME BIT(4)
+
+/* Extract the bit field to new shift */
+#define BIT_FIELD(val, width, shift, newshift) \
+ ((((val) >> (shift)) & ((BIT(width)) - 1)) << newshift)
+
+/**
+ * struct owl_dma_lli_hw - Hardware link list for dma transfer
+ * @next_lli: physical address of the next link list
+ * @saddr: source physical address
+ * @daddr: destination physical address
+ * @flen: frame length
+ * @fcnt: frame count
+ * @src_stride: source stride
+ * @dst_stride: destination stride
+ * @ctrla: dma_mode and linklist ctrl config
+ * @ctrlb: interrupt config
+ * @const_num: data for constant fill
+ */
+struct owl_dma_lli_hw {
+ u32 next_lli; /* physical address of the next link list */
+ u32 saddr; /* source physical address */
+ u32 daddr; /* destination physical address */
+ u32 flen:20; /* frame length */
+ u32 fcnt:12; /* frame count */
+ u32 src_stride; /* source stride */
+ u32 dst_stride; /* destination stride */
+ u32 ctrla; /* dma_mode and linklist ctrl */
+ u32 ctrlb; /* interrupt control */
+ u32 const_num; /* data for constant fill */
+};
+
+/**
+ * struct owl_dma_lli - Link list for dma transfer
+ * @hw: hardware link list
+ * @phys: physical address of hardware link list
+ * @node: node for txd's lli_list
+ */
+struct owl_dma_lli {
+ struct owl_dma_lli_hw hw;
+ dma_addr_t phys;
+ struct list_head node;
+};
+
+/**
+ * struct owl_dma_txd - Wrapper for struct dma_async_tx_descriptor
+ * @vd: virtual DMA descriptor
+ * @lli_list: link list of children sg's
+ */
+struct owl_dma_txd {
+ struct virt_dma_desc vd;
+ struct list_head lli_list;
+};
+
+/**
+ * struct owl_dma_pchan - Holder for the physical channels
+ * @id: physical index to this channel
+ * @base: virtual memory base for the dma channel
+ * @vchan: the virtual channel currently being served by this physical channel
+ * @lock: a lock to use when altering an instance of this struct
+ */
+struct owl_dma_pchan {
+ u32 id;
+ void __iomem *base;
+ struct owl_dma_vchan *vchan;
+ spinlock_t lock;
+};
+
+/**
+ * struct owl_dma_pchan - Wrapper for DMA ENGINE channel
+ * @vc: wrappped virtual channel
+ * @pchan: the physical channel utilized by this channel
+ * @txd: active transaction on this channel
+ */
+struct owl_dma_vchan {
+ struct virt_dma_chan vc;
+ struct owl_dma_pchan *pchan;
+ struct owl_dma_txd *txd;
+};
+
+/**
+ * struct owl_dma - Holder for the Owl DMA controller
+ * @dma: dma engine for this instance
+ * @base: virtual memory base for the DMA controller
+ * @clk: clock for the DMA controller
+ * @lock: a lock to use when change DMA controller global register
+ * @lli_pool: a pool for the LLI descriptors
+ * @nr_pchans: the number of physical channels
+ * @pchans: array of data for the physical channels
+ * @nr_vchans: the number of physical channels
+ * @vchans: array of data for the physical channels
+ */
+struct owl_dma {
+ struct dma_device dma;
+ void __iomem *base;
+ struct clk *clk;
+ spinlock_t lock;
+ struct dma_pool *lli_pool;
+
+ /* physical dma channels */
+ unsigned int nr_pchans;
+ struct owl_dma_pchan *pchans;
+
+ /* virtual dma channels */
+ unsigned int nr_vchans;
+ struct owl_dma_vchan *vchans;
+};
+
+static void pchan_update(void __iomem *reg, u32 val, bool state)
+{
+ u32 regval;
+
+ regval = readl(reg);
+
+ if (state)
+ regval |= val;
+ else
+ regval &= ~val;
+
+ writel(val, reg);
+}
+
+static void pchan_writel(struct owl_dma_pchan *pchan, u32 data, u32 reg)
+{
+ writel(data, pchan->base + reg);
+}
+
+static u32 pchan_readl(struct owl_dma_pchan *pchan, u32 reg)
+{
+ return readl(pchan->base + reg);
+}
+
+static void dma_update(void __iomem *reg, u32 val, bool state)
+{
+ u32 regval;
+
+ regval = readl(reg);
+
+ if (state)
+ regval |= val;
+ else
+ regval &= ~val;
+
+ writel(val, reg);
+}
+
+static void dma_writel(struct owl_dma *od, u32 data, u32 reg)
+{
+ writel(data, od->base + reg);
+}
+
+static u32 dma_readl(struct owl_dma *od, u32 reg)
+{
+ return readl(od->base + reg);
+}
+
+static inline struct owl_dma *to_owl_dma(struct dma_device *dd)
+{
+ return container_of(dd, struct owl_dma, dma);
+}
+
+static struct device *chan2dev(struct dma_chan *chan)
+{
+ return &chan->dev->device;
+}
+
+static inline struct owl_dma_vchan *to_owl_vchan(struct dma_chan *chan)
+{
+ return container_of(chan, struct owl_dma_vchan, vc.chan);
+}
+
+static inline struct owl_dma_txd *to_owl_txd(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct owl_dma_txd, vd.tx);
+}
+
+static inline u32 llc_hw_ctrla(u32 mode, u32 llc_ctl)
+{
+ u32 ctl;
+
+ ctl = BIT_FIELD(mode, 4, 28, 28)
+ | BIT_FIELD(mode, 8, 16, 20)
+ | BIT_FIELD(mode, 4, 8, 16)
+ | BIT_FIELD(mode, 6, 0, 10)
+ | BIT_FIELD(llc_ctl, 2, 10, 8)
+ | BIT_FIELD(llc_ctl, 2, 8, 6);
+
+ return ctl;
+}
+
+static inline u32 llc_hw_ctrlb(u32 int_ctl)
+{
+ u32 ctl;
+
+ ctl = BIT_FIELD(int_ctl, 7, 0, 18);
+
+ return ctl;
+}
+
+static void owl_dma_free_lli(struct owl_dma *od,
+ struct owl_dma_lli *lli)
+{
+ list_del(&lli->node);
+ dma_pool_free(od->lli_pool, lli, lli->phys);
+}
+
+static struct owl_dma_lli *owl_dma_alloc_lli(struct owl_dma *od)
+{
+ struct owl_dma_lli *lli;
+ dma_addr_t phys;
+
+ lli = dma_pool_alloc(od->lli_pool, GFP_NOWAIT, &phys);
+ if (!lli)
+ return NULL;
+
+ INIT_LIST_HEAD(&lli->node);
+ lli->phys = phys;
+
+ return lli;
+}
+
+static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd,
+ struct owl_dma_lli *prev,
+ struct owl_dma_lli *next)
+{
+ list_add_tail(&next->node, &txd->lli_list);
+
+ if (prev) {
+ prev->hw.next_lli = next->phys;
+ prev->hw.ctrla |= llc_hw_ctrla(OWL_DMA_MODE_LME, 0);
+ }
+
+ return next;
+}
+
+static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan,
+ struct owl_dma_lli *lli,
+ dma_addr_t src, dma_addr_t dst,
+ u32 len, enum dma_transfer_direction dir)
+{
+ struct owl_dma_lli_hw *hw = &lli->hw;
+ u32 mode;
+
+ mode = OWL_DMA_MODE_PW(0);
+
+ switch (dir) {
+ case DMA_MEM_TO_MEM:
+ mode |= OWL_DMA_MODE_TS(0) | OWL_DMA_MODE_ST_DCU
+ | OWL_DMA_MODE_DT_DCU | OWL_DMA_MODE_SAM_INC
+ | OWL_DMA_MODE_DAM_INC;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ hw->next_lli = 0; /* One link list by default */
+ hw->saddr = src;
+ hw->daddr = dst;
+
+ hw->fcnt = 1; /* Frame count fixed as 1 */
+ hw->flen = len; /* Max frame length is 1MB */
+ hw->src_stride = 0;
+ hw->dst_stride = 0;
+ hw->ctrla = llc_hw_ctrla(mode,
+ OWL_DMA_LLC_SAV_LOAD_NEXT | OWL_DMA_LLC_DAV_LOAD_NEXT);
+
+ hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK);
+
+ return 0;
+}
+
+static struct owl_dma_pchan *owl_dma_get_pchan(struct owl_dma *od,
+ struct owl_dma_vchan *vchan)
+{
+ struct owl_dma_pchan *pchan;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < od->nr_pchans; i++) {
+ pchan = &od->pchans[i];
+
+ spin_lock_irqsave(&pchan->lock, flags);
+ if (!pchan->vchan) {
+ pchan->vchan = vchan;
+ spin_unlock_irqrestore(&pchan->lock, flags);
+ break;
+ }
+
+ spin_unlock_irqrestore(&pchan->lock, flags);
+ }
+
+ if (i == od->nr_pchans) {
+ /* No physical channel available, cope with it */
+ dev_dbg(od->dma.dev, "no physical channel available");
+ return NULL;
+ }
+
+ return pchan;
+}
+
+static int owl_dma_pchan_busy(struct owl_dma *od, struct owl_dma_pchan *pchan)
+{
+ unsigned int val;
+
+ val = dma_readl(od, OWL_DMA_IDLE_STAT);
+
+ return !(val & (1 << pchan->id));
+}
+
+static void owl_dma_terminate_pchan(struct owl_dma *od,
+ struct owl_dma_pchan *pchan)
+{
+ unsigned long flags;
+ u32 irq_pd;
+
+ pchan_writel(pchan, 0, OWL_DMAX_START);
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+
+ spin_lock_irqsave(&od->lock, flags);
+ dma_update(od->base + OWL_DMA_IRQ_EN0, (1 << pchan->id), false);
+
+ irq_pd = dma_readl(od, OWL_DMA_IRQ_PD0);
+ if (irq_pd & (1 << pchan->id)) {
+ dev_warn(od->dma.dev,
+ "terminating pchan %d that still has pending irq",
+ pchan->id);
+ dma_writel(od, (1 << pchan->id), OWL_DMA_IRQ_PD0);
+ }
+
+ pchan->vchan = NULL;
+
+ spin_unlock_irqrestore(&od->lock, flags);
+}
+
+static void owl_dma_pause_pchan(struct owl_dma_pchan *pchan)
+{
+ pchan_writel(pchan, 1, OWL_DMAX_PAUSE);
+}
+
+static void owl_dma_resume_pchan(struct owl_dma_pchan *pchan)
+{
+ pchan_writel(pchan, 0, OWL_DMAX_PAUSE);
+}
+
+static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma *od = to_owl_dma(vchan->vc.chan.device);
+ struct virt_dma_desc *vd = vchan_next_desc(&vchan->vc);
+ struct owl_dma_pchan *pchan = vchan->pchan;
+ struct owl_dma_txd *txd = to_owl_txd(&vd->tx);
+ struct owl_dma_lli *lli;
+ unsigned long flags;
+ u32 int_ctl;
+
+ list_del(&vd->node);
+
+ vchan->txd = txd;
+
+ /* Wait for channel inactive */
+ while (owl_dma_pchan_busy(od, pchan))
+ cpu_relax();
+
+ lli = list_first_entry(&txd->lli_list,
+ struct owl_dma_lli, node);
+
+ int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK;
+
+ pchan_writel(pchan, OWL_DMA_MODE_LME, OWL_DMAX_MODE);
+ pchan_writel(pchan, OWL_DMA_LLC_SAV_LOAD_NEXT |
+ OWL_DMA_LLC_DAV_LOAD_NEXT, OWL_DMAX_LINKLIST_CTL);
+ pchan_writel(pchan, lli->phys, OWL_DMAX_NEXT_DESCRIPTOR);
+ pchan_writel(pchan, int_ctl, OWL_DMAX_INT_CTL);
+
+ /* Clear IRQ status for this pchan */
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+
+ spin_lock_irqsave(&od->lock, flags);
+
+ dma_update(od->base + OWL_DMA_IRQ_EN0, (1 << pchan->id), true);
+
+ spin_unlock_irqrestore(&od->lock, flags);
+
+ dev_dbg(chan2dev(&vchan->vc.chan), "starting pchan %d\n", pchan->id);
+
+ /* Start DMA transfer for this pchan */
+ pchan_writel(pchan, 0x1, OWL_DMAX_START);
+
+ return 0;
+}
+
+static void owl_dma_phy_free(struct owl_dma *od, struct owl_dma_vchan *vchan)
+{
+ /* Ensure that the physical channel is stopped */
+ owl_dma_terminate_pchan(od, vchan->pchan);
+
+ vchan->pchan = NULL;
+}
+
+static irqreturn_t owl_dma_interrupt(int irq, void *dev_id)
+{
+ struct owl_dma *od = dev_id;
+ struct owl_dma_vchan *vchan;
+ struct owl_dma_pchan *pchan;
+ unsigned long pending;
+ int i;
+ unsigned int global_irq_pending, chan_irq_pending;
+
+ spin_lock(&od->lock);
+
+ pending = dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ /* Clear IRQ status for each pchan */
+ for_each_set_bit(i, &pending, od->nr_pchans) {
+ pchan = &od->pchans[i];
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+ }
+
+ /* Clear pending IRQ */
+ dma_writel(od, pending, OWL_DMA_IRQ_PD0);
+
+ /* Check missed pending IRQ */
+ for (i = 0; i < od->nr_pchans; i++) {
+ pchan = &od->pchans[i];
+ chan_irq_pending = pchan_readl(pchan, OWL_DMAX_INT_CTL) &
+ pchan_readl(pchan, OWL_DMAX_INT_STATUS);
+
+ /* Dummy read to ensure OWL_DMA_IRQ_PD0 value is updated */
+ dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ global_irq_pending = dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ if (chan_irq_pending && !(global_irq_pending & BIT(i))) {
+ dev_dbg(od->dma.dev,
+ "global and channel IRQ pending doesn't match");
+
+ /* Clear IRQ status for this pchan */
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS,
+ 0xff, false);
+
+ /* Update global IRQ pending */
+ pending |= BIT(i);
+ }
+ }
+
+ spin_unlock(&od->lock);
+
+ for_each_set_bit(i, &pending, od->nr_pchans) {
+ struct owl_dma_txd *txd;
+
+ pchan = &od->pchans[i];
+
+ vchan = pchan->vchan;
+ if (!vchan) {
+ dev_warn(od->dma.dev, "no vchan attached on pchan %d\n",
+ pchan->id);
+ continue;
+ }
+
+ spin_lock(&vchan->vc.lock);
+
+ txd = vchan->txd;
+ if (txd) {
+ vchan->txd = NULL;
+
+ vchan_cookie_complete(&txd->vd);
+
+ /*
+ * Start the next descriptor (if any),
+ * otherwise free this channel.
+ */
+ if (vchan_next_desc(&vchan->vc))
+ owl_dma_start_next_txd(vchan);
+ else
+ owl_dma_phy_free(od, vchan);
+ }
+
+ spin_unlock(&vchan->vc.lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void owl_dma_free_txd(struct owl_dma *od, struct owl_dma_txd *txd)
+{
+ struct owl_dma_lli *lli, *_lli;
+
+ if (unlikely(!txd))
+ return;
+
+ list_for_each_entry_safe(lli, _lli, &txd->lli_list, node) {
+ owl_dma_free_lli(od, lli);
+ }
+
+ kfree(txd);
+}
+
+static void owl_dma_desc_free(struct virt_dma_desc *vd)
+{
+ struct owl_dma *od = to_owl_dma(vd->tx.chan->device);
+ struct owl_dma_txd *txd = to_owl_txd(&vd->tx);
+
+ owl_dma_free_txd(od, txd);
+}
+
+static int owl_dma_terminate_all(struct dma_chan *chan)
+{
+ struct owl_dma *od = to_owl_dma(chan->device);
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ if (vchan->pchan)
+ owl_dma_phy_free(od, vchan);
+
+ if (vchan->txd) {
+ owl_dma_desc_free(&vchan->txd->vd);
+ vchan->txd = NULL;
+ }
+
+ vchan_get_all_descriptors(&vchan->vc, &head);
+ vchan_dma_desc_free_list(&vchan->vc, &head);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static int owl_dma_pause(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ owl_dma_pause_pchan(vchan->pchan);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static int owl_dma_resume(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ if (!vchan->pchan && !vchan->txd)
+ return 0;
+
+ dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc);
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ owl_dma_resume_pchan(vchan->pchan);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma_pchan *pchan;
+ struct owl_dma_txd *txd;
+ struct owl_dma_lli *lli;
+ unsigned int next_lli_phy;
+ size_t bytes;
+
+ pchan = vchan->pchan;
+ txd = vchan->txd;
+
+ if (!pchan || !txd)
+ return 0;
+
+ /* Get remain count of current node in link list */
+ bytes = pchan_readl(pchan, OWL_DMAX_REMAIN_CNT);
+
+ /* Loop through the preceding nodes to get total remaining bytes */
+ if (pchan_readl(pchan, OWL_DMAX_MODE) & OWL_DMA_MODE_LME) {
+ next_lli_phy = pchan_readl(pchan, OWL_DMAX_NEXT_DESCRIPTOR);
+ list_for_each_entry(lli, &txd->lli_list, node) {
+ /* Start from the next active node */
+ if (lli->phys == next_lli_phy) {
+ list_for_each_entry(lli, &txd->lli_list, node)
+ bytes += lli->hw.flen;
+ break;
+ }
+ }
+ }
+
+ return bytes;
+}
+
+static enum dma_status owl_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ struct owl_dma_lli *lli;
+ struct virt_dma_desc *vd;
+ struct owl_dma_txd *txd;
+ enum dma_status ret;
+ unsigned long flags;
+ size_t bytes = 0;
+
+ ret = dma_cookie_status(chan, cookie, state);
+ if (ret == DMA_COMPLETE || !state)
+ return ret;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ vd = vchan_find_desc(&vchan->vc, cookie);
+ if (vd) {
+ txd = to_owl_txd(&vd->tx);
+ list_for_each_entry(lli, &txd->lli_list, node)
+ bytes += lli->hw.flen;
+ } else {
+ bytes = owl_dma_getbytes_chan(vchan);
+ }
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ dma_set_residue(state, bytes);
+
+ return ret;
+}
+
+static void owl_dma_phy_alloc_and_start(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma *od = to_owl_dma(vchan->vc.chan.device);
+ struct owl_dma_pchan *pchan;
+
+ pchan = owl_dma_get_pchan(od, vchan);
+ if (!pchan) {
+ dev_dbg(od->dma.dev, "no physical channel available for xfer");
+ return;
+ }
+
+ dev_dbg(od->dma.dev, "allocated pchan %d", pchan->id);
+
+ vchan->pchan = pchan;
+ owl_dma_start_next_txd(vchan);
+}
+
+static void owl_dma_issue_pending(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+ if (vchan_issue_pending(&vchan->vc)) {
+ if (!vchan->pchan)
+ owl_dma_phy_alloc_and_start(vchan);
+ }
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *owl_dma_prep_memcpy(
+ struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct owl_dma *od = to_owl_dma(chan->device);
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ struct owl_dma_txd *txd;
+ struct owl_dma_lli *lli, *prev = NULL;
+ size_t offset, bytes;
+ int ret;
+
+ if (!len)
+ return NULL;
+
+ txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
+ if (!txd)
+ return NULL;
+
+ INIT_LIST_HEAD(&txd->lli_list);
+
+ /* Process the transfer as frame by frame */
+ for (offset = 0; offset < len; offset += bytes) {
+ lli = owl_dma_alloc_lli(od);
+ if (!lli) {
+ dev_warn(chan2dev(chan), "failed to allocate lli");
+ goto err_txd_free;
+ }
+
+ bytes = min_t(size_t, (len - offset), OWL_DMA_FRAME_MAX_LENGTH);
+
+ ret = owl_dma_cfg_lli(vchan, lli, src + offset, dst + offset,
+ bytes, DMA_MEM_TO_MEM);
+ if (ret) {
+ dev_warn(chan2dev(chan), "failed to config lli");
+ goto err_txd_free;
+ }
+
+ prev = owl_dma_add_lli(txd, prev, lli);
+ }
+
+ return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+
+err_txd_free:
+ owl_dma_free_txd(od, txd);
+ return NULL;
+}
+
+static void owl_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+
+ /* Ensure all queued descriptors are freed */
+ vchan_free_chan_resources(&vchan->vc);
+}
+
+static inline void owl_dma_free(struct owl_dma *od)
+{
+ struct owl_dma_vchan *vchan = NULL;
+ struct owl_dma_vchan *next;
+
+ list_for_each_entry_safe(vchan,
+ next, &od->dma.channels, vc.chan.device_node) {
+ list_del(&vchan->vc.chan.device_node);
+ tasklet_kill(&vchan->vc.task);
+ }
+}
+
+static int owl_dma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct owl_dma *od;
+ struct resource *res;
+ int ret, i, irq, nr_channels, nr_requests;
+
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ od->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(od->base))
+ return PTR_ERR(od->base);
+
+ ret = of_property_read_u32(np, "dma-channels", &nr_channels);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get dma-channels.\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "dma-requests", &nr_requests);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get dma-requests.\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "dma-channels %d, dma-requests %d\n",
+ nr_channels, nr_requests);
+
+ od->nr_pchans = nr_channels;
+ od->nr_vchans = nr_requests;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, od);
+ spin_lock_init(&od->lock);
+
+ dma_cap_set(DMA_MEMCPY, od->dma.cap_mask);
+
+ od->dma.dev = &pdev->dev;
+ od->dma.device_free_chan_resources = owl_dma_free_chan_resources;
+ od->dma.device_tx_status = owl_dma_tx_status;
+ od->dma.device_issue_pending = owl_dma_issue_pending;
+ od->dma.device_prep_dma_memcpy = owl_dma_prep_memcpy;
+ od->dma.device_pause = owl_dma_pause;
+ od->dma.device_resume = owl_dma_resume;
+ od->dma.device_terminate_all = owl_dma_terminate_all;
+ od->dma.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ od->dma.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ od->dma.directions = BIT(DMA_MEM_TO_MEM);
+ od->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ INIT_LIST_HEAD(&od->dma.channels);
+
+ od->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(od->clk)) {
+ dev_err(&pdev->dev, "unable to get clock");
+ return PTR_ERR(od->clk);
+ }
+
+ /*
+ * Eventhough the DMA controller is capable of generating 4
+ * IRQ's for DMA priority feature, we only use 1 IRQ for
+ * simplification.
+ */
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, irq, owl_dma_interrupt, 0,
+ dev_name(&pdev->dev), od);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request IRQ\n");
+ return ret;
+ }
+
+ /* Init physical channel */
+ od->pchans = devm_kcalloc(&pdev->dev, od->nr_pchans,
+ sizeof(struct owl_dma_pchan), GFP_KERNEL);
+ if (!od->pchans)
+ return -ENOMEM;
+
+ for (i = 0; i < od->nr_pchans; i++) {
+ struct owl_dma_pchan *pchan = &od->pchans[i];
+
+ pchan->id = i;
+ pchan->base = od->base + OWL_DMA_CHAN_BASE(i);
+ }
+
+ /* Init virtual channel */
+ od->vchans = devm_kcalloc(&pdev->dev, od->nr_vchans,
+ sizeof(struct owl_dma_vchan), GFP_KERNEL);
+ if (!od->vchans)
+ return -ENOMEM;
+
+ for (i = 0; i < od->nr_vchans; i++) {
+ struct owl_dma_vchan *vchan = &od->vchans[i];
+
+ vchan->vc.desc_free = owl_dma_desc_free;
+ vchan_init(&vchan->vc, &od->dma);
+ }
+
+ /* Create a pool of consistent memory blocks for hardware descriptors */
+ od->lli_pool = dma_pool_create(dev_name(od->dma.dev), od->dma.dev,
+ sizeof(struct owl_dma_lli),
+ __alignof__(struct owl_dma_lli),
+ 0);
+ if (!od->lli_pool) {
+ dev_err(&pdev->dev, "unable to allocate DMA descriptor pool\n");
+ return -ENOMEM;
+ }
+
+ clk_prepare_enable(od->clk);
+
+ ret = dma_async_device_register(&od->dma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DMA engine device\n");
+ goto err_pool_free;
+ }
+
+ return 0;
+
+err_pool_free:
+ clk_disable_unprepare(od->clk);
+ dma_pool_destroy(od->lli_pool);
+
+ return ret;
+}
+
+static int owl_dma_remove(struct platform_device *pdev)
+{
+ struct owl_dma *od = platform_get_drvdata(pdev);
+
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&od->dma);
+
+ /* mask all interrupts for this execution environment */
+ dma_writel(od, 0x0, OWL_DMA_IRQ_EN0);
+ owl_dma_free(od);
+
+ clk_disable_unprepare(od->clk);
+
+ return 0;
+}
+
+static const struct of_device_id owl_dma_match[] = {
+ { .compatible = "actions,s900-dma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, owl_dma_match);
+
+static struct platform_driver owl_dma_driver = {
+ .probe = owl_dma_probe,
+ .remove = owl_dma_remove,
+ .driver = {
+ .name = "dma-owl",
+ .of_match_table = of_match_ptr(owl_dma_match),
+ },
+};
+
+static int owl_dma_init(void)
+{
+ return platform_driver_register(&owl_dma_driver);
+}
+subsys_initcall(owl_dma_init);
+
+static void __exit owl_dma_exit(void)
+{
+ platform_driver_unregister(&owl_dma_driver);
+}
+module_exit(owl_dma_exit);
+
+MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("Actions Semi Owl SoCs DMA driver");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* [2/4] arm64: dts: actions: Add Actions Semi S900 DMA Controller
From: Manivannan Sadhasivam @ 2018-07-18 20:07 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add DMA controller node for Actions Semi S900 SoC.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
arch/arm64/boot/dts/actions/s900.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm64/boot/dts/actions/s900.dtsi b/arch/arm64/boot/dts/actions/s900.dtsi
index 7ae8b931f000..2e8178e50832 100644
--- a/arch/arm64/boot/dts/actions/s900.dtsi
+++ b/arch/arm64/boot/dts/actions/s900.dtsi
@@ -191,6 +191,19 @@
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
};
+ dma: dma-controller@e0260000 {
+ compatible = "actions,s900-dma";
+ reg = <0x0 0xe0260000 0x0 0x1000>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ #dma-cells = <1>;
+ dma-channels = <12>;
+ dma-requests = <46>;
+ clocks = <&cmu CLK_DMAC>;
+ };
+
timer: timer@e0228000 {
compatible = "actions,s900-timer";
reg = <0x0 0xe0228000 0x0 0x8000>;
^ permalink raw reply related
* [1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Manivannan Sadhasivam @ 2018-07-18 20:07 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add devicetree binding for Actions Semi Owl SoCs DMA controller.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
.../devicetree/bindings/dma/owl-dma.txt | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/owl-dma.txt
diff --git a/Documentation/devicetree/bindings/dma/owl-dma.txt b/Documentation/devicetree/bindings/dma/owl-dma.txt
new file mode 100644
index 000000000000..dd6ce237b216
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/owl-dma.txt
@@ -0,0 +1,46 @@
+* Actions Semi Owl SoCs DMA controller
+
+This binding follows the generic DMA bindings defined in dma.txt.
+
+Required properties:
+- compatible: Should be "actions,s900-dma".
+- reg: Should contain DMA registers location and length.
+- interrupts: Should contain 4 interrupts shared by all channel.
+- #dma-cells: Must be <1>. Used to represent the number of integer
+ cells in the dmas property of client device.
+- dma-channels: Physical channels supported.
+- dma-requests: Virtual channels supported.
+- clocks: Phandle and Specifier of the clock feeding the DMA controller.
+
+Example:
+
+Controller:
+ dma: dma-controller@e0260000 {
+ compatible = "actions,s900-dma";
+ reg = <0x0 0xe0260000 0x0 0x1000>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ #dma-cells = <1>;
+ dma-channels = <12>;
+ dma-requests = <46>;
+ clocks = <&clock CLK_DMAC>;
+ };
+
+Client:
+
+DMA clients connected to the Actions Semi Owl SoCs DMA controller must
+use the format described in the dma.txt file, using a two-cell specifier
+for each channel.
+
+The two cells in order are:
+1. A phandle pointing to the DMA controller.
+2. The channel id.
+
+uart5: serial@e012a000 {
+ ...
+ dma-names = "tx", "rx";
+ dmas = <&dma 26>, <&dma 27>;
+ ...
+};
^ permalink raw reply related
* [RFC] dmaengine: Add metadat_ops for dma_async_tx_descriptor
From: Peter Ujfalusi @ 2018-07-18 10:06 UTC (permalink / raw)
To: Vinod
Cc: radheys, vinod.koul, lars, michal.simek, linux-kernel, dmaengine,
dan.j.williams, appanad, linux-arm-kernel
Hi Vinod,
On 2018-07-10 08:52, Vinod wrote:
>
> Hey Peter,
>
> Sorry for late response on this..
No problem, I was away myself also...
> On 01-06-18, 13:24, Peter Ujfalusi wrote:
>> If the DMA supports per descriptor metadata it can implement the attach,
>> get_ptr/set_len callbacks.
>>
>> Client drivers must only use either attach or get_ptr/set_len to avoid
>> miss configuration.
>>
>> Wrappers are also added for the metadata_ops:
>> dmaengine_desc_attach_metadata()
>> dmaengine_desc_get_metadata_ptr()
>> dmaengine_desc_set_metadata_len()
>>
>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>> ---
>> Hi,
>>
>> since attachments are bouncing back, I send the patch separately
>>
>> Regards,
>> Peter
>>
>> include/linux/dmaengine.h | 50 +++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 50 insertions(+)
>>
>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>> index 51fbb861e84b..ac42ace36aa3 100644
>> --- a/include/linux/dmaengine.h
>> +++ b/include/linux/dmaengine.h
>> @@ -491,6 +491,18 @@ struct dmaengine_unmap_data {
>> dma_addr_t addr[0];
>> };
>>
>> +struct dma_async_tx_descriptor;
>> +
>> +struct dma_descriptor_metadata_ops {
>> + int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
>> + size_t len);
>
> How does one detach?
I have not thought about detach, but clients can just attach NULL I guess.
> When should the client free up the memory, IOW when
> does dma driver drop ref to data.
The metadata is for the descriptor so the DMA driver might want to
access to it while the descriptor is valid.
Typically clients can free up their metadata storage after the dma
completion callback. On DEV_TO_MEM the metadata is going to be placed in
the provided buffer when the transfer is completed.
>
>> +
>> + void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
>> + size_t *payload_len, size_t *max_len);
>
> so what is this supposed to do..?
My issue with the attach in general is that it will need additional
memcpy to move the metadata from/to the client buffer to it's place.
With get_ptr the client can get the pointer to the actual place where
the metadata resides and modify/read it in place w/o memcpy.
I know, I know... We need to trust the clients, but with high throughput
peripherals the memcpy is taxing.
>
>> + int (*set_len)(struct dma_async_tx_descriptor *desc,
>> + size_t payload_len);
>
> attach already has length, so how does this help?
So, DMA drivers can implement either or both:
1. attach()
2. get_ptr() / set_len()
Clients must not mix the two way of handling the metadata.
The set_len() is intended to tell the DMA driver the client provided
metadata size (in MEM_TO_DEV case mostly).
MEM_TO_DEV flow on client side:
get_ptr()
fill in the metadata to the pointer (not exceeding max_len)
set_len() to tell the DMA driver the amount of valid bytes written
DEV_TO_MEM flow on client side:
In the completion callback, get_ptr()
the metadata is payload_len bytes and can be accessed in the return pointer.
BTW: The driver which is going to need this is now accessible in public:
https://git.ti.com/ti-linux-kernel/ti-linux-kernel/trees/ti-linux-4.14.y/drivers/dma/ti
or in my wip tree:
https://github.com/omap-audio/linux-audio/tree/peter/ti-linux-4.14.y/wip/drivers/dma/ti
prefixed with k3-*
>
>> +};
>> +
>> /**
>> * struct dma_async_tx_descriptor - async transaction descriptor
>> * ---dma generic offload fields---
>> @@ -520,6 +532,7 @@ struct dma_async_tx_descriptor {
>> dma_async_tx_callback_result callback_result;
>> void *callback_param;
>> struct dmaengine_unmap_data *unmap;
>> + struct dma_descriptor_metadata_ops *metadata_ops;
>> #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
>> struct dma_async_tx_descriptor *next;
>> struct dma_async_tx_descriptor *parent;
>> @@ -932,6 +945,43 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy(
>> len, flags);
>> }
>>
>> +static inline int dmaengine_desc_attach_metadata(
>> + struct dma_async_tx_descriptor *desc, void *data, size_t len)
>> +{
>> + if (!desc)
>> + return 0;
>> +
>> + if (!desc->metadata_ops || !desc->metadata_ops->attach)
>> + return -ENOTSUPP;
>> +
>> + return desc->metadata_ops->attach(desc, data, len);
>> +}
>> +
>> +static inline void *dmaengine_desc_get_metadata_ptr(
>> + struct dma_async_tx_descriptor *desc, size_t *payload_len,
>> + size_t *max_len)
>> +{
>> + if (!desc)
>> + return NULL;
>> +
>> + if (!desc->metadata_ops || !desc->metadata_ops->get_ptr)
>> + return ERR_PTR(-ENOTSUPP);
>> +
>> + return desc->metadata_ops->get_ptr(desc, payload_len, max_len);
>> +}
>> +
>> +static inline int dmaengine_desc_set_metadata_len(
>> + struct dma_async_tx_descriptor *desc, size_t payload_len)
>> +{
>> + if (!desc)
>> + return 0;
>> +
>> + if (!desc->metadata_ops || !desc->metadata_ops->set_len)
>> + return -ENOTSUPP;
>> +
>> + return desc->metadata_ops->set_len(desc, payload_len);
>> +}
>> +
>> /**
>> * dmaengine_terminate_all() - Terminate all active DMA transfers
>> * @chan: The channel for which to terminate the transfers
>> --
>> Peter
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
- Péter
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* dmaengine: dma_request_chan_by_mask() to handle deferred probing
From: Peter Ujfalusi @ 2018-07-18 9:29 UTC (permalink / raw)
To: vkoul, dan.j.williams; +Cc: dmaengine, linux-kernel
If there are no DMA devices registered yet, return with EPROBE_DEFER
similarly to the case when requesting a slave channel.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
drivers/dma/dmaengine.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index d8fc7b58e506..f62e59e1a264 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -768,8 +768,14 @@ struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask)
return ERR_PTR(-ENODEV);
chan = __dma_request_channel(mask, NULL, NULL);
- if (!chan)
- chan = ERR_PTR(-ENODEV);
+ if (!chan) {
+ mutex_lock(&dma_list_mutex);
+ if (list_empty(&dma_device_list))
+ chan = ERR_PTR(-EPROBE_DEFER);
+ else
+ chan = ERR_PTR(-ENODEV);
+ mutex_unlock(&dma_list_mutex);
+ }
return chan;
}
^ permalink raw reply related
* [05/14] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Vinod Koul @ 2018-07-18 5:27 UTC (permalink / raw)
To: Rob Herring
Cc: Paul Cercueil, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel, Mathieu Malaterre,
Daniel Silsby, open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM,
devicetree, linux-kernel@vger.kernel.org, Linux-MIPS
On 17-07-18, 11:40, Rob Herring wrote:
> On Tue, Jul 17, 2018 at 9:34 AM Vinod <vkoul@kernel.org> wrote:
> >
> > On 16-07-18, 15:33, Rob Herring wrote:
> > > On Mon, Jul 09, 2018 at 10:42:26PM +0530, Vinod wrote:
> > > > On 03-07-18, 14:32, Paul Cercueil wrote:
> > > >
> > > > > enum jz_version {
> > > > > + ID_JZ4740,
> > > > > ID_JZ4770,
> > > > > ID_JZ4780,
> > > > > };
> > > > > @@ -247,6 +248,7 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
> > > > > }
> > > > >
> > > > > static const unsigned int jz4780_dma_ord_max[] = {
> > > > > + [ID_JZ4740] = 5,
> > > > > [ID_JZ4770] = 6,
> > > > > [ID_JZ4780] = 7,
> > > > > };
> > > > > @@ -801,11 +803,13 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
> > > > > }
> > > > >
> > > > > static const unsigned int jz4780_dma_nb_channels[] = {
> > > > > + [ID_JZ4740] = 6,
> > > > > [ID_JZ4770] = 6,
> > > > > [ID_JZ4780] = 32,
> > > > > };
> > > >
> > > > I feel these should be done away with if we describe hardware in DT
> > >
> > > The compatible property can imply things like this.
> >
> > So what is the general recommendation, let DT describe hardware
> > including version delta or use compatible to code that in driver?
>
> Compatible is the version. Looking at the above, the version or ID
> isn't even stable.
>
> > Is it documented anywhere?
>
> Not really. It's a judgment call generally. Maybe # of DMA channels
> should be a property because that is something most controllers have.
> But you really have to define the property up front, not when the 2nd
> version of h/w shows up with different properties.
>
> To start defining guidelines, a couple of things come to mind:
>
> - Define properties for parameters that vary from board to board (for one SoC).
> - You can't add new required properties to existing bindings, so the
> not present default must work for all existing compatibles (or you
> need per compatible driver data).
> - Bugs/quirks/errata should be handled by compatible, not adding a
> property. Because bugs should be fixable without a dtb update and only
> a kernel update.
Sounds good to me, thanks for the guide.
^ permalink raw reply
* [05/14] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Rob Herring @ 2018-07-17 17:40 UTC (permalink / raw)
To: Vinod
Cc: Paul Cercueil, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel, Mathieu Malaterre,
Daniel Silsby, open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM,
devicetree, linux-kernel@vger.kernel.org, Linux-MIPS
On Tue, Jul 17, 2018 at 9:34 AM Vinod <vkoul@kernel.org> wrote:
>
> On 16-07-18, 15:33, Rob Herring wrote:
> > On Mon, Jul 09, 2018 at 10:42:26PM +0530, Vinod wrote:
> > > On 03-07-18, 14:32, Paul Cercueil wrote:
> > >
> > > > enum jz_version {
> > > > + ID_JZ4740,
> > > > ID_JZ4770,
> > > > ID_JZ4780,
> > > > };
> > > > @@ -247,6 +248,7 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
> > > > }
> > > >
> > > > static const unsigned int jz4780_dma_ord_max[] = {
> > > > + [ID_JZ4740] = 5,
> > > > [ID_JZ4770] = 6,
> > > > [ID_JZ4780] = 7,
> > > > };
> > > > @@ -801,11 +803,13 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
> > > > }
> > > >
> > > > static const unsigned int jz4780_dma_nb_channels[] = {
> > > > + [ID_JZ4740] = 6,
> > > > [ID_JZ4770] = 6,
> > > > [ID_JZ4780] = 32,
> > > > };
> > >
> > > I feel these should be done away with if we describe hardware in DT
> >
> > The compatible property can imply things like this.
>
> So what is the general recommendation, let DT describe hardware
> including version delta or use compatible to code that in driver?
Compatible is the version. Looking at the above, the version or ID
isn't even stable.
> Is it documented anywhere?
Not really. It's a judgment call generally. Maybe # of DMA channels
should be a property because that is something most controllers have.
But you really have to define the property up front, not when the 2nd
version of h/w shows up with different properties.
To start defining guidelines, a couple of things come to mind:
- Define properties for parameters that vary from board to board (for one SoC).
- You can't add new required properties to existing bindings, so the
not present default must work for all existing compatibles (or you
need per compatible driver data).
- Bugs/quirks/errata should be handled by compatible, not adding a
property. Because bugs should be fixable without a dtb update and only
a kernel update.
Rob
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [05/14] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Vinod Koul @ 2018-07-17 15:34 UTC (permalink / raw)
To: Rob Herring
Cc: Paul Cercueil, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel, Mathieu Malaterre,
Daniel Silsby, dmaengine, devicetree, linux-kernel, linux-mips
On 16-07-18, 15:33, Rob Herring wrote:
> On Mon, Jul 09, 2018 at 10:42:26PM +0530, Vinod wrote:
> > On 03-07-18, 14:32, Paul Cercueil wrote:
> >
> > > enum jz_version {
> > > + ID_JZ4740,
> > > ID_JZ4770,
> > > ID_JZ4780,
> > > };
> > > @@ -247,6 +248,7 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
> > > }
> > >
> > > static const unsigned int jz4780_dma_ord_max[] = {
> > > + [ID_JZ4740] = 5,
> > > [ID_JZ4770] = 6,
> > > [ID_JZ4780] = 7,
> > > };
> > > @@ -801,11 +803,13 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
> > > }
> > >
> > > static const unsigned int jz4780_dma_nb_channels[] = {
> > > + [ID_JZ4740] = 6,
> > > [ID_JZ4770] = 6,
> > > [ID_JZ4780] = 32,
> > > };
> >
> > I feel these should be done away with if we describe hardware in DT
>
> The compatible property can imply things like this.
So what is the general recommendation, let DT describe hardware
including version delta or use compatible to code that in driver?
Is it documented anywhere?
^ permalink raw reply
* [05/14] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Paul Cercueil @ 2018-07-17 11:00 UTC (permalink / raw)
To: Rob Herring
Cc: Vinod, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
Hi,
Le lun. 16 juil. 2018 à 23:33, Rob Herring <robh@kernel.org> a écrit :
> On Mon, Jul 09, 2018 at 10:42:26PM +0530, Vinod wrote:
>> On 03-07-18, 14:32, Paul Cercueil wrote:
>>
>> > enum jz_version {
>> > + ID_JZ4740,
>> > ID_JZ4770,
>> > ID_JZ4780,
>> > };
>> > @@ -247,6 +248,7 @@ static void jz4780_dma_desc_free(struct
>> virt_dma_desc *vdesc)
>> > }
>> >
>> > static const unsigned int jz4780_dma_ord_max[] = {
>> > + [ID_JZ4740] = 5,
>> > [ID_JZ4770] = 6,
>> > [ID_JZ4780] = 7,
>> > };
>> > @@ -801,11 +803,13 @@ static struct dma_chan
>> *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
>> > }
>> >
>> > static const unsigned int jz4780_dma_nb_channels[] = {
>> > + [ID_JZ4740] = 6,
>> > [ID_JZ4770] = 6,
>> > [ID_JZ4780] = 32,
>> > };
>>
>> I feel these should be done away with if we describe hardware in DT
>
> The compatible property can imply things like this.
>
> But how this is structured is a bit strange. Normally you have a per
> compatible struct with these as elements and the compatible matching
> selects the struct.
You're right, I'll change that.
>>
>> >
>> > static const struct of_device_id jz4780_dma_dt_match[] = {
>> > + { .compatible = "ingenic,jz4740-dma", .data = (void *)ID_JZ4740
>> },
>>
>> adding .compatible should be the only thing required, if at all for
>> this
>> addition :)
>>
>> --
>> ~Vinod
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* dmaengine: pl330: fix irq race with terminate_all
From: John Keeping @ 2018-07-17 10:48 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Dan Williams, linux-kernel, John Keeping
In pl330_update() when checking if a channel has been aborted, the
channel's lock is not taken, only the overall pl330_dmac lock. But in
pl330_terminate_all() the aborted flag (req_running==-1) is set under
the channel lock and not the pl330_dmac lock.
With threaded interrupts, this leads to a potential race:
pl330_terminate_all pl330_update
------------------- ------------
lock channel
entry
lock pl330
_stop channel
unlock pl330
lock pl330
check req_running != -1
req_running = -1
_start channel
Signed-off-by: John Keeping <john@metanate.com>
---
drivers/dma/pl330.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 370df2e74ddd..88750a34e859 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2256,13 +2256,14 @@ static int pl330_terminate_all(struct dma_chan *chan)
pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
+
spin_lock(&pl330->lock);
_stop(pch->thread);
- spin_unlock(&pl330->lock);
-
pch->thread->req[0].desc = NULL;
pch->thread->req[1].desc = NULL;
pch->thread->req_running = -1;
+ spin_unlock(&pl330->lock);
+
power_down = pch->active;
pch->active = false;
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox