diff mbox

[v3,7/8] dmaengine: dw: add support of iDMA 32-bit hardware

Message ID 20170111151812.1037-8-andriy.shevchenko@linux.intel.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Andy Shevchenko Jan. 11, 2017, 3:18 p.m. UTC
iDMA 32-bit is Intel designed DMA controller that behaves like Synopsys
Designware DMA. This patch adds a support of the new Intel hardware.

Due to iDMA 32-bit has no autoconfiguration the platform code must provide a
platform data to dw_dma_probe().

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/dma/dw/core.c                | 94 ++++++++++++++++++++++++++++++++----
 include/linux/platform_data/dma-dw.h |  2 +
 2 files changed, 87 insertions(+), 9 deletions(-)

Comments

Vinod Koul Jan. 13, 2017, 12:35 p.m. UTC | #1
> +static void idma32_fifo_partition(struct dw_dma *dw)
> +{
> +	u64 value = IDMA32C_FP_PSIZE_CH0(128) | IDMA32C_FP_PSIZE_CH1(128) |
> +		    IDMA32C_FP_UPDATE;
> +	u64 fifo_partition = 0;
> +
> +	if (!dw->pdata->is_idma32)
> +		return;
> +
> +	/* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
> +	fifo_partition |= value << 0;
> +
> +	/* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
> +	fifo_partition |= value << 32;
> +
> +	/* Program FIFO Partition registers - 128 bytes for each channel */
> +	idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
> +	idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
> +}

Don't recall what was this about, care to detail this one..
Andy Shevchenko Jan. 13, 2017, 2:14 p.m. UTC | #2
On Fri, 2017-01-13 at 18:05 +0530, Vinod Koul wrote:
>  
> > +static void idma32_fifo_partition(struct dw_dma *dw)
> > +{
> > +	u64 value = IDMA32C_FP_PSIZE_CH0(128) |
> > IDMA32C_FP_PSIZE_CH1(128) |
> > +		    IDMA32C_FP_UPDATE;
> > +	u64 fifo_partition = 0;
> > +
> > +	if (!dw->pdata->is_idma32)
> > +		return;
> > +
> > +	/* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
> > +	fifo_partition |= value << 0;
> > +
> > +	/* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
> > +	fifo_partition |= value << 32;
> > +
> > +	/* Program FIFO Partition registers - 128 bytes for each
> > channel */
> > +	idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
> > +	idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
> > +}
> 
> Don't recall what was this about, care to detail this one..

By default full FIFO (1024 bytes) is assigned to channel 0.

By programming these registers we setup equal parts of FIFO. More
detailed it's described in SC DMA HAS, IIRC.

Should I put this to commit message or as comment to the function?
Vinod Koul Jan. 17, 2017, 4:40 a.m. UTC | #3
On Fri, Jan 13, 2017 at 04:14:40PM +0200, Andy Shevchenko wrote:
> On Fri, 2017-01-13 at 18:05 +0530, Vinod Koul wrote:
> >  
> > > +static void idma32_fifo_partition(struct dw_dma *dw)
> > > +{
> > > +	u64 value = IDMA32C_FP_PSIZE_CH0(128) |
> > > IDMA32C_FP_PSIZE_CH1(128) |
> > > +		    IDMA32C_FP_UPDATE;
> > > +	u64 fifo_partition = 0;
> > > +
> > > +	if (!dw->pdata->is_idma32)
> > > +		return;
> > > +
> > > +	/* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
> > > +	fifo_partition |= value << 0;
> > > +
> > > +	/* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
> > > +	fifo_partition |= value << 32;
> > > +
> > > +	/* Program FIFO Partition registers - 128 bytes for each
> > > channel */
> > > +	idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
> > > +	idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
> > > +}
> > 
> > Don't recall what was this about, care to detail this one..
> 
> By default full FIFO (1024 bytes) is assigned to channel 0.

Ah yes :)

> By programming these registers we setup equal parts of FIFO. More
> detailed it's described in SC DMA HAS, IIRC.
> 
> Should I put this to commit message or as comment to the function?

That would be great, in both please.
diff mbox

Patch

diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 75a0d4563a61..cdfef067a04e 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -138,16 +138,32 @@  static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
 	dwc->descs_allocated--;
 }
 
