Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35609502 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull dmaengine fixes from Vinod Koul:
 "A couple of fixes showed up late in the cycle so sending them up and
  sending early in the week and not on Friday :).

  They fix a double lock in pl330 driver and runtime pm fixes for cppi
  driver"

* tag 'dmaengine-fix-4.10-rc7' of git://git.infradead.org/users/vkoul/slave-dma:
  dmaengine: pl330: fix double lock
  dmaengine: cppi41: Clean up pointless warnings
  dmaengine: cppi41: Fix oops in cppi41_runtime_resume
  dmaengine: cppi41: Fix runtime PM timeouts with USB mass storage
parents c325b353 91539eb1
Loading
Loading
Loading
Loading
+46 −23
Original line number Diff line number Diff line
@@ -153,6 +153,8 @@ struct cppi41_dd {

	/* context for suspend/resume */
	unsigned int dma_tdfdq;

	bool is_suspended;
};

#define FIST_COMPLETION_QUEUE	93
@@ -257,6 +259,10 @@ static struct cppi41_channel *desc_to_chan(struct cppi41_dd *cdd, u32 desc)
	BUG_ON(desc_num >= ALLOC_DECS_NUM);
	c = cdd->chan_busy[desc_num];
	cdd->chan_busy[desc_num] = NULL;

	/* Usecount for chan_busy[], paired with push_desc_queue() */
	pm_runtime_put(cdd->ddev.dev);

	return c;
}

@@ -317,12 +323,12 @@ static irqreturn_t cppi41_irq(int irq, void *data)

		while (val) {
			u32 desc, len;
			int error;

			error = pm_runtime_get(cdd->ddev.dev);
			if (error < 0)
				dev_err(cdd->ddev.dev, "%s pm runtime get: %i\n",
					__func__, error);
			/*
			 * This should never trigger, see the comments in
			 * push_desc_queue()
			 */
			WARN_ON(cdd->is_suspended);

			q_num = __fls(val);
			val &= ~(1 << q_num);
@@ -343,9 +349,6 @@ static irqreturn_t cppi41_irq(int irq, void *data)
			c->residue = pd_trans_len(c->desc->pd6) - len;
			dma_cookie_complete(&c->txd);
			dmaengine_desc_get_callback_invoke(&c->txd, NULL);

			pm_runtime_mark_last_busy(cdd->ddev.dev);
			pm_runtime_put_autosuspend(cdd->ddev.dev);
		}
	}
	return IRQ_HANDLED;
@@ -447,6 +450,15 @@ static void push_desc_queue(struct cppi41_channel *c)
	 */
	__iowmb();

	/*
	 * DMA transfers can take at least 200ms to complete with USB mass
	 * storage connected. To prevent autosuspend timeouts, we must use
	 * pm_runtime_get/put() when chan_busy[] is modified. This will get
	 * cleared in desc_to_chan() or cppi41_stop_chan() depending on the
	 * outcome of the transfer.
	 */
	pm_runtime_get(cdd->ddev.dev);

	desc_phys = lower_32_bits(c->desc_phys);
	desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
	WARN_ON(cdd->chan_busy[desc_num]);
@@ -457,20 +469,26 @@ static void push_desc_queue(struct cppi41_channel *c)
	cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num));
}

static void pending_desc(struct cppi41_channel *c)
/*
 * Caller must hold cdd->lock to prevent push_desc_queue()
 * getting called out of order. We have both cppi41_dma_issue_pending()
 * and cppi41_runtime_resume() call this function.
 */
static void cppi41_run_queue(struct cppi41_dd *cdd)
{
	struct cppi41_dd *cdd = c->cdd;
	unsigned long flags;
	struct cppi41_channel *c, *_c;

	spin_lock_irqsave(&cdd->lock, flags);
	list_add_tail(&c->node, &cdd->pending);
	spin_unlock_irqrestore(&cdd->lock, flags);
	list_for_each_entry_safe(c, _c, &cdd->pending, node) {
		push_desc_queue(c);
		list_del(&c->node);
	}
}

static void cppi41_dma_issue_pending(struct dma_chan *chan)
{
	struct cppi41_channel *c = to_cpp41_chan(chan);
	struct cppi41_dd *cdd = c->cdd;
	unsigned long flags;
	int error;

	error = pm_runtime_get(cdd->ddev.dev);
@@ -482,10 +500,11 @@ static void cppi41_dma_issue_pending(struct dma_chan *chan)
		return;
	}

	if (likely(pm_runtime_active(cdd->ddev.dev)))
		push_desc_queue(c);
	else
		pending_desc(c);
	spin_lock_irqsave(&cdd->lock, flags);
	list_add_tail(&c->node, &cdd->pending);
	if (!cdd->is_suspended)
		cppi41_run_queue(cdd);
	spin_unlock_irqrestore(&cdd->lock, flags);

	pm_runtime_mark_last_busy(cdd->ddev.dev);
	pm_runtime_put_autosuspend(cdd->ddev.dev);
