162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale MPC85xx, MPC83xx DMA Engine support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author:
862306a36Sopenharmony_ci *   Zhang Wei <wei.zhang@freescale.com>, Jul 2007
962306a36Sopenharmony_ci *   Ebony Zhu <ebony.zhu@freescale.com>, May 2007
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Description:
1262306a36Sopenharmony_ci *   DMA engine driver for Freescale MPC8540 DMA controller, which is
1362306a36Sopenharmony_ci *   also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc.
1462306a36Sopenharmony_ci *   The support for MPC8349 DMA controller is also added.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * This driver instructs the DMA controller to issue the PCI Read Multiple
1762306a36Sopenharmony_ci * command for PCI read operations, instead of using the default PCI Read Line
1862306a36Sopenharmony_ci * command. Please be aware that this setting may result in read pre-fetching
1962306a36Sopenharmony_ci * on some platforms.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/pci.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/interrupt.h>
2762306a36Sopenharmony_ci#include <linux/dmaengine.h>
2862306a36Sopenharmony_ci#include <linux/delay.h>
2962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3062306a36Sopenharmony_ci#include <linux/dmapool.h>
3162306a36Sopenharmony_ci#include <linux/of.h>
3262306a36Sopenharmony_ci#include <linux/of_address.h>
3362306a36Sopenharmony_ci#include <linux/of_irq.h>
3462306a36Sopenharmony_ci#include <linux/platform_device.h>
3562306a36Sopenharmony_ci#include <linux/fsldma.h>
3662306a36Sopenharmony_ci#include "dmaengine.h"
3762306a36Sopenharmony_ci#include "fsldma.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define chan_dbg(chan, fmt, arg...)					\
4062306a36Sopenharmony_ci	dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg)
4162306a36Sopenharmony_ci#define chan_err(chan, fmt, arg...)					\
4262306a36Sopenharmony_ci	dev_err(chan->dev, "%s: " fmt, chan->name, ##arg)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const char msg_ld_oom[] = "No free memory for link descriptor";
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Register Helpers
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void set_sr(struct fsldma_chan *chan, u32 val)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	FSL_DMA_OUT(chan, &chan->regs->sr, val, 32);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic u32 get_sr(struct fsldma_chan *chan)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return FSL_DMA_IN(chan, &chan->regs->sr, 32);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void set_mr(struct fsldma_chan *chan, u32 val)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	FSL_DMA_OUT(chan, &chan->regs->mr, val, 32);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic u32 get_mr(struct fsldma_chan *chan)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return FSL_DMA_IN(chan, &chan->regs->mr, 32);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic dma_addr_t get_cdar(struct fsldma_chan *chan)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void set_bcr(struct fsldma_chan *chan, u32 val)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic u32 get_bcr(struct fsldma_chan *chan)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return FSL_DMA_IN(chan, &chan->regs->bcr, 32);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci * Descriptor Helpers
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void set_desc_cnt(struct fsldma_chan *chan,
9562306a36Sopenharmony_ci				struct fsl_dma_ld_hw *hw, u32 count)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	hw->count = CPU_TO_DMA(chan, count, 32);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void set_desc_src(struct fsldma_chan *chan,
10162306a36Sopenharmony_ci			 struct fsl_dma_ld_hw *hw, dma_addr_t src)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	u64 snoop_bits;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
10662306a36Sopenharmony_ci		? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
10762306a36Sopenharmony_ci	hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void set_desc_dst(struct fsldma_chan *chan,
11162306a36Sopenharmony_ci			 struct fsl_dma_ld_hw *hw, dma_addr_t dst)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	u64 snoop_bits;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
11662306a36Sopenharmony_ci		? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
11762306a36Sopenharmony_ci	hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void set_desc_next(struct fsldma_chan *chan,
12162306a36Sopenharmony_ci			  struct fsl_dma_ld_hw *hw, dma_addr_t next)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	u64 snoop_bits;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
12662306a36Sopenharmony_ci		? FSL_DMA_SNEN : 0;
12762306a36Sopenharmony_ci	hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void set_ld_eol(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u64 snoop_bits;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
13562306a36Sopenharmony_ci		? FSL_DMA_SNEN : 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	desc->hw.next_ln_addr = CPU_TO_DMA(chan,
13862306a36Sopenharmony_ci		DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
13962306a36Sopenharmony_ci			| snoop_bits, 64);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * DMA Engine Hardware Control Helpers
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void dma_init(struct fsldma_chan *chan)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	/* Reset the channel */
14962306a36Sopenharmony_ci	set_mr(chan, 0);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	switch (chan->feature & FSL_DMA_IP_MASK) {
15262306a36Sopenharmony_ci	case FSL_DMA_IP_85XX:
15362306a36Sopenharmony_ci		/* Set the channel to below modes:
15462306a36Sopenharmony_ci		 * EIE - Error interrupt enable
15562306a36Sopenharmony_ci		 * EOLNIE - End of links interrupt enable
15662306a36Sopenharmony_ci		 * BWC - Bandwidth sharing among channels
15762306a36Sopenharmony_ci		 */
15862306a36Sopenharmony_ci		set_mr(chan, FSL_DMA_MR_BWC | FSL_DMA_MR_EIE
15962306a36Sopenharmony_ci			| FSL_DMA_MR_EOLNIE);
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case FSL_DMA_IP_83XX:
16262306a36Sopenharmony_ci		/* Set the channel to below modes:
16362306a36Sopenharmony_ci		 * EOTIE - End-of-transfer interrupt enable
16462306a36Sopenharmony_ci		 * PRC_RM - PCI read multiple
16562306a36Sopenharmony_ci		 */
16662306a36Sopenharmony_ci		set_mr(chan, FSL_DMA_MR_EOTIE | FSL_DMA_MR_PRC_RM);
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int dma_is_idle(struct fsldma_chan *chan)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	u32 sr = get_sr(chan);
17462306a36Sopenharmony_ci	return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * Start the DMA controller
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci * Preconditions:
18162306a36Sopenharmony_ci * - the CDAR register must point to the start descriptor
18262306a36Sopenharmony_ci * - the MRn[CS] bit must be cleared
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic void dma_start(struct fsldma_chan *chan)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	u32 mode;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mode = get_mr(chan);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
19162306a36Sopenharmony_ci		set_bcr(chan, 0);
19262306a36Sopenharmony_ci		mode |= FSL_DMA_MR_EMP_EN;
19362306a36Sopenharmony_ci	} else {
19462306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_EMP_EN;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (chan->feature & FSL_DMA_CHAN_START_EXT) {
19862306a36Sopenharmony_ci		mode |= FSL_DMA_MR_EMS_EN;
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_EMS_EN;
20162306a36Sopenharmony_ci		mode |= FSL_DMA_MR_CS;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	set_mr(chan, mode);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void dma_halt(struct fsldma_chan *chan)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	u32 mode;
21062306a36Sopenharmony_ci	int i;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* read the mode register */
21362306a36Sopenharmony_ci	mode = get_mr(chan);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * The 85xx controller supports channel abort, which will stop
21762306a36Sopenharmony_ci	 * the current transfer. On 83xx, this bit is the transfer error
21862306a36Sopenharmony_ci	 * mask bit, which should not be changed.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
22162306a36Sopenharmony_ci		mode |= FSL_DMA_MR_CA;
22262306a36Sopenharmony_ci		set_mr(chan, mode);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_CA;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* stop the DMA controller */
22862306a36Sopenharmony_ci	mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN);
22962306a36Sopenharmony_ci	set_mr(chan, mode);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* wait for the DMA controller to become idle */
23262306a36Sopenharmony_ci	for (i = 0; i < 100; i++) {
23362306a36Sopenharmony_ci		if (dma_is_idle(chan))
23462306a36Sopenharmony_ci			return;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		udelay(10);
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (!dma_is_idle(chan))
24062306a36Sopenharmony_ci		chan_err(chan, "DMA halt timeout!\n");
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/**
24462306a36Sopenharmony_ci * fsl_chan_set_src_loop_size - Set source address hold transfer size
24562306a36Sopenharmony_ci * @chan : Freescale DMA channel
24662306a36Sopenharmony_ci * @size     : Address loop size, 0 for disable loop
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci * The set source address hold transfer size. The source
24962306a36Sopenharmony_ci * address hold or loop transfer size is when the DMA transfer
25062306a36Sopenharmony_ci * data from source address (SA), if the loop size is 4, the DMA will
25162306a36Sopenharmony_ci * read data from SA, SA + 1, SA + 2, SA + 3, then loop back to SA,
25262306a36Sopenharmony_ci * SA + 1 ... and so on.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic void fsl_chan_set_src_loop_size(struct fsldma_chan *chan, int size)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	u32 mode;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	mode = get_mr(chan);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	switch (size) {
26162306a36Sopenharmony_ci	case 0:
26262306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_SAHE;
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	case 1:
26562306a36Sopenharmony_ci	case 2:
26662306a36Sopenharmony_ci	case 4:
26762306a36Sopenharmony_ci	case 8:
26862306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_SAHTS_MASK;
26962306a36Sopenharmony_ci		mode |= FSL_DMA_MR_SAHE | (__ilog2(size) << 14);
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	set_mr(chan, mode);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/**
27762306a36Sopenharmony_ci * fsl_chan_set_dst_loop_size - Set destination address hold transfer size
27862306a36Sopenharmony_ci * @chan : Freescale DMA channel
27962306a36Sopenharmony_ci * @size     : Address loop size, 0 for disable loop
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * The set destination address hold transfer size. The destination
28262306a36Sopenharmony_ci * address hold or loop transfer size is when the DMA transfer
28362306a36Sopenharmony_ci * data to destination address (TA), if the loop size is 4, the DMA will
28462306a36Sopenharmony_ci * write data to TA, TA + 1, TA + 2, TA + 3, then loop back to TA,
28562306a36Sopenharmony_ci * TA + 1 ... and so on.
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_cistatic void fsl_chan_set_dst_loop_size(struct fsldma_chan *chan, int size)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	u32 mode;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	mode = get_mr(chan);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	switch (size) {
29462306a36Sopenharmony_ci	case 0:
29562306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_DAHE;
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci	case 1:
29862306a36Sopenharmony_ci	case 2:
29962306a36Sopenharmony_ci	case 4:
30062306a36Sopenharmony_ci	case 8:
30162306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_DAHTS_MASK;
30262306a36Sopenharmony_ci		mode |= FSL_DMA_MR_DAHE | (__ilog2(size) << 16);
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	set_mr(chan, mode);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/**
31062306a36Sopenharmony_ci * fsl_chan_set_request_count - Set DMA Request Count for external control
31162306a36Sopenharmony_ci * @chan : Freescale DMA channel
31262306a36Sopenharmony_ci * @size     : Number of bytes to transfer in a single request
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * The Freescale DMA channel can be controlled by the external signal DREQ#.
31562306a36Sopenharmony_ci * The DMA request count is how many bytes are allowed to transfer before
31662306a36Sopenharmony_ci * pausing the channel, after which a new assertion of DREQ# resumes channel
31762306a36Sopenharmony_ci * operation.
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci * A size of 0 disables external pause control. The maximum size is 1024.
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_cistatic void fsl_chan_set_request_count(struct fsldma_chan *chan, int size)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	u32 mode;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	BUG_ON(size > 1024);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	mode = get_mr(chan);
32862306a36Sopenharmony_ci	mode &= ~FSL_DMA_MR_BWC_MASK;
32962306a36Sopenharmony_ci	mode |= (__ilog2(size) << 24) & FSL_DMA_MR_BWC_MASK;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	set_mr(chan, mode);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/**
33562306a36Sopenharmony_ci * fsl_chan_toggle_ext_pause - Toggle channel external pause status
33662306a36Sopenharmony_ci * @chan : Freescale DMA channel
33762306a36Sopenharmony_ci * @enable   : 0 is disabled, 1 is enabled.
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * The Freescale DMA channel can be controlled by the external signal DREQ#.
34062306a36Sopenharmony_ci * The DMA Request Count feature should be used in addition to this feature
34162306a36Sopenharmony_ci * to set the number of bytes to transfer before pausing the channel.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic void fsl_chan_toggle_ext_pause(struct fsldma_chan *chan, int enable)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	if (enable)
34662306a36Sopenharmony_ci		chan->feature |= FSL_DMA_CHAN_PAUSE_EXT;
34762306a36Sopenharmony_ci	else
34862306a36Sopenharmony_ci		chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/**
35262306a36Sopenharmony_ci * fsl_chan_toggle_ext_start - Toggle channel external start status
35362306a36Sopenharmony_ci * @chan : Freescale DMA channel
35462306a36Sopenharmony_ci * @enable   : 0 is disabled, 1 is enabled.
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * If enable the external start, the channel can be started by an
35762306a36Sopenharmony_ci * external DMA start pin. So the dma_start() does not start the
35862306a36Sopenharmony_ci * transfer immediately. The DMA channel will wait for the
35962306a36Sopenharmony_ci * control pin asserted.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_cistatic void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	if (enable)
36462306a36Sopenharmony_ci		chan->feature |= FSL_DMA_CHAN_START_EXT;
36562306a36Sopenharmony_ci	else
36662306a36Sopenharmony_ci		chan->feature &= ~FSL_DMA_CHAN_START_EXT;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciint fsl_dma_external_start(struct dma_chan *dchan, int enable)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct fsldma_chan *chan;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!dchan)
37462306a36Sopenharmony_ci		return -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	chan = to_fsl_chan(dchan);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	fsl_chan_toggle_ext_start(chan, enable);
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_dma_external_start);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (list_empty(&chan->ld_pending))
38862306a36Sopenharmony_ci		goto out_splice;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/*
39162306a36Sopenharmony_ci	 * Add the hardware descriptor to the chain of hardware descriptors
39262306a36Sopenharmony_ci	 * that already exists in memory.
39362306a36Sopenharmony_ci	 *
39462306a36Sopenharmony_ci	 * This will un-set the EOL bit of the existing transaction, and the
39562306a36Sopenharmony_ci	 * last link in this transaction will become the EOL descriptor.
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	set_desc_next(chan, &tail->hw, desc->async_tx.phys);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/*
40062306a36Sopenharmony_ci	 * Add the software descriptor and all children to the list
40162306a36Sopenharmony_ci	 * of pending transactions
40262306a36Sopenharmony_ci	 */
40362306a36Sopenharmony_ciout_splice:
40462306a36Sopenharmony_ci	list_splice_tail_init(&desc->tx_list, &chan->ld_pending);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct fsldma_chan *chan = to_fsl_chan(tx->chan);
41062306a36Sopenharmony_ci	struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
41162306a36Sopenharmony_ci	struct fsl_desc_sw *child;
41262306a36Sopenharmony_ci	dma_cookie_t cookie = -EINVAL;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	spin_lock_bh(&chan->desc_lock);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci#ifdef CONFIG_PM
41762306a36Sopenharmony_ci	if (unlikely(chan->pm_state != RUNNING)) {
41862306a36Sopenharmony_ci		chan_dbg(chan, "cannot submit due to suspend\n");
41962306a36Sopenharmony_ci		spin_unlock_bh(&chan->desc_lock);
42062306a36Sopenharmony_ci		return -1;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci#endif
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * assign cookies to all of the software descriptors
42662306a36Sopenharmony_ci	 * that make up this transaction
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	list_for_each_entry(child, &desc->tx_list, node) {
42962306a36Sopenharmony_ci		cookie = dma_cookie_assign(&child->async_tx);
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* put this transaction onto the tail of the pending queue */
43362306a36Sopenharmony_ci	append_ld_queue(chan, desc);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	spin_unlock_bh(&chan->desc_lock);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return cookie;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/**
44162306a36Sopenharmony_ci * fsl_dma_free_descriptor - Free descriptor from channel's DMA pool.
44262306a36Sopenharmony_ci * @chan : Freescale DMA channel
44362306a36Sopenharmony_ci * @desc: descriptor to be freed
44462306a36Sopenharmony_ci */
44562306a36Sopenharmony_cistatic void fsl_dma_free_descriptor(struct fsldma_chan *chan,
44662306a36Sopenharmony_ci		struct fsl_desc_sw *desc)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	list_del(&desc->node);
44962306a36Sopenharmony_ci	chan_dbg(chan, "LD %p free\n", desc);
45062306a36Sopenharmony_ci	dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * fsl_dma_alloc_descriptor - Allocate descriptor from channel's DMA pool.
45562306a36Sopenharmony_ci * @chan : Freescale DMA channel
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * Return - The descriptor allocated. NULL for failed.
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct fsl_desc_sw *desc;
46262306a36Sopenharmony_ci	dma_addr_t pdesc;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &pdesc);
46562306a36Sopenharmony_ci	if (!desc) {
46662306a36Sopenharmony_ci		chan_dbg(chan, "out of memory for link descriptor\n");
46762306a36Sopenharmony_ci		return NULL;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	INIT_LIST_HEAD(&desc->tx_list);
47162306a36Sopenharmony_ci	dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
47262306a36Sopenharmony_ci	desc->async_tx.tx_submit = fsl_dma_tx_submit;
47362306a36Sopenharmony_ci	desc->async_tx.phys = pdesc;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	chan_dbg(chan, "LD %p allocated\n", desc);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return desc;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/**
48162306a36Sopenharmony_ci * fsldma_clean_completed_descriptor - free all descriptors which
48262306a36Sopenharmony_ci * has been completed and acked
48362306a36Sopenharmony_ci * @chan: Freescale DMA channel
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci * This function is used on all completed and acked descriptors.
48662306a36Sopenharmony_ci * All descriptors should only be freed in this function.
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cistatic void fsldma_clean_completed_descriptor(struct fsldma_chan *chan)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct fsl_desc_sw *desc, *_desc;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* Run the callback for each descriptor, in order */
49362306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &chan->ld_completed, node)
49462306a36Sopenharmony_ci		if (async_tx_test_ack(&desc->async_tx))
49562306a36Sopenharmony_ci			fsl_dma_free_descriptor(chan, desc);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/**
49962306a36Sopenharmony_ci * fsldma_run_tx_complete_actions - cleanup a single link descriptor
50062306a36Sopenharmony_ci * @chan: Freescale DMA channel
50162306a36Sopenharmony_ci * @desc: descriptor to cleanup and free
50262306a36Sopenharmony_ci * @cookie: Freescale DMA transaction identifier
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * This function is used on a descriptor which has been executed by the DMA
50562306a36Sopenharmony_ci * controller. It will run any callbacks, submit any dependencies.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic dma_cookie_t fsldma_run_tx_complete_actions(struct fsldma_chan *chan,
50862306a36Sopenharmony_ci		struct fsl_desc_sw *desc, dma_cookie_t cookie)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct dma_async_tx_descriptor *txd = &desc->async_tx;
51162306a36Sopenharmony_ci	dma_cookie_t ret = cookie;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	BUG_ON(txd->cookie < 0);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (txd->cookie > 0) {
51662306a36Sopenharmony_ci		ret = txd->cookie;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		dma_descriptor_unmap(txd);
51962306a36Sopenharmony_ci		/* Run the link descriptor callback function */
52062306a36Sopenharmony_ci		dmaengine_desc_get_callback_invoke(txd, NULL);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Run any dependencies */
52462306a36Sopenharmony_ci	dma_run_dependencies(txd);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return ret;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/**
53062306a36Sopenharmony_ci * fsldma_clean_running_descriptor - move the completed descriptor from
53162306a36Sopenharmony_ci * ld_running to ld_completed
53262306a36Sopenharmony_ci * @chan: Freescale DMA channel
53362306a36Sopenharmony_ci * @desc: the descriptor which is completed
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Free the descriptor directly if acked by async_tx api, or move it to
53662306a36Sopenharmony_ci * queue ld_completed.
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic void fsldma_clean_running_descriptor(struct fsldma_chan *chan,
53962306a36Sopenharmony_ci		struct fsl_desc_sw *desc)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	/* Remove from the list of transactions */
54262306a36Sopenharmony_ci	list_del(&desc->node);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/*
54562306a36Sopenharmony_ci	 * the client is allowed to attach dependent operations
54662306a36Sopenharmony_ci	 * until 'ack' is set
54762306a36Sopenharmony_ci	 */
54862306a36Sopenharmony_ci	if (!async_tx_test_ack(&desc->async_tx)) {
54962306a36Sopenharmony_ci		/*
55062306a36Sopenharmony_ci		 * Move this descriptor to the list of descriptors which is
55162306a36Sopenharmony_ci		 * completed, but still awaiting the 'ack' bit to be set.
55262306a36Sopenharmony_ci		 */
55362306a36Sopenharmony_ci		list_add_tail(&desc->node, &chan->ld_completed);
55462306a36Sopenharmony_ci		return;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/**
56162306a36Sopenharmony_ci * fsl_chan_xfer_ld_queue - transfer any pending transactions
56262306a36Sopenharmony_ci * @chan : Freescale DMA channel
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * HARDWARE STATE: idle
56562306a36Sopenharmony_ci * LOCKING: must hold chan->desc_lock
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct fsl_desc_sw *desc;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/*
57262306a36Sopenharmony_ci	 * If the list of pending descriptors is empty, then we
57362306a36Sopenharmony_ci	 * don't need to do any work at all
57462306a36Sopenharmony_ci	 */
57562306a36Sopenharmony_ci	if (list_empty(&chan->ld_pending)) {
57662306a36Sopenharmony_ci		chan_dbg(chan, "no pending LDs\n");
57762306a36Sopenharmony_ci		return;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 * The DMA controller is not idle, which means that the interrupt
58262306a36Sopenharmony_ci	 * handler will start any queued transactions when it runs after
58362306a36Sopenharmony_ci	 * this transaction finishes
58462306a36Sopenharmony_ci	 */
58562306a36Sopenharmony_ci	if (!chan->idle) {
58662306a36Sopenharmony_ci		chan_dbg(chan, "DMA controller still busy\n");
58762306a36Sopenharmony_ci		return;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/*
59162306a36Sopenharmony_ci	 * If there are some link descriptors which have not been
59262306a36Sopenharmony_ci	 * transferred, we need to start the controller
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/*
59662306a36Sopenharmony_ci	 * Move all elements from the queue of pending transactions
59762306a36Sopenharmony_ci	 * onto the list of running transactions
59862306a36Sopenharmony_ci	 */
59962306a36Sopenharmony_ci	chan_dbg(chan, "idle, starting controller\n");
60062306a36Sopenharmony_ci	desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node);
60162306a36Sopenharmony_ci	list_splice_tail_init(&chan->ld_pending, &chan->ld_running);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/*
60462306a36Sopenharmony_ci	 * The 85xx DMA controller doesn't clear the channel start bit
60562306a36Sopenharmony_ci	 * automatically at the end of a transfer. Therefore we must clear
60662306a36Sopenharmony_ci	 * it in software before starting the transfer.
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
60962306a36Sopenharmony_ci		u32 mode;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		mode = get_mr(chan);
61262306a36Sopenharmony_ci		mode &= ~FSL_DMA_MR_CS;
61362306a36Sopenharmony_ci		set_mr(chan, mode);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/*
61762306a36Sopenharmony_ci	 * Program the descriptor's address into the DMA controller,
61862306a36Sopenharmony_ci	 * then start the DMA transaction
61962306a36Sopenharmony_ci	 */
62062306a36Sopenharmony_ci	set_cdar(chan, desc->async_tx.phys);
62162306a36Sopenharmony_ci	get_cdar(chan);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	dma_start(chan);
62462306a36Sopenharmony_ci	chan->idle = false;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * fsldma_cleanup_descriptors - cleanup link descriptors which are completed
62962306a36Sopenharmony_ci * and move them to ld_completed to free until flag 'ack' is set
63062306a36Sopenharmony_ci * @chan: Freescale DMA channel
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * This function is used on descriptors which have been executed by the DMA
63362306a36Sopenharmony_ci * controller. It will run any callbacks, submit any dependencies, then
63462306a36Sopenharmony_ci * free these descriptors if flag 'ack' is set.
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_cistatic void fsldma_cleanup_descriptors(struct fsldma_chan *chan)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct fsl_desc_sw *desc, *_desc;
63962306a36Sopenharmony_ci	dma_cookie_t cookie = 0;
64062306a36Sopenharmony_ci	dma_addr_t curr_phys = get_cdar(chan);
64162306a36Sopenharmony_ci	int seen_current = 0;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	fsldma_clean_completed_descriptor(chan);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Run the callback for each descriptor, in order */
64662306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) {
64762306a36Sopenharmony_ci		/*
64862306a36Sopenharmony_ci		 * do not advance past the current descriptor loaded into the
64962306a36Sopenharmony_ci		 * hardware channel, subsequent descriptors are either in
65062306a36Sopenharmony_ci		 * process or have not been submitted
65162306a36Sopenharmony_ci		 */
65262306a36Sopenharmony_ci		if (seen_current)
65362306a36Sopenharmony_ci			break;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		/*
65662306a36Sopenharmony_ci		 * stop the search if we reach the current descriptor and the
65762306a36Sopenharmony_ci		 * channel is busy
65862306a36Sopenharmony_ci		 */
65962306a36Sopenharmony_ci		if (desc->async_tx.phys == curr_phys) {
66062306a36Sopenharmony_ci			seen_current = 1;
66162306a36Sopenharmony_ci			if (!dma_is_idle(chan))
66262306a36Sopenharmony_ci				break;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		cookie = fsldma_run_tx_complete_actions(chan, desc, cookie);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		fsldma_clean_running_descriptor(chan, desc);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/*
67162306a36Sopenharmony_ci	 * Start any pending transactions automatically
67262306a36Sopenharmony_ci	 *
67362306a36Sopenharmony_ci	 * In the ideal case, we keep the DMA controller busy while we go
67462306a36Sopenharmony_ci	 * ahead and free the descriptors below.
67562306a36Sopenharmony_ci	 */
67662306a36Sopenharmony_ci	fsl_chan_xfer_ld_queue(chan);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (cookie > 0)
67962306a36Sopenharmony_ci		chan->common.completed_cookie = cookie;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/**
68362306a36Sopenharmony_ci * fsl_dma_alloc_chan_resources - Allocate resources for DMA channel.
68462306a36Sopenharmony_ci * @chan : Freescale DMA channel
68562306a36Sopenharmony_ci *
68662306a36Sopenharmony_ci * This function will create a dma pool for descriptor allocation.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * Return - The number of descriptors allocated.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_cistatic int fsl_dma_alloc_chan_resources(struct dma_chan *dchan)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct fsldma_chan *chan = to_fsl_chan(dchan);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* Has this channel already been allocated? */
69562306a36Sopenharmony_ci	if (chan->desc_pool)
69662306a36Sopenharmony_ci		return 1;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/*
69962306a36Sopenharmony_ci	 * We need the descriptor to be aligned to 32bytes
70062306a36Sopenharmony_ci	 * for meeting FSL DMA specification requirement.
70162306a36Sopenharmony_ci	 */
70262306a36Sopenharmony_ci	chan->desc_pool = dma_pool_create(chan->name, chan->dev,
70362306a36Sopenharmony_ci					  sizeof(struct fsl_desc_sw),
70462306a36Sopenharmony_ci					  __alignof__(struct fsl_desc_sw), 0);
70562306a36Sopenharmony_ci	if (!chan->desc_pool) {
70662306a36Sopenharmony_ci		chan_err(chan, "unable to allocate descriptor pool\n");
70762306a36Sopenharmony_ci		return -ENOMEM;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* there is at least one descriptor free to be allocated */
71162306a36Sopenharmony_ci	return 1;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci/**
71562306a36Sopenharmony_ci * fsldma_free_desc_list - Free all descriptors in a queue
71662306a36Sopenharmony_ci * @chan: Freescae DMA channel
71762306a36Sopenharmony_ci * @list: the list to free
71862306a36Sopenharmony_ci *
71962306a36Sopenharmony_ci * LOCKING: must hold chan->desc_lock
72062306a36Sopenharmony_ci */
72162306a36Sopenharmony_cistatic void fsldma_free_desc_list(struct fsldma_chan *chan,
72262306a36Sopenharmony_ci				  struct list_head *list)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct fsl_desc_sw *desc, *_desc;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, list, node)
72762306a36Sopenharmony_ci		fsl_dma_free_descriptor(chan, desc);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic void fsldma_free_desc_list_reverse(struct fsldma_chan *chan,
73162306a36Sopenharmony_ci					  struct list_head *list)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct fsl_desc_sw *desc, *_desc;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(desc, _desc, list, node)
73662306a36Sopenharmony_ci		fsl_dma_free_descriptor(chan, desc);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/**
74062306a36Sopenharmony_ci * fsl_dma_free_chan_resources - Free all resources of the channel.
74162306a36Sopenharmony_ci * @chan : Freescale DMA channel
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cistatic void fsl_dma_free_chan_resources(struct dma_chan *dchan)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct fsldma_chan *chan = to_fsl_chan(dchan);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	chan_dbg(chan, "free all channel resources\n");
74862306a36Sopenharmony_ci	spin_lock_bh(&chan->desc_lock);
74962306a36Sopenharmony_ci	fsldma_cleanup_descriptors(chan);
75062306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_pending);
75162306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_running);
75262306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_completed);
75362306a36Sopenharmony_ci	spin_unlock_bh(&chan->desc_lock);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	dma_pool_destroy(chan->desc_pool);
75662306a36Sopenharmony_ci	chan->desc_pool = NULL;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
76062306a36Sopenharmony_cifsl_dma_prep_memcpy(struct dma_chan *dchan,
76162306a36Sopenharmony_ci	dma_addr_t dma_dst, dma_addr_t dma_src,
76262306a36Sopenharmony_ci	size_t len, unsigned long flags)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct fsldma_chan *chan;
76562306a36Sopenharmony_ci	struct fsl_desc_sw *first = NULL, *prev = NULL, *new;
76662306a36Sopenharmony_ci	size_t copy;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (!dchan)
76962306a36Sopenharmony_ci		return NULL;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!len)
77262306a36Sopenharmony_ci		return NULL;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	chan = to_fsl_chan(dchan);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	do {
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		/* Allocate the link descriptor from DMA pool */
77962306a36Sopenharmony_ci		new = fsl_dma_alloc_descriptor(chan);
78062306a36Sopenharmony_ci		if (!new) {
78162306a36Sopenharmony_ci			chan_err(chan, "%s\n", msg_ld_oom);
78262306a36Sopenharmony_ci			goto fail;
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		set_desc_cnt(chan, &new->hw, copy);
78862306a36Sopenharmony_ci		set_desc_src(chan, &new->hw, dma_src);
78962306a36Sopenharmony_ci		set_desc_dst(chan, &new->hw, dma_dst);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		if (!first)
79262306a36Sopenharmony_ci			first = new;
79362306a36Sopenharmony_ci		else
79462306a36Sopenharmony_ci			set_desc_next(chan, &prev->hw, new->async_tx.phys);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		new->async_tx.cookie = 0;
79762306a36Sopenharmony_ci		async_tx_ack(&new->async_tx);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		prev = new;
80062306a36Sopenharmony_ci		len -= copy;
80162306a36Sopenharmony_ci		dma_src += copy;
80262306a36Sopenharmony_ci		dma_dst += copy;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		/* Insert the link descriptor to the LD ring */
80562306a36Sopenharmony_ci		list_add_tail(&new->node, &first->tx_list);
80662306a36Sopenharmony_ci	} while (len);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	new->async_tx.flags = flags; /* client is in control of this ack */
80962306a36Sopenharmony_ci	new->async_tx.cookie = -EBUSY;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* Set End-of-link to the last link descriptor of new list */
81262306a36Sopenharmony_ci	set_ld_eol(chan, new);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return &first->async_tx;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cifail:
81762306a36Sopenharmony_ci	if (!first)
81862306a36Sopenharmony_ci		return NULL;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	fsldma_free_desc_list_reverse(chan, &first->tx_list);
82162306a36Sopenharmony_ci	return NULL;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic int fsl_dma_device_terminate_all(struct dma_chan *dchan)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct fsldma_chan *chan;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (!dchan)
82962306a36Sopenharmony_ci		return -EINVAL;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	chan = to_fsl_chan(dchan);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	spin_lock_bh(&chan->desc_lock);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Halt the DMA engine */
83662306a36Sopenharmony_ci	dma_halt(chan);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Remove and free all of the descriptors in the LD queue */
83962306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_pending);
84062306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_running);
84162306a36Sopenharmony_ci	fsldma_free_desc_list(chan, &chan->ld_completed);
84262306a36Sopenharmony_ci	chan->idle = true;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	spin_unlock_bh(&chan->desc_lock);
84562306a36Sopenharmony_ci	return 0;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic int fsl_dma_device_config(struct dma_chan *dchan,
84962306a36Sopenharmony_ci				 struct dma_slave_config *config)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct fsldma_chan *chan;
85262306a36Sopenharmony_ci	int size;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (!dchan)
85562306a36Sopenharmony_ci		return -EINVAL;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	chan = to_fsl_chan(dchan);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* make sure the channel supports setting burst size */
86062306a36Sopenharmony_ci	if (!chan->set_request_count)
86162306a36Sopenharmony_ci		return -ENXIO;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* we set the controller burst size depending on direction */
86462306a36Sopenharmony_ci	if (config->direction == DMA_MEM_TO_DEV)
86562306a36Sopenharmony_ci		size = config->dst_addr_width * config->dst_maxburst;
86662306a36Sopenharmony_ci	else
86762306a36Sopenharmony_ci		size = config->src_addr_width * config->src_maxburst;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	chan->set_request_count(chan, size);
87062306a36Sopenharmony_ci	return 0;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/**
87562306a36Sopenharmony_ci * fsl_dma_memcpy_issue_pending - Issue the DMA start command
87662306a36Sopenharmony_ci * @chan : Freescale DMA channel
87762306a36Sopenharmony_ci */
87862306a36Sopenharmony_cistatic void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct fsldma_chan *chan = to_fsl_chan(dchan);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	spin_lock_bh(&chan->desc_lock);
88362306a36Sopenharmony_ci	fsl_chan_xfer_ld_queue(chan);
88462306a36Sopenharmony_ci	spin_unlock_bh(&chan->desc_lock);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci/**
88862306a36Sopenharmony_ci * fsl_tx_status - Determine the DMA status
88962306a36Sopenharmony_ci * @chan : Freescale DMA channel
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_cistatic enum dma_status fsl_tx_status(struct dma_chan *dchan,
89262306a36Sopenharmony_ci					dma_cookie_t cookie,
89362306a36Sopenharmony_ci					struct dma_tx_state *txstate)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	struct fsldma_chan *chan = to_fsl_chan(dchan);
89662306a36Sopenharmony_ci	enum dma_status ret;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ret = dma_cookie_status(dchan, cookie, txstate);
89962306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
90062306a36Sopenharmony_ci		return ret;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	spin_lock_bh(&chan->desc_lock);
90362306a36Sopenharmony_ci	fsldma_cleanup_descriptors(chan);
90462306a36Sopenharmony_ci	spin_unlock_bh(&chan->desc_lock);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return dma_cookie_status(dchan, cookie, txstate);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
91062306a36Sopenharmony_ci/* Interrupt Handling                                                         */
91162306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic irqreturn_t fsldma_chan_irq(int irq, void *data)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct fsldma_chan *chan = data;
91662306a36Sopenharmony_ci	u32 stat;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* save and clear the status register */
91962306a36Sopenharmony_ci	stat = get_sr(chan);
92062306a36Sopenharmony_ci	set_sr(chan, stat);
92162306a36Sopenharmony_ci	chan_dbg(chan, "irq: stat = 0x%x\n", stat);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* check that this was really our device */
92462306a36Sopenharmony_ci	stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH);
92562306a36Sopenharmony_ci	if (!stat)
92662306a36Sopenharmony_ci		return IRQ_NONE;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (stat & FSL_DMA_SR_TE)
92962306a36Sopenharmony_ci		chan_err(chan, "Transfer Error!\n");
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/*
93262306a36Sopenharmony_ci	 * Programming Error
93362306a36Sopenharmony_ci	 * The DMA_INTERRUPT async_tx is a NULL transfer, which will
93462306a36Sopenharmony_ci	 * trigger a PE interrupt.
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	if (stat & FSL_DMA_SR_PE) {
93762306a36Sopenharmony_ci		chan_dbg(chan, "irq: Programming Error INT\n");
93862306a36Sopenharmony_ci		stat &= ~FSL_DMA_SR_PE;
93962306a36Sopenharmony_ci		if (get_bcr(chan) != 0)
94062306a36Sopenharmony_ci			chan_err(chan, "Programming Error!\n");
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/*
94462306a36Sopenharmony_ci	 * For MPC8349, EOCDI event need to update cookie
94562306a36Sopenharmony_ci	 * and start the next transfer if it exist.
94662306a36Sopenharmony_ci	 */
94762306a36Sopenharmony_ci	if (stat & FSL_DMA_SR_EOCDI) {
94862306a36Sopenharmony_ci		chan_dbg(chan, "irq: End-of-Chain link INT\n");
94962306a36Sopenharmony_ci		stat &= ~FSL_DMA_SR_EOCDI;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/*
95362306a36Sopenharmony_ci	 * If it current transfer is the end-of-transfer,
95462306a36Sopenharmony_ci	 * we should clear the Channel Start bit for
95562306a36Sopenharmony_ci	 * prepare next transfer.
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci	if (stat & FSL_DMA_SR_EOLNI) {
95862306a36Sopenharmony_ci		chan_dbg(chan, "irq: End-of-link INT\n");
95962306a36Sopenharmony_ci		stat &= ~FSL_DMA_SR_EOLNI;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* check that the DMA controller is really idle */
96362306a36Sopenharmony_ci	if (!dma_is_idle(chan))
96462306a36Sopenharmony_ci		chan_err(chan, "irq: controller not idle!\n");
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	/* check that we handled all of the bits */
96762306a36Sopenharmony_ci	if (stat)
96862306a36Sopenharmony_ci		chan_err(chan, "irq: unhandled sr 0x%08x\n", stat);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/*
97162306a36Sopenharmony_ci	 * Schedule the tasklet to handle all cleanup of the current
97262306a36Sopenharmony_ci	 * transaction. It will start a new transaction if there is
97362306a36Sopenharmony_ci	 * one pending.
97462306a36Sopenharmony_ci	 */
97562306a36Sopenharmony_ci	tasklet_schedule(&chan->tasklet);
97662306a36Sopenharmony_ci	chan_dbg(chan, "irq: Exit\n");
97762306a36Sopenharmony_ci	return IRQ_HANDLED;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void dma_do_tasklet(struct tasklet_struct *t)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct fsldma_chan *chan = from_tasklet(chan, t, tasklet);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	chan_dbg(chan, "tasklet entry\n");
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	spin_lock(&chan->desc_lock);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* the hardware is now idle and ready for more */
98962306a36Sopenharmony_ci	chan->idle = true;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Run all cleanup for descriptors which have been completed */
99262306a36Sopenharmony_ci	fsldma_cleanup_descriptors(chan);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	spin_unlock(&chan->desc_lock);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	chan_dbg(chan, "tasklet exit\n");
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic irqreturn_t fsldma_ctrl_irq(int irq, void *data)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct fsldma_device *fdev = data;
100262306a36Sopenharmony_ci	struct fsldma_chan *chan;
100362306a36Sopenharmony_ci	unsigned int handled = 0;
100462306a36Sopenharmony_ci	u32 gsr, mask;
100562306a36Sopenharmony_ci	int i;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	gsr = (fdev->feature & FSL_DMA_BIG_ENDIAN) ? in_be32(fdev->regs)
100862306a36Sopenharmony_ci						   : in_le32(fdev->regs);
100962306a36Sopenharmony_ci	mask = 0xff000000;
101062306a36Sopenharmony_ci	dev_dbg(fdev->dev, "IRQ: gsr 0x%.8x\n", gsr);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
101362306a36Sopenharmony_ci		chan = fdev->chan[i];
101462306a36Sopenharmony_ci		if (!chan)
101562306a36Sopenharmony_ci			continue;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		if (gsr & mask) {
101862306a36Sopenharmony_ci			dev_dbg(fdev->dev, "IRQ: chan %d\n", chan->id);
101962306a36Sopenharmony_ci			fsldma_chan_irq(irq, chan);
102062306a36Sopenharmony_ci			handled++;
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		gsr &= ~mask;
102462306a36Sopenharmony_ci		mask >>= 8;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic void fsldma_free_irqs(struct fsldma_device *fdev)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct fsldma_chan *chan;
103362306a36Sopenharmony_ci	int i;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (fdev->irq) {
103662306a36Sopenharmony_ci		dev_dbg(fdev->dev, "free per-controller IRQ\n");
103762306a36Sopenharmony_ci		free_irq(fdev->irq, fdev);
103862306a36Sopenharmony_ci		return;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
104262306a36Sopenharmony_ci		chan = fdev->chan[i];
104362306a36Sopenharmony_ci		if (chan && chan->irq) {
104462306a36Sopenharmony_ci			chan_dbg(chan, "free per-channel IRQ\n");
104562306a36Sopenharmony_ci			free_irq(chan->irq, chan);
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic int fsldma_request_irqs(struct fsldma_device *fdev)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	struct fsldma_chan *chan;
105362306a36Sopenharmony_ci	int ret;
105462306a36Sopenharmony_ci	int i;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	/* if we have a per-controller IRQ, use that */
105762306a36Sopenharmony_ci	if (fdev->irq) {
105862306a36Sopenharmony_ci		dev_dbg(fdev->dev, "request per-controller IRQ\n");
105962306a36Sopenharmony_ci		ret = request_irq(fdev->irq, fsldma_ctrl_irq, IRQF_SHARED,
106062306a36Sopenharmony_ci				  "fsldma-controller", fdev);
106162306a36Sopenharmony_ci		return ret;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* no per-controller IRQ, use the per-channel IRQs */
106562306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
106662306a36Sopenharmony_ci		chan = fdev->chan[i];
106762306a36Sopenharmony_ci		if (!chan)
106862306a36Sopenharmony_ci			continue;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		if (!chan->irq) {
107162306a36Sopenharmony_ci			chan_err(chan, "interrupts property missing in device tree\n");
107262306a36Sopenharmony_ci			ret = -ENODEV;
107362306a36Sopenharmony_ci			goto out_unwind;
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		chan_dbg(chan, "request per-channel IRQ\n");
107762306a36Sopenharmony_ci		ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED,
107862306a36Sopenharmony_ci				  "fsldma-chan", chan);
107962306a36Sopenharmony_ci		if (ret) {
108062306a36Sopenharmony_ci			chan_err(chan, "unable to request per-channel IRQ\n");
108162306a36Sopenharmony_ci			goto out_unwind;
108262306a36Sopenharmony_ci		}
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return 0;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ciout_unwind:
108862306a36Sopenharmony_ci	for (/* none */; i >= 0; i--) {
108962306a36Sopenharmony_ci		chan = fdev->chan[i];
109062306a36Sopenharmony_ci		if (!chan)
109162306a36Sopenharmony_ci			continue;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		if (!chan->irq)
109462306a36Sopenharmony_ci			continue;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		free_irq(chan->irq, chan);
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return ret;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
110362306a36Sopenharmony_ci/* OpenFirmware Subsystem                                                     */
110462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic int fsl_dma_chan_probe(struct fsldma_device *fdev,
110762306a36Sopenharmony_ci	struct device_node *node, u32 feature, const char *compatible)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct fsldma_chan *chan;
111062306a36Sopenharmony_ci	struct resource res;
111162306a36Sopenharmony_ci	int err;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* alloc channel */
111462306a36Sopenharmony_ci	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
111562306a36Sopenharmony_ci	if (!chan) {
111662306a36Sopenharmony_ci		err = -ENOMEM;
111762306a36Sopenharmony_ci		goto out_return;
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* ioremap registers for use */
112162306a36Sopenharmony_ci	chan->regs = of_iomap(node, 0);
112262306a36Sopenharmony_ci	if (!chan->regs) {
112362306a36Sopenharmony_ci		dev_err(fdev->dev, "unable to ioremap registers\n");
112462306a36Sopenharmony_ci		err = -ENOMEM;
112562306a36Sopenharmony_ci		goto out_free_chan;
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	err = of_address_to_resource(node, 0, &res);
112962306a36Sopenharmony_ci	if (err) {
113062306a36Sopenharmony_ci		dev_err(fdev->dev, "unable to find 'reg' property\n");
113162306a36Sopenharmony_ci		goto out_iounmap_regs;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	chan->feature = feature;
113562306a36Sopenharmony_ci	if (!fdev->feature)
113662306a36Sopenharmony_ci		fdev->feature = chan->feature;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/*
113962306a36Sopenharmony_ci	 * If the DMA device's feature is different than the feature
114062306a36Sopenharmony_ci	 * of its channels, report the bug
114162306a36Sopenharmony_ci	 */
114262306a36Sopenharmony_ci	WARN_ON(fdev->feature != chan->feature);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	chan->dev = fdev->dev;
114562306a36Sopenharmony_ci	chan->id = (res.start & 0xfff) < 0x300 ?
114662306a36Sopenharmony_ci		   ((res.start - 0x100) & 0xfff) >> 7 :
114762306a36Sopenharmony_ci		   ((res.start - 0x200) & 0xfff) >> 7;
114862306a36Sopenharmony_ci	if (chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
114962306a36Sopenharmony_ci		dev_err(fdev->dev, "too many channels for device\n");
115062306a36Sopenharmony_ci		err = -EINVAL;
115162306a36Sopenharmony_ci		goto out_iounmap_regs;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	fdev->chan[chan->id] = chan;
115562306a36Sopenharmony_ci	tasklet_setup(&chan->tasklet, dma_do_tasklet);
115662306a36Sopenharmony_ci	snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* Initialize the channel */
115962306a36Sopenharmony_ci	dma_init(chan);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* Clear cdar registers */
116262306a36Sopenharmony_ci	set_cdar(chan, 0);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	switch (chan->feature & FSL_DMA_IP_MASK) {
116562306a36Sopenharmony_ci	case FSL_DMA_IP_85XX:
116662306a36Sopenharmony_ci		chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
116762306a36Sopenharmony_ci		fallthrough;
116862306a36Sopenharmony_ci	case FSL_DMA_IP_83XX:
116962306a36Sopenharmony_ci		chan->toggle_ext_start = fsl_chan_toggle_ext_start;
117062306a36Sopenharmony_ci		chan->set_src_loop_size = fsl_chan_set_src_loop_size;
117162306a36Sopenharmony_ci		chan->set_dst_loop_size = fsl_chan_set_dst_loop_size;
117262306a36Sopenharmony_ci		chan->set_request_count = fsl_chan_set_request_count;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	spin_lock_init(&chan->desc_lock);
117662306a36Sopenharmony_ci	INIT_LIST_HEAD(&chan->ld_pending);
117762306a36Sopenharmony_ci	INIT_LIST_HEAD(&chan->ld_running);
117862306a36Sopenharmony_ci	INIT_LIST_HEAD(&chan->ld_completed);
117962306a36Sopenharmony_ci	chan->idle = true;
118062306a36Sopenharmony_ci#ifdef CONFIG_PM
118162306a36Sopenharmony_ci	chan->pm_state = RUNNING;
118262306a36Sopenharmony_ci#endif
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	chan->common.device = &fdev->common;
118562306a36Sopenharmony_ci	dma_cookie_init(&chan->common);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/* find the IRQ line, if it exists in the device tree */
118862306a36Sopenharmony_ci	chan->irq = irq_of_parse_and_map(node, 0);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Add the channel to DMA device channel list */
119162306a36Sopenharmony_ci	list_add_tail(&chan->common.device_node, &fdev->common.channels);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	dev_info(fdev->dev, "#%d (%s), irq %d\n", chan->id, compatible,
119462306a36Sopenharmony_ci		 chan->irq ? chan->irq : fdev->irq);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return 0;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ciout_iounmap_regs:
119962306a36Sopenharmony_ci	iounmap(chan->regs);
120062306a36Sopenharmony_ciout_free_chan:
120162306a36Sopenharmony_ci	kfree(chan);
120262306a36Sopenharmony_ciout_return:
120362306a36Sopenharmony_ci	return err;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic void fsl_dma_chan_remove(struct fsldma_chan *chan)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	irq_dispose_mapping(chan->irq);
120962306a36Sopenharmony_ci	list_del(&chan->common.device_node);
121062306a36Sopenharmony_ci	iounmap(chan->regs);
121162306a36Sopenharmony_ci	kfree(chan);
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int fsldma_of_probe(struct platform_device *op)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct fsldma_device *fdev;
121762306a36Sopenharmony_ci	struct device_node *child;
121862306a36Sopenharmony_ci	unsigned int i;
121962306a36Sopenharmony_ci	int err;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	fdev = kzalloc(sizeof(*fdev), GFP_KERNEL);
122262306a36Sopenharmony_ci	if (!fdev) {
122362306a36Sopenharmony_ci		err = -ENOMEM;
122462306a36Sopenharmony_ci		goto out_return;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	fdev->dev = &op->dev;
122862306a36Sopenharmony_ci	INIT_LIST_HEAD(&fdev->common.channels);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* ioremap the registers for use */
123162306a36Sopenharmony_ci	fdev->regs = of_iomap(op->dev.of_node, 0);
123262306a36Sopenharmony_ci	if (!fdev->regs) {
123362306a36Sopenharmony_ci		dev_err(&op->dev, "unable to ioremap registers\n");
123462306a36Sopenharmony_ci		err = -ENOMEM;
123562306a36Sopenharmony_ci		goto out_free;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* map the channel IRQ if it exists, but don't hookup the handler yet */
123962306a36Sopenharmony_ci	fdev->irq = irq_of_parse_and_map(op->dev.of_node, 0);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
124262306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
124362306a36Sopenharmony_ci	fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
124462306a36Sopenharmony_ci	fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources;
124562306a36Sopenharmony_ci	fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
124662306a36Sopenharmony_ci	fdev->common.device_tx_status = fsl_tx_status;
124762306a36Sopenharmony_ci	fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
124862306a36Sopenharmony_ci	fdev->common.device_config = fsl_dma_device_config;
124962306a36Sopenharmony_ci	fdev->common.device_terminate_all = fsl_dma_device_terminate_all;
125062306a36Sopenharmony_ci	fdev->common.dev = &op->dev;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	fdev->common.src_addr_widths = FSL_DMA_BUSWIDTHS;
125362306a36Sopenharmony_ci	fdev->common.dst_addr_widths = FSL_DMA_BUSWIDTHS;
125462306a36Sopenharmony_ci	fdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
125562306a36Sopenharmony_ci	fdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	dma_set_mask(&(op->dev), DMA_BIT_MASK(36));
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	platform_set_drvdata(op, fdev);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/*
126262306a36Sopenharmony_ci	 * We cannot use of_platform_bus_probe() because there is no
126362306a36Sopenharmony_ci	 * of_platform_bus_remove(). Instead, we manually instantiate every DMA
126462306a36Sopenharmony_ci	 * channel object.
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	for_each_child_of_node(op->dev.of_node, child) {
126762306a36Sopenharmony_ci		if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) {
126862306a36Sopenharmony_ci			fsl_dma_chan_probe(fdev, child,
126962306a36Sopenharmony_ci				FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN,
127062306a36Sopenharmony_ci				"fsl,eloplus-dma-channel");
127162306a36Sopenharmony_ci		}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		if (of_device_is_compatible(child, "fsl,elo-dma-channel")) {
127462306a36Sopenharmony_ci			fsl_dma_chan_probe(fdev, child,
127562306a36Sopenharmony_ci				FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN,
127662306a36Sopenharmony_ci				"fsl,elo-dma-channel");
127762306a36Sopenharmony_ci		}
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/*
128162306a36Sopenharmony_ci	 * Hookup the IRQ handler(s)
128262306a36Sopenharmony_ci	 *
128362306a36Sopenharmony_ci	 * If we have a per-controller interrupt, we prefer that to the
128462306a36Sopenharmony_ci	 * per-channel interrupts to reduce the number of shared interrupt
128562306a36Sopenharmony_ci	 * handlers on the same IRQ line
128662306a36Sopenharmony_ci	 */
128762306a36Sopenharmony_ci	err = fsldma_request_irqs(fdev);
128862306a36Sopenharmony_ci	if (err) {
128962306a36Sopenharmony_ci		dev_err(fdev->dev, "unable to request IRQs\n");
129062306a36Sopenharmony_ci		goto out_free_fdev;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	dma_async_device_register(&fdev->common);
129462306a36Sopenharmony_ci	return 0;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ciout_free_fdev:
129762306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
129862306a36Sopenharmony_ci		if (fdev->chan[i])
129962306a36Sopenharmony_ci			fsl_dma_chan_remove(fdev->chan[i]);
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci	irq_dispose_mapping(fdev->irq);
130262306a36Sopenharmony_ci	iounmap(fdev->regs);
130362306a36Sopenharmony_ciout_free:
130462306a36Sopenharmony_ci	kfree(fdev);
130562306a36Sopenharmony_ciout_return:
130662306a36Sopenharmony_ci	return err;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic int fsldma_of_remove(struct platform_device *op)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct fsldma_device *fdev;
131262306a36Sopenharmony_ci	unsigned int i;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	fdev = platform_get_drvdata(op);
131562306a36Sopenharmony_ci	dma_async_device_unregister(&fdev->common);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	fsldma_free_irqs(fdev);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
132062306a36Sopenharmony_ci		if (fdev->chan[i])
132162306a36Sopenharmony_ci			fsl_dma_chan_remove(fdev->chan[i]);
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci	irq_dispose_mapping(fdev->irq);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	iounmap(fdev->regs);
132662306a36Sopenharmony_ci	kfree(fdev);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	return 0;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci#ifdef CONFIG_PM
133262306a36Sopenharmony_cistatic int fsldma_suspend_late(struct device *dev)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct fsldma_device *fdev = dev_get_drvdata(dev);
133562306a36Sopenharmony_ci	struct fsldma_chan *chan;
133662306a36Sopenharmony_ci	int i;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
133962306a36Sopenharmony_ci		chan = fdev->chan[i];
134062306a36Sopenharmony_ci		if (!chan)
134162306a36Sopenharmony_ci			continue;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		spin_lock_bh(&chan->desc_lock);
134462306a36Sopenharmony_ci		if (unlikely(!chan->idle))
134562306a36Sopenharmony_ci			goto out;
134662306a36Sopenharmony_ci		chan->regs_save.mr = get_mr(chan);
134762306a36Sopenharmony_ci		chan->pm_state = SUSPENDED;
134862306a36Sopenharmony_ci		spin_unlock_bh(&chan->desc_lock);
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci	return 0;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ciout:
135362306a36Sopenharmony_ci	for (; i >= 0; i--) {
135462306a36Sopenharmony_ci		chan = fdev->chan[i];
135562306a36Sopenharmony_ci		if (!chan)
135662306a36Sopenharmony_ci			continue;
135762306a36Sopenharmony_ci		chan->pm_state = RUNNING;
135862306a36Sopenharmony_ci		spin_unlock_bh(&chan->desc_lock);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci	return -EBUSY;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic int fsldma_resume_early(struct device *dev)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	struct fsldma_device *fdev = dev_get_drvdata(dev);
136662306a36Sopenharmony_ci	struct fsldma_chan *chan;
136762306a36Sopenharmony_ci	u32 mode;
136862306a36Sopenharmony_ci	int i;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
137162306a36Sopenharmony_ci		chan = fdev->chan[i];
137262306a36Sopenharmony_ci		if (!chan)
137362306a36Sopenharmony_ci			continue;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		spin_lock_bh(&chan->desc_lock);
137662306a36Sopenharmony_ci		mode = chan->regs_save.mr
137762306a36Sopenharmony_ci			& ~FSL_DMA_MR_CS & ~FSL_DMA_MR_CC & ~FSL_DMA_MR_CA;
137862306a36Sopenharmony_ci		set_mr(chan, mode);
137962306a36Sopenharmony_ci		chan->pm_state = RUNNING;
138062306a36Sopenharmony_ci		spin_unlock_bh(&chan->desc_lock);
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	return 0;
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic const struct dev_pm_ops fsldma_pm_ops = {
138762306a36Sopenharmony_ci	.suspend_late	= fsldma_suspend_late,
138862306a36Sopenharmony_ci	.resume_early	= fsldma_resume_early,
138962306a36Sopenharmony_ci};
139062306a36Sopenharmony_ci#endif
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic const struct of_device_id fsldma_of_ids[] = {
139362306a36Sopenharmony_ci	{ .compatible = "fsl,elo3-dma", },
139462306a36Sopenharmony_ci	{ .compatible = "fsl,eloplus-dma", },
139562306a36Sopenharmony_ci	{ .compatible = "fsl,elo-dma", },
139662306a36Sopenharmony_ci	{}
139762306a36Sopenharmony_ci};
139862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsldma_of_ids);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cistatic struct platform_driver fsldma_of_driver = {
140162306a36Sopenharmony_ci	.driver = {
140262306a36Sopenharmony_ci		.name = "fsl-elo-dma",
140362306a36Sopenharmony_ci		.of_match_table = fsldma_of_ids,
140462306a36Sopenharmony_ci#ifdef CONFIG_PM
140562306a36Sopenharmony_ci		.pm = &fsldma_pm_ops,
140662306a36Sopenharmony_ci#endif
140762306a36Sopenharmony_ci	},
140862306a36Sopenharmony_ci	.probe = fsldma_of_probe,
140962306a36Sopenharmony_ci	.remove = fsldma_of_remove,
141062306a36Sopenharmony_ci};
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
141362306a36Sopenharmony_ci/* Module Init / Exit                                                         */
141462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic __init int fsldma_init(void)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	pr_info("Freescale Elo series DMA driver\n");
141962306a36Sopenharmony_ci	return platform_driver_register(&fsldma_of_driver);
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic void __exit fsldma_exit(void)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	platform_driver_unregister(&fsldma_of_driver);
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cisubsys_initcall(fsldma_init);
142862306a36Sopenharmony_cimodule_exit(fsldma_exit);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Elo series DMA driver");
143162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1432