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

Commit 7e6ecebd authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'ep93xx-fixes-for-3.5' of git://github.com/RyanMallon/linux-ep93xx into next/drivers



Branch has one driver feature and one board bug fix. Merging it as a driver branch.

DMA driver change was later:
Acked-by: default avatarVinod Koul <vinod.koul@linux.intel.com>

* tag 'ep93xx-fixes-for-3.5' of git://github.com/RyanMallon/linux-ep93xx:
  dmaengine/ep93xx_dma: Implement double buffering for M2M DMA channels
  arm: ep93xx: Don't try to release not acquired GPIO lines
parents caafc71b 2b3c83ef
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -734,7 +734,7 @@ int ep93xx_keypad_acquire_gpio(struct platform_device *pdev)
fail_gpio_d:
	gpio_free(EP93XX_GPIO_LINE_C(i));
fail_gpio_c:
	for ( ; i >= 0; --i) {
	for (--i; i >= 0; --i) {
		gpio_free(EP93XX_GPIO_LINE_C(i));
		gpio_free(EP93XX_GPIO_LINE_D(i));
	}
+93 −24
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@
#define M2M_CONTROL_TM_SHIFT		13
#define M2M_CONTROL_TM_TX		(1 << M2M_CONTROL_TM_SHIFT)
#define M2M_CONTROL_TM_RX		(2 << M2M_CONTROL_TM_SHIFT)
#define M2M_CONTROL_NFBINT		BIT(21)
#define M2M_CONTROL_RSS_SHIFT		22
#define M2M_CONTROL_RSS_SSPRX		(1 << M2M_CONTROL_RSS_SHIFT)
#define M2M_CONTROL_RSS_SSPTX		(2 << M2M_CONTROL_RSS_SHIFT)
@@ -79,7 +80,22 @@
#define M2M_CONTROL_PWSC_SHIFT		25

#define M2M_INTERRUPT			0x0004
#define M2M_INTERRUPT_DONEINT		BIT(1)
#define M2M_INTERRUPT_MASK		6

#define M2M_STATUS			0x000c
#define M2M_STATUS_CTL_SHIFT		1
#define M2M_STATUS_CTL_IDLE		(0 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_STALL		(1 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MEMRD		(2 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MEMWR		(3 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_BWCWAIT		(4 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MASK		(7 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_BUF_SHIFT		4
#define M2M_STATUS_BUF_NO		(0 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_ON		(1 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_NEXT		(2 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_MASK		(3 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_DONE			BIT(6)

#define M2M_BCR0			0x0010
#define M2M_BCR1			0x0014
@@ -426,15 +442,6 @@ static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)

/*
 * M2M DMA implementation
 *
 * For the M2M transfers we don't use NFB at all. This is because it simply
 * doesn't work well with memcpy transfers. When you submit both buffers it is
 * extremely unlikely that you get an NFB interrupt, but it instead reports
 * DONE interrupt and both buffers are already transferred which means that we
 * weren't able to update the next buffer.
 *
 * So for now we "simulate" NFB by just submitting buffer after buffer
 * without double buffering.
 */

static int m2m_hw_setup(struct ep93xx_dma_chan *edmac)
@@ -543,6 +550,11 @@ static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
	m2m_fill_desc(edmac);
	control |= M2M_CONTROL_DONEINT;

	if (ep93xx_dma_advance_active(edmac)) {
		m2m_fill_desc(edmac);
		control |= M2M_CONTROL_NFBINT;
	}

	/*
	 * Now we can finally enable the channel. For M2M channel this must be
	 * done _after_ the BCRx registers are programmed.
@@ -560,34 +572,91 @@ static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
	}
}

/*
 * According to EP93xx User's Guide, we should receive DONE interrupt when all
 * M2M DMA controller transactions complete normally. This is not always the
 * case - sometimes EP93xx M2M DMA asserts DONE interrupt when the DMA channel
 * is still running (channel Buffer FSM in DMA_BUF_ON state, and channel
 * Control FSM in DMA_MEM_RD state, observed at least in IDE-DMA operation).
 * In effect, disabling the channel when only DONE bit is set could stop
 * currently running DMA transfer. To avoid this, we use Buffer FSM and
 * Control FSM to check current state of DMA channel.
 */
static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac)
{
	u32 status = readl(edmac->regs + M2M_STATUS);
	u32 ctl_fsm = status & M2M_STATUS_CTL_MASK;
	u32 buf_fsm = status & M2M_STATUS_BUF_MASK;
	bool done = status & M2M_STATUS_DONE;
	bool last_done;
	u32 control;
	struct ep93xx_dma_desc *desc;

	if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT))
	/* Accept only DONE and NFB interrupts */
	if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_MASK))
		return INTERRUPT_UNKNOWN;

	if (done) {
		/* Clear the DONE bit */
		writel(0, edmac->regs + M2M_INTERRUPT);
	}

	/* Disable interrupts and the channel */
	control = readl(edmac->regs + M2M_CONTROL);
	control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE);
	writel(control, edmac->regs + M2M_CONTROL);
	/*
	 * Check whether we are done with descriptors or not. This, together
	 * with DMA channel state, determines action to take in interrupt.
	 */
	desc = ep93xx_dma_get_active(edmac);
	last_done = !desc || desc->txd.cookie;

	/*
	 * Since we only get DONE interrupt we have to find out ourselves
	 * whether there still is something to process. So we try to advance
	 * the chain an see whether it succeeds.
	 * Use M2M DMA Buffer FSM and Control FSM to check current state of
	 * DMA channel. Using DONE and NFB bits from channel status register
	 * or bits from channel interrupt register is not reliable.
	 */
	if (!last_done &&
	    (buf_fsm == M2M_STATUS_BUF_NO ||
	     buf_fsm == M2M_STATUS_BUF_ON)) {
		/*
		 * Two buffers are ready for update when Buffer FSM is in
		 * DMA_NO_BUF state. Only one buffer can be prepared without
		 * disabling the channel or polling the DONE bit.
		 * To simplify things, always prepare only one buffer.
		 */
		if (ep93xx_dma_advance_active(edmac)) {
		edmac->edma->hw_submit(edmac);
			m2m_fill_desc(edmac);
			if (done && !edmac->chan.private) {
				/* Software trigger for memcpy channel */
				control = readl(edmac->regs + M2M_CONTROL);
				control |= M2M_CONTROL_START;
				writel(control, edmac->regs + M2M_CONTROL);
			}
			return INTERRUPT_NEXT_BUFFER;
		} else {
			last_done = true;
		}
	}

	/*
	 * Disable the channel only when Buffer FSM is in DMA_NO_BUF state
	 * and Control FSM is in DMA_STALL state.
	 */
	if (last_done &&
	    buf_fsm == M2M_STATUS_BUF_NO &&
	    ctl_fsm == M2M_STATUS_CTL_STALL) {
		/* Disable interrupts and the channel */
		control = readl(edmac->regs + M2M_CONTROL);
		control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_NFBINT
			    | M2M_CONTROL_ENABLE);
		writel(control, edmac->regs + M2M_CONTROL);
		return INTERRUPT_DONE;
	}

	/*
	 * Nothing to do this time.
	 */
	return INTERRUPT_NEXT_BUFFER;
}

/*
 * DMA engine API implementation
 */