@@ -705,6 +724,9 @@ static int cppi41_stop_chan(struct dma_chan *chan)
	WARN_ON(!cdd->chan_busy[desc_num]);
	cdd->chan_busy[desc_num] = NULL;

	/* Usecount for chan_busy[], paired with push_desc_queue() */
	pm_runtime_put(cdd->ddev.dev);

	return 0;
}

@@ -1150,8 +1172,12 @@ static int __maybe_unused cppi41_resume(struct device *dev)
static int __maybe_unused cppi41_runtime_suspend(struct device *dev)
{
	struct cppi41_dd *cdd = dev_get_drvdata(dev);
	unsigned long flags;

	spin_lock_irqsave(&cdd->lock, flags);
	cdd->is_suspended = true;
	WARN_ON(!list_empty(&cdd->pending));
	spin_unlock_irqrestore(&cdd->lock, flags);

	return 0;
}
@@ -1159,14 +1185,11 @@ static int __maybe_unused cppi41_runtime_suspend(struct device *dev)
static int __maybe_unused cppi41_runtime_resume(struct device *dev)
{
	struct cppi41_dd *cdd = dev_get_drvdata(dev);
	struct cppi41_channel *c, *_c;
	unsigned long flags;

	spin_lock_irqsave(&cdd->lock, flags);
	list_for_each_entry_safe(c, _c, &cdd->pending, node) {
		push_desc_queue(c);
		list_del(&c->node);
	}
	cdd->is_suspended = false;
	cppi41_run_queue(cdd);
	spin_unlock_irqrestore(&cdd->lock, flags);

	return 0;
+6 −13
Original line number Diff line number Diff line
@@ -1699,7 +1699,6 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i)
static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
{
	struct pl330_thread *thrd = NULL;
	unsigned long flags;
	int chans, i;

	if (pl330->state == DYING)
@@ -1707,8 +1706,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)

	chans = pl330->pcfg.num_chan;

	spin_lock_irqsave(&pl330->lock, flags);

	for (i = 0; i < chans; i++) {
		thrd = &pl330->channels[i];
		if ((thrd->free) && (!_manager_ns(thrd) ||
@@ -1726,8 +1723,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
		thrd = NULL;
	}

	spin_unlock_irqrestore(&pl330->lock, flags);

	return thrd;
}

@@ -1745,7 +1740,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev)
static void pl330_release_channel(struct pl330_thread *thrd)
{
	struct pl330_dmac *pl330;
	unsigned long flags;

	if (!thrd || thrd->free)
		return;
@@ -1757,10 +1751,8 @@ static void pl330_release_channel(struct pl330_thread *thrd)

	pl330 = thrd->dmac;

	spin_lock_irqsave(&pl330->lock, flags);
	_free_event(thrd, thrd->ev);
	thrd->free = true;
	spin_unlock_irqrestore(&pl330->lock, flags);
}

/* Initialize the structure for PL330 configuration, that can be used
@@ -2122,20 +2114,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
	struct pl330_dmac *pl330 = pch->dmac;
	unsigned long flags;

	spin_lock_irqsave(&pch->lock, flags);
	spin_lock_irqsave(&pl330->lock, flags);

	dma_cookie_init(chan);
	pch->cyclic = false;

	pch->thread = pl330_request_channel(pl330);
	if (!pch->thread) {
		spin_unlock_irqrestore(&pch->lock, flags);
		spin_unlock_irqrestore(&pl330->lock, flags);
		return -ENOMEM;
	}

	tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);

	spin_unlock_irqrestore(&pch->lock, flags);
	spin_unlock_irqrestore(&pl330->lock, flags);

	return 1;
}
@@ -2238,12 +2230,13 @@ static int pl330_pause(struct dma_chan *chan)
static void pl330_free_chan_resources(struct dma_chan *chan)
{
	struct dma_pl330_chan *pch = to_pchan(chan);
	struct pl330_dmac *pl330 = pch->dmac;
	unsigned long flags;

	tasklet_kill(&pch->task);

	pm_runtime_get_sync(pch->dmac->ddma.dev);
	spin_lock_irqsave(&pch->lock, flags);
	spin_lock_irqsave(&pl330->lock, flags);

	pl330_release_channel(pch->thread);
	pch->thread = NULL;
@@ -2251,7 +2244,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
	if (pch->cyclic)
		list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);

	spin_unlock_irqrestore(&pch->lock, flags);
	spin_unlock_irqrestore(&pl330->lock, flags);
	pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
	pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
}