-static void dwc_initialize(struct dw_dma_chan *dwc)
+static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc)
+{
+	u32 cfghi = 0;
+	u32 cfglo = 0;
+
+	/* Set default burst alignment */
+	cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN;
+
+	/* Low 4 bits of the request lines */
+	cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf);
+	cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf);
+
+	/* Request line extension (2 bits) */
+	cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3);
+	cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3);
+
+	channel_writel(dwc, CFG_LO, cfglo);
+	channel_writel(dwc, CFG_HI, cfghi);
+}
+
+static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc)
 {
-	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
 	u32 cfghi = DWC_CFGH_FIFO_MODE;
 	u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
 	bool hs_polarity = dwc->dws.hs_polarity;
 
-	if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
-		return;
-
 	cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
 	cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
 
@@ -156,6 +172,19 @@  static void dwc_initialize(struct dw_dma_chan *dwc)
 
 	channel_writel(dwc, CFG_LO, cfglo);
 	channel_writel(dwc, CFG_HI, cfghi);
+}
+
+static void dwc_initialize(struct dw_dma_chan *dwc)
+{
+	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
+
+	if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
+		return;
+
+	if (dw->pdata->is_idma32)
+		dwc_initialize_chan_idma32(dwc);
+	else
+		dwc_initialize_chan_dw(dwc);
 
 	/* Enable interrupts */
 	channel_set_bit(dw, MASK.XFER, dwc->mask);
@@ -187,8 +216,13 @@  static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc)
 static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes,
 			  unsigned int width, size_t *len)
 {
+	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
 	u32 block;
 
+	/* Always in bytes for iDMA 32-bit */
+	if (dw->pdata->is_idma32)
+		width = 0;
+
 	if ((bytes >> width) > dwc->block_size) {
 		block = dwc->block_size;
 		*len = block << width;
@@ -202,6 +236,11 @@  static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes,
 
 static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
 {
+	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
+
+	if (dw->pdata->is_idma32)
+		return IDMA32C_CTLH_BLOCK_TS(block);
+
 	return DWC_CTLH_BLOCK_TS(block) << width;
 }
 
@@ -915,6 +954,7 @@  static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 {
 	struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
 	struct dma_slave_config *sc = &dwc->dma_sconfig;
+	struct dw_dma *dw = to_dw_dma(chan->device);
 	/*
 	 * Fix sconfig's burst size according to dw_dmac. We need to convert
 	 * them as:
@@ -922,7 +962,7 @@  static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 	 *
 	 * NOTE: burst size 2 is not supported by DesignWare controller.
 	 */
-	u32 s = 2;
+	u32 s = dw->pdata->is_idma32 ? 1 : 2;
 
 	/* Check if chan will be configured for slave transfers */
 	if (!is_slave_direction(sconfig->direction))
@@ -937,12 +977,19 @@  static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 	return 0;
 }
 
-static void dwc_chan_pause(struct dw_dma_chan *dwc)
+static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain)
 {
+	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
 	unsigned int		count = 20;	/* timeout iterations */
 	u32			cfglo;
 
 	cfglo = channel_readl(dwc, CFG_LO);
+	if (dw->pdata->is_idma32) {
+		if (drain)
+			cfglo |= IDMA32C_CFGL_CH_DRAIN;
+		else
+			cfglo &= ~IDMA32C_CFGL_CH_DRAIN;
+	}
 	channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
 	while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--)
 		udelay(2);
@@ -956,7 +1003,7 @@  static int dwc_pause(struct dma_chan *chan)
 	unsigned long		flags;
 
 	spin_lock_irqsave(&dwc->lock, flags);
-	dwc_chan_pause(dwc);
+	dwc_chan_pause(dwc, false);
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	return 0;
@@ -998,6 +1045,8 @@  static int dwc_terminate_all(struct dma_chan *chan)
 
 	clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
 
+	dwc_chan_pause(dwc, true);
+
 	dwc_chan_disable(dw, dwc);
 
 	dwc_chan_resume(dwc);
@@ -1090,6 +1139,26 @@  static void dwc_issue_pending(struct dma_chan *chan)
 
 /*----------------------------------------------------------------------*/
 
+static void idma32_fifo_partition(struct dw_dma *dw)
+{
+	u64 value = IDMA32C_FP_PSIZE_CH0(128) | IDMA32C_FP_PSIZE_CH1(128) |
+		    IDMA32C_FP_UPDATE;
+	u64 fifo_partition = 0;
+
+	if (!dw->pdata->is_idma32)
+		return;
+
+	/* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
+	fifo_partition |= value << 0;
+
+	/* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
+	fifo_partition |= value << 32;
+
+	/* Program FIFO Partition registers - 128 bytes for each channel */
+	idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
+	idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
+}
+
 static void dw_dma_off(struct dw_dma *dw)
 {
 	unsigned int i;
@@ -1509,8 +1578,13 @@  int dw_dma_probe(struct dw_dma_chip *chip)
 	/* Force dma off, just in case */
 	dw_dma_off(dw);
 
+	idma32_fifo_partition(dw);
+
 	/* Device and instance ID for IRQ and DMA pool */
-	snprintf(dw->name, sizeof(dw->name), "dw_dmac%d", chip->id);
+	if (pdata->is_idma32)
+		snprintf(dw->name, sizeof(dw->name), "idma32%d", chip->id);
+	else
+		snprintf(dw->name, sizeof(dw->name), "dw_dmac%d", chip->id);
 
 	/* Create a pool of consistent memory blocks for hardware descriptors */
 	dw->desc_pool = dmam_pool_create(dw->name, chip->dev,
@@ -1673,6 +1747,8 @@  int dw_dma_enable(struct dw_dma_chip *chip)
 {
 	struct dw_dma *dw = chip->dw;
 
+	idma32_fifo_partition(dw);
+
 	dw_dma_on(dw);
 	return 0;
 }
diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h
index e69e415d0d98..896cb71a382c 100644
--- a/include/linux/platform_data/dma-dw.h
+++ b/include/linux/platform_data/dma-dw.h
@@ -41,6 +41,7 @@  struct dw_dma_slave {
  * @is_private: The device channels should be marked as private and not for
  *	by the general purpose DMA channel allocator.
  * @is_memcpy: The device channels do support memory-to-memory transfers.
+ * @is_idma32: The type of the DMA controller is iDMA32
  * @chan_allocation_order: Allocate channels starting from 0 or 7
  * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0.
  * @block_size: Maximum block size supported by the controller
@@ -53,6 +54,7 @@  struct dw_dma_platform_data {
 	unsigned int	nr_channels;
 	bool		is_private;
 	bool		is_memcpy;
+	bool		is_idma32;
 #define CHAN_ALLOCATION_ASCENDING	0	/* zero to seven */
 #define CHAN_ALLOCATION_DESCENDING	1	/* seven to zero */
 	unsigned char	chan_allocation_order;