Commit 306a63be authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'dmaengine-fix-4.6-rc4' of git://git.infradead.org/users/vkoul/slave-dma

Pull dmaengine fixes from Vinod Koul:
 "This time we have some odd fixes in hsu, edma, omap and xilinx.

  Usual fixes and nothing special"

* tag 'dmaengine-fix-4.6-rc4' of git://git.infradead.org/users/vkoul/slave-dma:
  dmaengine: dw: fix master selection
  dmaengine: edma: special case slot limit workaround
  dmaengine: edma: Remove dynamic TPTC power management feature
  dmaengine: vdma: don't crash when bad channel is requested
  dmaengine: omap-dma: Do not suppress interrupts for memcpy
  dmaengine: omap-dma: Fix polled channel completion detection and handling
  dmaengine: hsu: correct use of channel status register
  dmaengine: hsu: correct residue calculation of active descriptor
  dmaengine: hsu: set HSU_CH_MTSR to memory width
parents ac82a57a 956e6c8e
Loading
Loading
Loading
Loading
+19 −15
Original line number Diff line number Diff line
@@ -130,26 +130,14 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
static void dwc_initialize(struct dw_dma_chan *dwc)
{
	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
	struct dw_dma_slave *dws = dwc->chan.private;
	u32 cfghi = DWC_CFGH_FIFO_MODE;
	u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);

	if (dwc->initialized == true)
		return;

	if (dws) {
		/*
		 * We need controller-specific data to set up slave
		 * transfers.
		 */
		BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev);

		cfghi |= DWC_CFGH_DST_PER(dws->dst_id);
		cfghi |= DWC_CFGH_SRC_PER(dws->src_id);
	} else {
	cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
	cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
	}

	channel_writel(dwc, CFG_LO, cfglo);
	channel_writel(dwc, CFG_HI, cfghi);
@@ -941,7 +929,7 @@ bool dw_dma_filter(struct dma_chan *chan, void *param)
	struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
	struct dw_dma_slave *dws = param;

	if (!dws || dws->dma_dev != chan->device->dev)
	if (dws->dma_dev != chan->device->dev)
		return false;

	/* We have to copy data since dws can be temporary storage */
@@ -1165,6 +1153,14 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
	 * doesn't mean what you think it means), and status writeback.
	 */

	/*
	 * We need controller-specific data to set up slave transfers.
	 */
	if (chan->private && !dw_dma_filter(chan, chan->private)) {
		dev_warn(chan2dev(chan), "Wrong controller-specific data\n");
		return -EINVAL;
	}

	/* Enable controller here if needed */
	if (!dw->in_use)
		dw_dma_on(dw);
@@ -1226,6 +1222,14 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
	spin_lock_irqsave(&dwc->lock, flags);
	list_splice_init(&dwc->free_list, &list);
	dwc->descs_allocated = 0;

	/* Clear custom channel configuration */
	dwc->src_id = 0;
	dwc->dst_id = 0;

	dwc->src_master = 0;
	dwc->dst_master = 0;

	dwc->initialized = false;

	/* Disable interrupts */
+25 −38
Original line number Diff line number Diff line
@@ -1238,6 +1238,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
	struct edma_desc *edesc;
	dma_addr_t src_addr, dst_addr;
	enum dma_slave_buswidth dev_width;
	bool use_intermediate = false;
	u32 burst;
	int i, ret, nslots;

@@ -1279,8 +1280,21 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
	 * but the synchronization is difficult to achieve with Cyclic and
	 * cannot be guaranteed, so we error out early.
	 */
	if (nslots > MAX_NR_SG)
	if (nslots > MAX_NR_SG) {
		/*
		 * If the burst and period sizes are the same, we can put
		 * the full buffer into a single period and activate
		 * intermediate interrupts. This will produce interrupts
		 * after each burst, which is also after each desired period.
		 */
		if (burst == period_len) {
			period_len = buf_len;
			nslots = 2;
			use_intermediate = true;
		} else {
			return NULL;
		}
	}

	edesc = kzalloc(sizeof(*edesc) + nslots * sizeof(edesc->pset[0]),
			GFP_ATOMIC);
@@ -1358,8 +1372,13 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
		/*
		 * Enable period interrupt only if it is requested
		 */
		if (tx_flags & DMA_PREP_INTERRUPT)
		if (tx_flags & DMA_PREP_INTERRUPT) {
			edesc->pset[i].param.opt |= TCINTEN;

			/* Also enable intermediate interrupts if necessary */
			if (use_intermediate)
				edesc->pset[i].param.opt |= ITCINTEN;
		}
	}

	/* Place the cyclic channel to highest priority queue */
@@ -1570,32 +1589,6 @@ static irqreturn_t dma_ccerr_handler(int irq, void *data)
	return IRQ_HANDLED;
}

static void edma_tc_set_pm_state(struct edma_tc *tc, bool enable)
{
	struct platform_device *tc_pdev;
	int ret;

	if (!IS_ENABLED(CONFIG_OF) || !tc)
		return;

	tc_pdev = of_find_device_by_node(tc->node);
	if (!tc_pdev) {
		pr_err("%s: TPTC device is not found\n", __func__);
		return;
	}
	if (!pm_runtime_enabled(&tc_pdev->dev))
		pm_runtime_enable(&tc_pdev->dev);

	if (enable)
		ret = pm_runtime_get_sync(&tc_pdev->dev);
	else
		ret = pm_runtime_put_sync(&tc_pdev->dev);

	if (ret < 0)
		pr_err("%s: pm_runtime_%s_sync() failed for %s\n", __func__,
		       enable ? "get" : "put", dev_name(&tc_pdev->dev));
}

/* Alloc channel resources */
static int edma_alloc_chan_resources(struct dma_chan *chan)
{
@@ -1632,8 +1625,6 @@ static int edma_alloc_chan_resources(struct dma_chan *chan)
		EDMA_CHAN_SLOT(echan->ch_num), chan->chan_id,
		echan->hw_triggered ? "HW" : "SW");

	edma_tc_set_pm_state(echan->tc, true);

	return 0;

err_slot:
@@ -1670,7 +1661,6 @@ static void edma_free_chan_resources(struct dma_chan *chan)
		echan->alloced = false;
	}

	edma_tc_set_pm_state(echan->tc, false);
	echan->tc = NULL;
	echan->hw_triggered = false;

@@ -2417,10 +2407,8 @@ static int edma_pm_suspend(struct device *dev)
	int i;

	for (i = 0; i < ecc->num_channels; i++) {
		if (echan[i].alloced) {
		if (echan[i].alloced)
			edma_setup_interrupt(&echan[i], false);
			edma_tc_set_pm_state(echan[i].tc, false);
		}
	}

	return 0;
@@ -2450,8 +2438,6 @@ static int edma_pm_resume(struct device *dev)

			/* Set up channel -> slot mapping for the entry slot */
			edma_set_chmap(&echan[i], echan[i].slot[0]);

			edma_tc_set_pm_state(echan[i].tc, true);
		}
	}

@@ -2475,7 +2461,8 @@ static struct platform_driver edma_driver = {

static int edma_tptc_probe(struct platform_device *pdev)
{
	return 0;
	pm_runtime_enable(&pdev->dev);
	return pm_runtime_get_sync(&pdev->dev);
}

static struct platform_driver edma_tptc_driver = {
+8 −5
Original line number Diff line number Diff line
@@ -64,10 +64,10 @@ static void hsu_dma_chan_start(struct hsu_dma_chan *hsuc)

	if (hsuc->direction == DMA_MEM_TO_DEV) {
		bsr = config->dst_maxburst;
		mtsr = config->dst_addr_width;
		mtsr = config->src_addr_width;
	} else if (hsuc->direction == DMA_DEV_TO_MEM) {
		bsr = config->src_maxburst;
		mtsr = config->src_addr_width;
		mtsr = config->dst_addr_width;
	}

	hsu_chan_disable(hsuc);
@@ -135,7 +135,7 @@ static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc)
	sr = hsu_chan_readl(hsuc, HSU_CH_SR);
	spin_unlock_irqrestore(&hsuc->vchan.lock, flags);

	return sr;
	return sr & ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY);
}

irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
@@ -254,10 +254,13 @@ static void hsu_dma_issue_pending(struct dma_chan *chan)
static size_t hsu_dma_active_desc_size(struct hsu_dma_chan *hsuc)
{
	struct hsu_dma_desc *desc = hsuc->desc;
	size_t bytes = desc->length;
	size_t bytes = 0;
	int i;

	i = desc->active % HSU_DMA_CHAN_NR_DESC;
	for (i = desc->active; i < desc->nents; i++)
		bytes += desc->sg[i].len;

	i = HSU_DMA_CHAN_NR_DESC - 1;
	do {
		bytes += hsu_chan_readl(hsuc, HSU_CH_DxTSR(i));
	} while (--i >= 0);
+3 −0
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@
#define HSU_CH_SR_DESCTO(x)	BIT(8 + (x))
#define HSU_CH_SR_DESCTO_ANY	(BIT(11) | BIT(10) | BIT(9) | BIT(8))
#define HSU_CH_SR_CHE		BIT(15)
#define HSU_CH_SR_DESCE(x)	BIT(16 + (x))
#define HSU_CH_SR_DESCE_ANY	(BIT(19) | BIT(18) | BIT(17) | BIT(16))
#define HSU_CH_SR_CDESC_ANY	(BIT(31) | BIT(30))

/* Bits in HSU_CH_CR */
#define HSU_CH_CR_CHA		BIT(0)
+17 −9
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ struct omap_chan {
	unsigned dma_sig;
	bool cyclic;
	bool paused;
	bool running;

	int dma_ch;
	struct omap_desc *desc;
@@ -294,6 +295,8 @@ static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)

	/* Enable channel */
	omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE);

	c->running = true;
}

static void omap_dma_stop(struct omap_chan *c)
@@ -355,6 +358,8 @@ static void omap_dma_stop(struct omap_chan *c)

		omap_dma_chan_write(c, CLNK_CTRL, val);
	}

	c->running = false;
}

static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
@@ -673,15 +678,20 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
	struct omap_chan *c = to_omap_dma_chan(chan);
	struct virt_dma_desc *vd;
	enum dma_status ret;
	uint32_t ccr;
	unsigned long flags;

	ccr = omap_dma_chan_read(c, CCR);
	/* The channel is no longer active, handle the completion right away */
	ret = dma_cookie_status(chan, cookie, txstate);

	if (!c->paused && c->running) {
		uint32_t ccr = omap_dma_chan_read(c, CCR);
		/*
		 * The channel is no longer active, set the return value
		 * accordingly
		 */
		if (!(ccr & CCR_ENABLE))
		omap_dma_callback(c->dma_ch, 0, c);
			ret = DMA_COMPLETE;
	}

	ret = dma_cookie_status(chan, cookie, txstate);
	if (ret == DMA_COMPLETE || !txstate)
		return ret;

@@ -945,9 +955,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_memcpy(
	d->ccr = c->ccr;
	d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_POSTINC;

	d->cicr = CICR_DROP_IE;
	if (tx_flags & DMA_PREP_INTERRUPT)
		d->cicr |= CICR_FRAME_IE;
	d->cicr = CICR_DROP_IE | CICR_FRAME_IE;

	d->csdp = data_type;

Loading