162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * offload engine driver for the Marvell XOR engine
462306a36Sopenharmony_ci * Copyright (C) 2007, 2008, Marvell International Ltd.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/of_device.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/memory.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/of_irq.h>
1962306a36Sopenharmony_ci#include <linux/irqdomain.h>
2062306a36Sopenharmony_ci#include <linux/cpumask.h>
2162306a36Sopenharmony_ci#include <linux/platform_data/dma-mv_xor.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "dmaengine.h"
2462306a36Sopenharmony_ci#include "mv_xor.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cienum mv_xor_type {
2762306a36Sopenharmony_ci	XOR_ORION,
2862306a36Sopenharmony_ci	XOR_ARMADA_38X,
2962306a36Sopenharmony_ci	XOR_ARMADA_37XX,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cienum mv_xor_mode {
3362306a36Sopenharmony_ci	XOR_MODE_IN_REG,
3462306a36Sopenharmony_ci	XOR_MODE_IN_DESC,
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void mv_xor_issue_pending(struct dma_chan *chan);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define to_mv_xor_chan(chan)		\
4062306a36Sopenharmony_ci	container_of(chan, struct mv_xor_chan, dmachan)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define to_mv_xor_slot(tx)		\
4362306a36Sopenharmony_ci	container_of(tx, struct mv_xor_desc_slot, async_tx)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define mv_chan_to_devp(chan)           \
4662306a36Sopenharmony_ci	((chan)->dmadev.dev)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void mv_desc_init(struct mv_xor_desc_slot *desc,
4962306a36Sopenharmony_ci			 dma_addr_t addr, u32 byte_count,
5062306a36Sopenharmony_ci			 enum dma_ctrl_flags flags)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct mv_xor_desc *hw_desc = desc->hw_desc;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	hw_desc->status = XOR_DESC_DMA_OWNED;
5562306a36Sopenharmony_ci	hw_desc->phy_next_desc = 0;
5662306a36Sopenharmony_ci	/* Enable end-of-descriptor interrupts only for DMA_PREP_INTERRUPT */
5762306a36Sopenharmony_ci	hw_desc->desc_command = (flags & DMA_PREP_INTERRUPT) ?
5862306a36Sopenharmony_ci				XOR_DESC_EOD_INT_EN : 0;
5962306a36Sopenharmony_ci	hw_desc->phy_dest_addr = addr;
6062306a36Sopenharmony_ci	hw_desc->byte_count = byte_count;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void mv_desc_set_mode(struct mv_xor_desc_slot *desc)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct mv_xor_desc *hw_desc = desc->hw_desc;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	switch (desc->type) {
6862306a36Sopenharmony_ci	case DMA_XOR:
6962306a36Sopenharmony_ci	case DMA_INTERRUPT:
7062306a36Sopenharmony_ci		hw_desc->desc_command |= XOR_DESC_OPERATION_XOR;
7162306a36Sopenharmony_ci		break;
7262306a36Sopenharmony_ci	case DMA_MEMCPY:
7362306a36Sopenharmony_ci		hw_desc->desc_command |= XOR_DESC_OPERATION_MEMCPY;
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	default:
7662306a36Sopenharmony_ci		BUG();
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void mv_desc_set_next_desc(struct mv_xor_desc_slot *desc,
8262306a36Sopenharmony_ci				  u32 next_desc_addr)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct mv_xor_desc *hw_desc = desc->hw_desc;
8562306a36Sopenharmony_ci	BUG_ON(hw_desc->phy_next_desc);
8662306a36Sopenharmony_ci	hw_desc->phy_next_desc = next_desc_addr;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void mv_desc_set_src_addr(struct mv_xor_desc_slot *desc,
9062306a36Sopenharmony_ci				 int index, dma_addr_t addr)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct mv_xor_desc *hw_desc = desc->hw_desc;
9362306a36Sopenharmony_ci	hw_desc->phy_src_addr[mv_phy_src_idx(index)] = addr;
9462306a36Sopenharmony_ci	if (desc->type == DMA_XOR)
9562306a36Sopenharmony_ci		hw_desc->desc_command |= (1 << index);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic u32 mv_chan_get_current_desc(struct mv_xor_chan *chan)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	return readl_relaxed(XOR_CURR_DESC(chan));
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void mv_chan_set_next_descriptor(struct mv_xor_chan *chan,
10462306a36Sopenharmony_ci					u32 next_desc_addr)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	writel_relaxed(next_desc_addr, XOR_NEXT_DESC(chan));
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void mv_chan_unmask_interrupts(struct mv_xor_chan *chan)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	u32 val = readl_relaxed(XOR_INTR_MASK(chan));
11262306a36Sopenharmony_ci	val |= XOR_INTR_MASK_VALUE << (chan->idx * 16);
11362306a36Sopenharmony_ci	writel_relaxed(val, XOR_INTR_MASK(chan));
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic u32 mv_chan_get_intr_cause(struct mv_xor_chan *chan)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u32 intr_cause = readl_relaxed(XOR_INTR_CAUSE(chan));
11962306a36Sopenharmony_ci	intr_cause = (intr_cause >> (chan->idx * 16)) & 0xFFFF;
12062306a36Sopenharmony_ci	return intr_cause;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void mv_chan_clear_eoc_cause(struct mv_xor_chan *chan)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u32 val;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	val = XOR_INT_END_OF_DESC | XOR_INT_END_OF_CHAIN | XOR_INT_STOPPED;
12862306a36Sopenharmony_ci	val = ~(val << (chan->idx * 16));
12962306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(chan), "%s, val 0x%08x\n", __func__, val);
13062306a36Sopenharmony_ci	writel_relaxed(val, XOR_INTR_CAUSE(chan));
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void mv_chan_clear_err_status(struct mv_xor_chan *chan)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	u32 val = 0xFFFF0000 >> (chan->idx * 16);
13662306a36Sopenharmony_ci	writel_relaxed(val, XOR_INTR_CAUSE(chan));
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void mv_chan_set_mode(struct mv_xor_chan *chan,
14062306a36Sopenharmony_ci			     u32 op_mode)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	u32 config = readl_relaxed(XOR_CONFIG(chan));
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	config &= ~0x7;
14562306a36Sopenharmony_ci	config |= op_mode;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#if defined(__BIG_ENDIAN)
14862306a36Sopenharmony_ci	config |= XOR_DESCRIPTOR_SWAP;
14962306a36Sopenharmony_ci#else
15062306a36Sopenharmony_ci	config &= ~XOR_DESCRIPTOR_SWAP;
15162306a36Sopenharmony_ci#endif
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	writel_relaxed(config, XOR_CONFIG(chan));
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void mv_chan_activate(struct mv_xor_chan *chan)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(chan), " activate chan.\n");
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* writel ensures all descriptors are flushed before activation */
16162306a36Sopenharmony_ci	writel(BIT(0), XOR_ACTIVATION(chan));
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic char mv_chan_is_busy(struct mv_xor_chan *chan)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u32 state = readl_relaxed(XOR_ACTIVATION(chan));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	state = (state >> 4) & 0x3;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return (state == 1) ? 1 : 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*
17462306a36Sopenharmony_ci * mv_chan_start_new_chain - program the engine to operate on new
17562306a36Sopenharmony_ci * chain headed by sw_desc
17662306a36Sopenharmony_ci * Caller must hold &mv_chan->lock while calling this function
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_cistatic void mv_chan_start_new_chain(struct mv_xor_chan *mv_chan,
17962306a36Sopenharmony_ci				    struct mv_xor_desc_slot *sw_desc)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: sw_desc %p\n",
18262306a36Sopenharmony_ci		__func__, __LINE__, sw_desc);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* set the hardware chain */
18562306a36Sopenharmony_ci	mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	mv_chan->pending++;
18862306a36Sopenharmony_ci	mv_xor_issue_pending(&mv_chan->dmachan);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic dma_cookie_t
19262306a36Sopenharmony_cimv_desc_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
19362306a36Sopenharmony_ci				struct mv_xor_chan *mv_chan,
19462306a36Sopenharmony_ci				dma_cookie_t cookie)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	BUG_ON(desc->async_tx.cookie < 0);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (desc->async_tx.cookie > 0) {
19962306a36Sopenharmony_ci		cookie = desc->async_tx.cookie;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		dma_descriptor_unmap(&desc->async_tx);
20262306a36Sopenharmony_ci		/* call the callback (must not sleep or submit new
20362306a36Sopenharmony_ci		 * operations to this channel)
20462306a36Sopenharmony_ci		 */
20562306a36Sopenharmony_ci		dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* run dependent operations */
20962306a36Sopenharmony_ci	dma_run_dependencies(&desc->async_tx);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return cookie;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int
21562306a36Sopenharmony_cimv_chan_clean_completed_slots(struct mv_xor_chan *mv_chan)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct mv_xor_desc_slot *iter, *_iter;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
22062306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
22162306a36Sopenharmony_ci				 node) {
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		if (async_tx_test_ack(&iter->async_tx)) {
22462306a36Sopenharmony_ci			list_move_tail(&iter->node, &mv_chan->free_slots);
22562306a36Sopenharmony_ci			if (!list_empty(&iter->sg_tx_list)) {
22662306a36Sopenharmony_ci				list_splice_tail_init(&iter->sg_tx_list,
22762306a36Sopenharmony_ci							&mv_chan->free_slots);
22862306a36Sopenharmony_ci			}
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int
23562306a36Sopenharmony_cimv_desc_clean_slot(struct mv_xor_desc_slot *desc,
23662306a36Sopenharmony_ci		   struct mv_xor_chan *mv_chan)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n",
23962306a36Sopenharmony_ci		__func__, __LINE__, desc, desc->async_tx.flags);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* the client is allowed to attach dependent operations
24262306a36Sopenharmony_ci	 * until 'ack' is set
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	if (!async_tx_test_ack(&desc->async_tx)) {
24562306a36Sopenharmony_ci		/* move this slot to the completed_slots */
24662306a36Sopenharmony_ci		list_move_tail(&desc->node, &mv_chan->completed_slots);
24762306a36Sopenharmony_ci		if (!list_empty(&desc->sg_tx_list)) {
24862306a36Sopenharmony_ci			list_splice_tail_init(&desc->sg_tx_list,
24962306a36Sopenharmony_ci					      &mv_chan->completed_slots);
25062306a36Sopenharmony_ci		}
25162306a36Sopenharmony_ci	} else {
25262306a36Sopenharmony_ci		list_move_tail(&desc->node, &mv_chan->free_slots);
25362306a36Sopenharmony_ci		if (!list_empty(&desc->sg_tx_list)) {
25462306a36Sopenharmony_ci			list_splice_tail_init(&desc->sg_tx_list,
25562306a36Sopenharmony_ci					      &mv_chan->free_slots);
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/* This function must be called with the mv_xor_chan spinlock held */
26362306a36Sopenharmony_cistatic void mv_chan_slot_cleanup(struct mv_xor_chan *mv_chan)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct mv_xor_desc_slot *iter, *_iter;
26662306a36Sopenharmony_ci	dma_cookie_t cookie = 0;
26762306a36Sopenharmony_ci	int busy = mv_chan_is_busy(mv_chan);
26862306a36Sopenharmony_ci	u32 current_desc = mv_chan_get_current_desc(mv_chan);
26962306a36Sopenharmony_ci	int current_cleaned = 0;
27062306a36Sopenharmony_ci	struct mv_xor_desc *hw_desc;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
27362306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "current_desc %x\n", current_desc);
27462306a36Sopenharmony_ci	mv_chan_clean_completed_slots(mv_chan);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* free completed slots from the chain starting with
27762306a36Sopenharmony_ci	 * the oldest descriptor
27862306a36Sopenharmony_ci	 */
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &mv_chan->chain,
28162306a36Sopenharmony_ci				 node) {
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* clean finished descriptors */
28462306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
28562306a36Sopenharmony_ci		if (hw_desc->status & XOR_DESC_SUCCESS) {
28662306a36Sopenharmony_ci			cookie = mv_desc_run_tx_complete_actions(iter, mv_chan,
28762306a36Sopenharmony_ci								 cookie);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci			/* done processing desc, clean slot */
29062306a36Sopenharmony_ci			mv_desc_clean_slot(iter, mv_chan);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci			/* break if we did cleaned the current */
29362306a36Sopenharmony_ci			if (iter->async_tx.phys == current_desc) {
29462306a36Sopenharmony_ci				current_cleaned = 1;
29562306a36Sopenharmony_ci				break;
29662306a36Sopenharmony_ci			}
29762306a36Sopenharmony_ci		} else {
29862306a36Sopenharmony_ci			if (iter->async_tx.phys == current_desc) {
29962306a36Sopenharmony_ci				current_cleaned = 0;
30062306a36Sopenharmony_ci				break;
30162306a36Sopenharmony_ci			}
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if ((busy == 0) && !list_empty(&mv_chan->chain)) {
30662306a36Sopenharmony_ci		if (current_cleaned) {
30762306a36Sopenharmony_ci			/*
30862306a36Sopenharmony_ci			 * current descriptor cleaned and removed, run
30962306a36Sopenharmony_ci			 * from list head
31062306a36Sopenharmony_ci			 */
31162306a36Sopenharmony_ci			iter = list_entry(mv_chan->chain.next,
31262306a36Sopenharmony_ci					  struct mv_xor_desc_slot,
31362306a36Sopenharmony_ci					  node);
31462306a36Sopenharmony_ci			mv_chan_start_new_chain(mv_chan, iter);
31562306a36Sopenharmony_ci		} else {
31662306a36Sopenharmony_ci			if (!list_is_last(&iter->node, &mv_chan->chain)) {
31762306a36Sopenharmony_ci				/*
31862306a36Sopenharmony_ci				 * descriptors are still waiting after
31962306a36Sopenharmony_ci				 * current, trigger them
32062306a36Sopenharmony_ci				 */
32162306a36Sopenharmony_ci				iter = list_entry(iter->node.next,
32262306a36Sopenharmony_ci						  struct mv_xor_desc_slot,
32362306a36Sopenharmony_ci						  node);
32462306a36Sopenharmony_ci				mv_chan_start_new_chain(mv_chan, iter);
32562306a36Sopenharmony_ci			} else {
32662306a36Sopenharmony_ci				/*
32762306a36Sopenharmony_ci				 * some descriptors are still waiting
32862306a36Sopenharmony_ci				 * to be cleaned
32962306a36Sopenharmony_ci				 */
33062306a36Sopenharmony_ci				tasklet_schedule(&mv_chan->irq_tasklet);
33162306a36Sopenharmony_ci			}
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (cookie > 0)
33662306a36Sopenharmony_ci		mv_chan->dmachan.completed_cookie = cookie;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void mv_xor_tasklet(struct tasklet_struct *t)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct mv_xor_chan *chan = from_tasklet(chan, t, irq_tasklet);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock(&chan->lock);
34462306a36Sopenharmony_ci	mv_chan_slot_cleanup(chan);
34562306a36Sopenharmony_ci	spin_unlock(&chan->lock);
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct mv_xor_desc_slot *
34962306a36Sopenharmony_cimv_chan_alloc_slot(struct mv_xor_chan *mv_chan)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct mv_xor_desc_slot *iter;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	spin_lock_bh(&mv_chan->lock);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!list_empty(&mv_chan->free_slots)) {
35662306a36Sopenharmony_ci		iter = list_first_entry(&mv_chan->free_slots,
35762306a36Sopenharmony_ci					struct mv_xor_desc_slot,
35862306a36Sopenharmony_ci					node);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		list_move_tail(&iter->node, &mv_chan->allocated_slots);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		spin_unlock_bh(&mv_chan->lock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		/* pre-ack descriptor */
36562306a36Sopenharmony_ci		async_tx_ack(&iter->async_tx);
36662306a36Sopenharmony_ci		iter->async_tx.cookie = -EBUSY;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		return iter;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	spin_unlock_bh(&mv_chan->lock);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* try to free some slots if the allocation fails */
37562306a36Sopenharmony_ci	tasklet_schedule(&mv_chan->irq_tasklet);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return NULL;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/************************ DMA engine API functions ****************************/
38162306a36Sopenharmony_cistatic dma_cookie_t
38262306a36Sopenharmony_cimv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct mv_xor_desc_slot *sw_desc = to_mv_xor_slot(tx);
38562306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(tx->chan);
38662306a36Sopenharmony_ci	struct mv_xor_desc_slot *old_chain_tail;
38762306a36Sopenharmony_ci	dma_cookie_t cookie;
38862306a36Sopenharmony_ci	int new_hw_chain = 1;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan),
39162306a36Sopenharmony_ci		"%s sw_desc %p: async_tx %p\n",
39262306a36Sopenharmony_ci		__func__, sw_desc, &sw_desc->async_tx);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	spin_lock_bh(&mv_chan->lock);
39562306a36Sopenharmony_ci	cookie = dma_cookie_assign(tx);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (list_empty(&mv_chan->chain))
39862306a36Sopenharmony_ci		list_move_tail(&sw_desc->node, &mv_chan->chain);
39962306a36Sopenharmony_ci	else {
40062306a36Sopenharmony_ci		new_hw_chain = 0;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		old_chain_tail = list_entry(mv_chan->chain.prev,
40362306a36Sopenharmony_ci					    struct mv_xor_desc_slot,
40462306a36Sopenharmony_ci					    node);
40562306a36Sopenharmony_ci		list_move_tail(&sw_desc->node, &mv_chan->chain);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %pa\n",
40862306a36Sopenharmony_ci			&old_chain_tail->async_tx.phys);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* fix up the hardware chain */
41162306a36Sopenharmony_ci		mv_desc_set_next_desc(old_chain_tail, sw_desc->async_tx.phys);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		/* if the channel is not busy */
41462306a36Sopenharmony_ci		if (!mv_chan_is_busy(mv_chan)) {
41562306a36Sopenharmony_ci			u32 current_desc = mv_chan_get_current_desc(mv_chan);
41662306a36Sopenharmony_ci			/*
41762306a36Sopenharmony_ci			 * and the curren desc is the end of the chain before
41862306a36Sopenharmony_ci			 * the append, then we need to start the channel
41962306a36Sopenharmony_ci			 */
42062306a36Sopenharmony_ci			if (current_desc == old_chain_tail->async_tx.phys)
42162306a36Sopenharmony_ci				new_hw_chain = 1;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (new_hw_chain)
42662306a36Sopenharmony_ci		mv_chan_start_new_chain(mv_chan, sw_desc);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	spin_unlock_bh(&mv_chan->lock);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return cookie;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/* returns the number of allocated descriptors */
43462306a36Sopenharmony_cistatic int mv_xor_alloc_chan_resources(struct dma_chan *chan)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	void *virt_desc;
43762306a36Sopenharmony_ci	dma_addr_t dma_desc;
43862306a36Sopenharmony_ci	int idx;
43962306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
44062306a36Sopenharmony_ci	struct mv_xor_desc_slot *slot = NULL;
44162306a36Sopenharmony_ci	int num_descs_in_pool = MV_XOR_POOL_SIZE/MV_XOR_SLOT_SIZE;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Allocate descriptor slots */
44462306a36Sopenharmony_ci	idx = mv_chan->slots_allocated;
44562306a36Sopenharmony_ci	while (idx < num_descs_in_pool) {
44662306a36Sopenharmony_ci		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
44762306a36Sopenharmony_ci		if (!slot) {
44862306a36Sopenharmony_ci			dev_info(mv_chan_to_devp(mv_chan),
44962306a36Sopenharmony_ci				 "channel only initialized %d descriptor slots",
45062306a36Sopenharmony_ci				 idx);
45162306a36Sopenharmony_ci			break;
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci		virt_desc = mv_chan->dma_desc_pool_virt;
45462306a36Sopenharmony_ci		slot->hw_desc = virt_desc + idx * MV_XOR_SLOT_SIZE;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		dma_async_tx_descriptor_init(&slot->async_tx, chan);
45762306a36Sopenharmony_ci		slot->async_tx.tx_submit = mv_xor_tx_submit;
45862306a36Sopenharmony_ci		INIT_LIST_HEAD(&slot->node);
45962306a36Sopenharmony_ci		INIT_LIST_HEAD(&slot->sg_tx_list);
46062306a36Sopenharmony_ci		dma_desc = mv_chan->dma_desc_pool;
46162306a36Sopenharmony_ci		slot->async_tx.phys = dma_desc + idx * MV_XOR_SLOT_SIZE;
46262306a36Sopenharmony_ci		slot->idx = idx++;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		spin_lock_bh(&mv_chan->lock);
46562306a36Sopenharmony_ci		mv_chan->slots_allocated = idx;
46662306a36Sopenharmony_ci		list_add_tail(&slot->node, &mv_chan->free_slots);
46762306a36Sopenharmony_ci		spin_unlock_bh(&mv_chan->lock);
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan),
47162306a36Sopenharmony_ci		"allocated %d descriptor slots\n",
47262306a36Sopenharmony_ci		mv_chan->slots_allocated);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return mv_chan->slots_allocated ? : -ENOMEM;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/*
47862306a36Sopenharmony_ci * Check if source or destination is an PCIe/IO address (non-SDRAM) and add
47962306a36Sopenharmony_ci * a new MBus window if necessary. Use a cache for these check so that
48062306a36Sopenharmony_ci * the MMIO mapped registers don't have to be accessed for this check
48162306a36Sopenharmony_ci * to speed up this process.
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_cistatic int mv_xor_add_io_win(struct mv_xor_chan *mv_chan, u32 addr)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct mv_xor_device *xordev = mv_chan->xordev;
48662306a36Sopenharmony_ci	void __iomem *base = mv_chan->mmr_high_base;
48762306a36Sopenharmony_ci	u32 win_enable;
48862306a36Sopenharmony_ci	u32 size;
48962306a36Sopenharmony_ci	u8 target, attr;
49062306a36Sopenharmony_ci	int ret;
49162306a36Sopenharmony_ci	int i;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Nothing needs to get done for the Armada 3700 */
49462306a36Sopenharmony_ci	if (xordev->xor_type == XOR_ARMADA_37XX)
49562306a36Sopenharmony_ci		return 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/*
49862306a36Sopenharmony_ci	 * Loop over the cached windows to check, if the requested area
49962306a36Sopenharmony_ci	 * is already mapped. If this the case, nothing needs to be done
50062306a36Sopenharmony_ci	 * and we can return.
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci	for (i = 0; i < WINDOW_COUNT; i++) {
50362306a36Sopenharmony_ci		if (addr >= xordev->win_start[i] &&
50462306a36Sopenharmony_ci		    addr <= xordev->win_end[i]) {
50562306a36Sopenharmony_ci			/* Window is already mapped */
50662306a36Sopenharmony_ci			return 0;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/*
51162306a36Sopenharmony_ci	 * The window is not mapped, so we need to create the new mapping
51262306a36Sopenharmony_ci	 */
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* If no IO window is found that addr has to be located in SDRAM */
51562306a36Sopenharmony_ci	ret = mvebu_mbus_get_io_win_info(addr, &size, &target, &attr);
51662306a36Sopenharmony_ci	if (ret < 0)
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/*
52062306a36Sopenharmony_ci	 * Mask the base addr 'addr' according to 'size' read back from the
52162306a36Sopenharmony_ci	 * MBus window. Otherwise we might end up with an address located
52262306a36Sopenharmony_ci	 * somewhere in the middle of this area here.
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	size -= 1;
52562306a36Sopenharmony_ci	addr &= ~size;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/*
52862306a36Sopenharmony_ci	 * Reading one of both enabled register is enough, as they are always
52962306a36Sopenharmony_ci	 * programmed to the identical values
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	win_enable = readl(base + WINDOW_BAR_ENABLE(0));
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* Set 'i' to the first free window to write the new values to */
53462306a36Sopenharmony_ci	i = ffs(~win_enable) - 1;
53562306a36Sopenharmony_ci	if (i >= WINDOW_COUNT)
53662306a36Sopenharmony_ci		return -ENOMEM;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	writel((addr & 0xffff0000) | (attr << 8) | target,
53962306a36Sopenharmony_ci	       base + WINDOW_BASE(i));
54062306a36Sopenharmony_ci	writel(size & 0xffff0000, base + WINDOW_SIZE(i));
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* Fill the caching variables for later use */
54362306a36Sopenharmony_ci	xordev->win_start[i] = addr;
54462306a36Sopenharmony_ci	xordev->win_end[i] = addr + size;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	win_enable |= (1 << i);
54762306a36Sopenharmony_ci	win_enable |= 3 << (16 + (2 * i));
54862306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(0));
54962306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(1));
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return 0;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
55562306a36Sopenharmony_cimv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
55662306a36Sopenharmony_ci		    unsigned int src_cnt, size_t len, unsigned long flags)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
55962306a36Sopenharmony_ci	struct mv_xor_desc_slot *sw_desc;
56062306a36Sopenharmony_ci	int ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
56362306a36Sopenharmony_ci		return NULL;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan),
56862306a36Sopenharmony_ci		"%s src_cnt: %d len: %zu dest %pad flags: %ld\n",
56962306a36Sopenharmony_ci		__func__, src_cnt, len, &dest, flags);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* Check if a new window needs to get added for 'dest' */
57262306a36Sopenharmony_ci	ret = mv_xor_add_io_win(mv_chan, dest);
57362306a36Sopenharmony_ci	if (ret)
57462306a36Sopenharmony_ci		return NULL;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	sw_desc = mv_chan_alloc_slot(mv_chan);
57762306a36Sopenharmony_ci	if (sw_desc) {
57862306a36Sopenharmony_ci		sw_desc->type = DMA_XOR;
57962306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
58062306a36Sopenharmony_ci		mv_desc_init(sw_desc, dest, len, flags);
58162306a36Sopenharmony_ci		if (mv_chan->op_in_desc == XOR_MODE_IN_DESC)
58262306a36Sopenharmony_ci			mv_desc_set_mode(sw_desc);
58362306a36Sopenharmony_ci		while (src_cnt--) {
58462306a36Sopenharmony_ci			/* Check if a new window needs to get added for 'src' */
58562306a36Sopenharmony_ci			ret = mv_xor_add_io_win(mv_chan, src[src_cnt]);
58662306a36Sopenharmony_ci			if (ret)
58762306a36Sopenharmony_ci				return NULL;
58862306a36Sopenharmony_ci			mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]);
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan),
59362306a36Sopenharmony_ci		"%s sw_desc %p async_tx %p \n",
59462306a36Sopenharmony_ci		__func__, sw_desc, &sw_desc->async_tx);
59562306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
59962306a36Sopenharmony_cimv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
60062306a36Sopenharmony_ci		size_t len, unsigned long flags)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * A MEMCPY operation is identical to an XOR operation with only
60462306a36Sopenharmony_ci	 * a single source address.
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	return mv_xor_prep_dma_xor(chan, dest, &src, 1, len, flags);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
61062306a36Sopenharmony_cimv_xor_prep_dma_interrupt(struct dma_chan *chan, unsigned long flags)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
61362306a36Sopenharmony_ci	dma_addr_t src, dest;
61462306a36Sopenharmony_ci	size_t len;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	src = mv_chan->dummy_src_addr;
61762306a36Sopenharmony_ci	dest = mv_chan->dummy_dst_addr;
61862306a36Sopenharmony_ci	len = MV_XOR_MIN_BYTE_COUNT;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/*
62162306a36Sopenharmony_ci	 * We implement the DMA_INTERRUPT operation as a minimum sized
62262306a36Sopenharmony_ci	 * XOR operation with a single dummy source address.
62362306a36Sopenharmony_ci	 */
62462306a36Sopenharmony_ci	return mv_xor_prep_dma_xor(chan, dest, &src, 1, len, flags);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic void mv_xor_free_chan_resources(struct dma_chan *chan)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
63062306a36Sopenharmony_ci	struct mv_xor_desc_slot *iter, *_iter;
63162306a36Sopenharmony_ci	int in_use_descs = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	spin_lock_bh(&mv_chan->lock);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	mv_chan_slot_cleanup(mv_chan);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &mv_chan->chain,
63862306a36Sopenharmony_ci					node) {
63962306a36Sopenharmony_ci		in_use_descs++;
64062306a36Sopenharmony_ci		list_move_tail(&iter->node, &mv_chan->free_slots);
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
64362306a36Sopenharmony_ci				 node) {
64462306a36Sopenharmony_ci		in_use_descs++;
64562306a36Sopenharmony_ci		list_move_tail(&iter->node, &mv_chan->free_slots);
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &mv_chan->allocated_slots,
64862306a36Sopenharmony_ci				 node) {
64962306a36Sopenharmony_ci		in_use_descs++;
65062306a36Sopenharmony_ci		list_move_tail(&iter->node, &mv_chan->free_slots);
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(
65362306a36Sopenharmony_ci		iter, _iter, &mv_chan->free_slots, node) {
65462306a36Sopenharmony_ci		list_del(&iter->node);
65562306a36Sopenharmony_ci		kfree(iter);
65662306a36Sopenharmony_ci		mv_chan->slots_allocated--;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n",
66062306a36Sopenharmony_ci		__func__, mv_chan->slots_allocated);
66162306a36Sopenharmony_ci	spin_unlock_bh(&mv_chan->lock);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (in_use_descs)
66462306a36Sopenharmony_ci		dev_err(mv_chan_to_devp(mv_chan),
66562306a36Sopenharmony_ci			"freeing %d in use descriptors!\n", in_use_descs);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci/**
66962306a36Sopenharmony_ci * mv_xor_status - poll the status of an XOR transaction
67062306a36Sopenharmony_ci * @chan: XOR channel handle
67162306a36Sopenharmony_ci * @cookie: XOR transaction identifier
67262306a36Sopenharmony_ci * @txstate: XOR transactions state holder (or NULL)
67362306a36Sopenharmony_ci */
67462306a36Sopenharmony_cistatic enum dma_status mv_xor_status(struct dma_chan *chan,
67562306a36Sopenharmony_ci					  dma_cookie_t cookie,
67662306a36Sopenharmony_ci					  struct dma_tx_state *txstate)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
67962306a36Sopenharmony_ci	enum dma_status ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
68262306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
68362306a36Sopenharmony_ci		return ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	spin_lock_bh(&mv_chan->lock);
68662306a36Sopenharmony_ci	mv_chan_slot_cleanup(mv_chan);
68762306a36Sopenharmony_ci	spin_unlock_bh(&mv_chan->lock);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return dma_cookie_status(chan, cookie, txstate);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void mv_chan_dump_regs(struct mv_xor_chan *chan)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	u32 val;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	val = readl_relaxed(XOR_CONFIG(chan));
69762306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "config       0x%08x\n", val);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	val = readl_relaxed(XOR_ACTIVATION(chan));
70062306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "activation   0x%08x\n", val);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	val = readl_relaxed(XOR_INTR_CAUSE(chan));
70362306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "intr cause   0x%08x\n", val);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	val = readl_relaxed(XOR_INTR_MASK(chan));
70662306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "intr mask    0x%08x\n", val);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	val = readl_relaxed(XOR_ERROR_CAUSE(chan));
70962306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "error cause  0x%08x\n", val);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	val = readl_relaxed(XOR_ERROR_ADDR(chan));
71262306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "error addr   0x%08x\n", val);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void mv_chan_err_interrupt_handler(struct mv_xor_chan *chan,
71662306a36Sopenharmony_ci					  u32 intr_cause)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	if (intr_cause & XOR_INT_ERR_DECODE) {
71962306a36Sopenharmony_ci		dev_dbg(mv_chan_to_devp(chan), "ignoring address decode error\n");
72062306a36Sopenharmony_ci		return;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	dev_err(mv_chan_to_devp(chan), "error on chan %d. intr cause 0x%08x\n",
72462306a36Sopenharmony_ci		chan->idx, intr_cause);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	mv_chan_dump_regs(chan);
72762306a36Sopenharmony_ci	WARN_ON(1);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic irqreturn_t mv_xor_interrupt_handler(int irq, void *data)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct mv_xor_chan *chan = data;
73362306a36Sopenharmony_ci	u32 intr_cause = mv_chan_get_intr_cause(chan);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	dev_dbg(mv_chan_to_devp(chan), "intr cause %x\n", intr_cause);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (intr_cause & XOR_INTR_ERRORS)
73862306a36Sopenharmony_ci		mv_chan_err_interrupt_handler(chan, intr_cause);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	tasklet_schedule(&chan->irq_tasklet);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	mv_chan_clear_eoc_cause(chan);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return IRQ_HANDLED;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void mv_xor_issue_pending(struct dma_chan *chan)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (mv_chan->pending >= MV_XOR_THRESHOLD) {
75262306a36Sopenharmony_ci		mv_chan->pending = 0;
75362306a36Sopenharmony_ci		mv_chan_activate(mv_chan);
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/*
75862306a36Sopenharmony_ci * Perform a transaction to verify the HW works.
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int mv_chan_memcpy_self_test(struct mv_xor_chan *mv_chan)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	int i, ret;
76462306a36Sopenharmony_ci	void *src, *dest;
76562306a36Sopenharmony_ci	dma_addr_t src_dma, dest_dma;
76662306a36Sopenharmony_ci	struct dma_chan *dma_chan;
76762306a36Sopenharmony_ci	dma_cookie_t cookie;
76862306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
76962306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap;
77062306a36Sopenharmony_ci	int err = 0;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	src = kmalloc(PAGE_SIZE, GFP_KERNEL);
77362306a36Sopenharmony_ci	if (!src)
77462306a36Sopenharmony_ci		return -ENOMEM;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	dest = kzalloc(PAGE_SIZE, GFP_KERNEL);
77762306a36Sopenharmony_ci	if (!dest) {
77862306a36Sopenharmony_ci		kfree(src);
77962306a36Sopenharmony_ci		return -ENOMEM;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Fill in src buffer */
78362306a36Sopenharmony_ci	for (i = 0; i < PAGE_SIZE; i++)
78462306a36Sopenharmony_ci		((u8 *) src)[i] = (u8)i;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	dma_chan = &mv_chan->dmachan;
78762306a36Sopenharmony_ci	if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
78862306a36Sopenharmony_ci		err = -ENODEV;
78962306a36Sopenharmony_ci		goto out;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	unmap = dmaengine_get_unmap_data(dma_chan->device->dev, 2, GFP_KERNEL);
79362306a36Sopenharmony_ci	if (!unmap) {
79462306a36Sopenharmony_ci		err = -ENOMEM;
79562306a36Sopenharmony_ci		goto free_resources;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	src_dma = dma_map_page(dma_chan->device->dev, virt_to_page(src),
79962306a36Sopenharmony_ci			       offset_in_page(src), PAGE_SIZE,
80062306a36Sopenharmony_ci			       DMA_TO_DEVICE);
80162306a36Sopenharmony_ci	unmap->addr[0] = src_dma;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ret = dma_mapping_error(dma_chan->device->dev, src_dma);
80462306a36Sopenharmony_ci	if (ret) {
80562306a36Sopenharmony_ci		err = -ENOMEM;
80662306a36Sopenharmony_ci		goto free_resources;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci	unmap->to_cnt = 1;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	dest_dma = dma_map_page(dma_chan->device->dev, virt_to_page(dest),
81162306a36Sopenharmony_ci				offset_in_page(dest), PAGE_SIZE,
81262306a36Sopenharmony_ci				DMA_FROM_DEVICE);
81362306a36Sopenharmony_ci	unmap->addr[1] = dest_dma;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ret = dma_mapping_error(dma_chan->device->dev, dest_dma);
81662306a36Sopenharmony_ci	if (ret) {
81762306a36Sopenharmony_ci		err = -ENOMEM;
81862306a36Sopenharmony_ci		goto free_resources;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	unmap->from_cnt = 1;
82162306a36Sopenharmony_ci	unmap->len = PAGE_SIZE;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	tx = mv_xor_prep_dma_memcpy(dma_chan, dest_dma, src_dma,
82462306a36Sopenharmony_ci				    PAGE_SIZE, 0);
82562306a36Sopenharmony_ci	if (!tx) {
82662306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
82762306a36Sopenharmony_ci			"Self-test cannot prepare operation, disabling\n");
82862306a36Sopenharmony_ci		err = -ENODEV;
82962306a36Sopenharmony_ci		goto free_resources;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	cookie = mv_xor_tx_submit(tx);
83362306a36Sopenharmony_ci	if (dma_submit_error(cookie)) {
83462306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
83562306a36Sopenharmony_ci			"Self-test submit error, disabling\n");
83662306a36Sopenharmony_ci		err = -ENODEV;
83762306a36Sopenharmony_ci		goto free_resources;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	mv_xor_issue_pending(dma_chan);
84162306a36Sopenharmony_ci	async_tx_ack(tx);
84262306a36Sopenharmony_ci	msleep(1);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (mv_xor_status(dma_chan, cookie, NULL) !=
84562306a36Sopenharmony_ci	    DMA_COMPLETE) {
84662306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
84762306a36Sopenharmony_ci			"Self-test copy timed out, disabling\n");
84862306a36Sopenharmony_ci		err = -ENODEV;
84962306a36Sopenharmony_ci		goto free_resources;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
85362306a36Sopenharmony_ci				PAGE_SIZE, DMA_FROM_DEVICE);
85462306a36Sopenharmony_ci	if (memcmp(src, dest, PAGE_SIZE)) {
85562306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
85662306a36Sopenharmony_ci			"Self-test copy failed compare, disabling\n");
85762306a36Sopenharmony_ci		err = -ENODEV;
85862306a36Sopenharmony_ci		goto free_resources;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cifree_resources:
86262306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
86362306a36Sopenharmony_ci	mv_xor_free_chan_resources(dma_chan);
86462306a36Sopenharmony_ciout:
86562306a36Sopenharmony_ci	kfree(src);
86662306a36Sopenharmony_ci	kfree(dest);
86762306a36Sopenharmony_ci	return err;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci#define MV_XOR_NUM_SRC_TEST 4 /* must be <= 15 */
87162306a36Sopenharmony_cistatic int
87262306a36Sopenharmony_cimv_chan_xor_self_test(struct mv_xor_chan *mv_chan)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	int i, src_idx, ret;
87562306a36Sopenharmony_ci	struct page *dest;
87662306a36Sopenharmony_ci	struct page *xor_srcs[MV_XOR_NUM_SRC_TEST];
87762306a36Sopenharmony_ci	dma_addr_t dma_srcs[MV_XOR_NUM_SRC_TEST];
87862306a36Sopenharmony_ci	dma_addr_t dest_dma;
87962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
88062306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap;
88162306a36Sopenharmony_ci	struct dma_chan *dma_chan;
88262306a36Sopenharmony_ci	dma_cookie_t cookie;
88362306a36Sopenharmony_ci	u8 cmp_byte = 0;
88462306a36Sopenharmony_ci	u32 cmp_word;
88562306a36Sopenharmony_ci	int err = 0;
88662306a36Sopenharmony_ci	int src_count = MV_XOR_NUM_SRC_TEST;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	for (src_idx = 0; src_idx < src_count; src_idx++) {
88962306a36Sopenharmony_ci		xor_srcs[src_idx] = alloc_page(GFP_KERNEL);
89062306a36Sopenharmony_ci		if (!xor_srcs[src_idx]) {
89162306a36Sopenharmony_ci			while (src_idx--)
89262306a36Sopenharmony_ci				__free_page(xor_srcs[src_idx]);
89362306a36Sopenharmony_ci			return -ENOMEM;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	dest = alloc_page(GFP_KERNEL);
89862306a36Sopenharmony_ci	if (!dest) {
89962306a36Sopenharmony_ci		while (src_idx--)
90062306a36Sopenharmony_ci			__free_page(xor_srcs[src_idx]);
90162306a36Sopenharmony_ci		return -ENOMEM;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* Fill in src buffers */
90562306a36Sopenharmony_ci	for (src_idx = 0; src_idx < src_count; src_idx++) {
90662306a36Sopenharmony_ci		u8 *ptr = page_address(xor_srcs[src_idx]);
90762306a36Sopenharmony_ci		for (i = 0; i < PAGE_SIZE; i++)
90862306a36Sopenharmony_ci			ptr[i] = (1 << src_idx);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	for (src_idx = 0; src_idx < src_count; src_idx++)
91262306a36Sopenharmony_ci		cmp_byte ^= (u8) (1 << src_idx);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	cmp_word = (cmp_byte << 24) | (cmp_byte << 16) |
91562306a36Sopenharmony_ci		(cmp_byte << 8) | cmp_byte;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	memset(page_address(dest), 0, PAGE_SIZE);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	dma_chan = &mv_chan->dmachan;
92062306a36Sopenharmony_ci	if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
92162306a36Sopenharmony_ci		err = -ENODEV;
92262306a36Sopenharmony_ci		goto out;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	unmap = dmaengine_get_unmap_data(dma_chan->device->dev, src_count + 1,
92662306a36Sopenharmony_ci					 GFP_KERNEL);
92762306a36Sopenharmony_ci	if (!unmap) {
92862306a36Sopenharmony_ci		err = -ENOMEM;
92962306a36Sopenharmony_ci		goto free_resources;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* test xor */
93362306a36Sopenharmony_ci	for (i = 0; i < src_count; i++) {
93462306a36Sopenharmony_ci		unmap->addr[i] = dma_map_page(dma_chan->device->dev, xor_srcs[i],
93562306a36Sopenharmony_ci					      0, PAGE_SIZE, DMA_TO_DEVICE);
93662306a36Sopenharmony_ci		dma_srcs[i] = unmap->addr[i];
93762306a36Sopenharmony_ci		ret = dma_mapping_error(dma_chan->device->dev, unmap->addr[i]);
93862306a36Sopenharmony_ci		if (ret) {
93962306a36Sopenharmony_ci			err = -ENOMEM;
94062306a36Sopenharmony_ci			goto free_resources;
94162306a36Sopenharmony_ci		}
94262306a36Sopenharmony_ci		unmap->to_cnt++;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	unmap->addr[src_count] = dma_map_page(dma_chan->device->dev, dest, 0, PAGE_SIZE,
94662306a36Sopenharmony_ci				      DMA_FROM_DEVICE);
94762306a36Sopenharmony_ci	dest_dma = unmap->addr[src_count];
94862306a36Sopenharmony_ci	ret = dma_mapping_error(dma_chan->device->dev, unmap->addr[src_count]);
94962306a36Sopenharmony_ci	if (ret) {
95062306a36Sopenharmony_ci		err = -ENOMEM;
95162306a36Sopenharmony_ci		goto free_resources;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	unmap->from_cnt = 1;
95462306a36Sopenharmony_ci	unmap->len = PAGE_SIZE;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	tx = mv_xor_prep_dma_xor(dma_chan, dest_dma, dma_srcs,
95762306a36Sopenharmony_ci				 src_count, PAGE_SIZE, 0);
95862306a36Sopenharmony_ci	if (!tx) {
95962306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
96062306a36Sopenharmony_ci			"Self-test cannot prepare operation, disabling\n");
96162306a36Sopenharmony_ci		err = -ENODEV;
96262306a36Sopenharmony_ci		goto free_resources;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	cookie = mv_xor_tx_submit(tx);
96662306a36Sopenharmony_ci	if (dma_submit_error(cookie)) {
96762306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
96862306a36Sopenharmony_ci			"Self-test submit error, disabling\n");
96962306a36Sopenharmony_ci		err = -ENODEV;
97062306a36Sopenharmony_ci		goto free_resources;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	mv_xor_issue_pending(dma_chan);
97462306a36Sopenharmony_ci	async_tx_ack(tx);
97562306a36Sopenharmony_ci	msleep(8);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (mv_xor_status(dma_chan, cookie, NULL) !=
97862306a36Sopenharmony_ci	    DMA_COMPLETE) {
97962306a36Sopenharmony_ci		dev_err(dma_chan->device->dev,
98062306a36Sopenharmony_ci			"Self-test xor timed out, disabling\n");
98162306a36Sopenharmony_ci		err = -ENODEV;
98262306a36Sopenharmony_ci		goto free_resources;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
98662306a36Sopenharmony_ci				PAGE_SIZE, DMA_FROM_DEVICE);
98762306a36Sopenharmony_ci	for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
98862306a36Sopenharmony_ci		u32 *ptr = page_address(dest);
98962306a36Sopenharmony_ci		if (ptr[i] != cmp_word) {
99062306a36Sopenharmony_ci			dev_err(dma_chan->device->dev,
99162306a36Sopenharmony_ci				"Self-test xor failed compare, disabling. index %d, data %x, expected %x\n",
99262306a36Sopenharmony_ci				i, ptr[i], cmp_word);
99362306a36Sopenharmony_ci			err = -ENODEV;
99462306a36Sopenharmony_ci			goto free_resources;
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cifree_resources:
99962306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
100062306a36Sopenharmony_ci	mv_xor_free_chan_resources(dma_chan);
100162306a36Sopenharmony_ciout:
100262306a36Sopenharmony_ci	src_idx = src_count;
100362306a36Sopenharmony_ci	while (src_idx--)
100462306a36Sopenharmony_ci		__free_page(xor_srcs[src_idx]);
100562306a36Sopenharmony_ci	__free_page(dest);
100662306a36Sopenharmony_ci	return err;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct dma_chan *chan, *_chan;
101262306a36Sopenharmony_ci	struct device *dev = mv_chan->dmadev.dev;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	dma_async_device_unregister(&mv_chan->dmadev);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	dma_free_coherent(dev, MV_XOR_POOL_SIZE,
101762306a36Sopenharmony_ci			  mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
101862306a36Sopenharmony_ci	dma_unmap_single(dev, mv_chan->dummy_src_addr,
101962306a36Sopenharmony_ci			 MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
102062306a36Sopenharmony_ci	dma_unmap_single(dev, mv_chan->dummy_dst_addr,
102162306a36Sopenharmony_ci			 MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels,
102462306a36Sopenharmony_ci				 device_node) {
102562306a36Sopenharmony_ci		list_del(&chan->device_node);
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	free_irq(mv_chan->irq, mv_chan);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic struct mv_xor_chan *
103462306a36Sopenharmony_cimv_xor_channel_add(struct mv_xor_device *xordev,
103562306a36Sopenharmony_ci		   struct platform_device *pdev,
103662306a36Sopenharmony_ci		   int idx, dma_cap_mask_t cap_mask, int irq)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int ret = 0;
103962306a36Sopenharmony_ci	struct mv_xor_chan *mv_chan;
104062306a36Sopenharmony_ci	struct dma_device *dma_dev;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
104362306a36Sopenharmony_ci	if (!mv_chan)
104462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	mv_chan->idx = idx;
104762306a36Sopenharmony_ci	mv_chan->irq = irq;
104862306a36Sopenharmony_ci	if (xordev->xor_type == XOR_ORION)
104962306a36Sopenharmony_ci		mv_chan->op_in_desc = XOR_MODE_IN_REG;
105062306a36Sopenharmony_ci	else
105162306a36Sopenharmony_ci		mv_chan->op_in_desc = XOR_MODE_IN_DESC;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	dma_dev = &mv_chan->dmadev;
105462306a36Sopenharmony_ci	dma_dev->dev = &pdev->dev;
105562306a36Sopenharmony_ci	mv_chan->xordev = xordev;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/*
105862306a36Sopenharmony_ci	 * These source and destination dummy buffers are used to implement
105962306a36Sopenharmony_ci	 * a DMA_INTERRUPT operation as a minimum-sized XOR operation.
106062306a36Sopenharmony_ci	 * Hence, we only need to map the buffers at initialization-time.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	mv_chan->dummy_src_addr = dma_map_single(dma_dev->dev,
106362306a36Sopenharmony_ci		mv_chan->dummy_src, MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
106462306a36Sopenharmony_ci	mv_chan->dummy_dst_addr = dma_map_single(dma_dev->dev,
106562306a36Sopenharmony_ci		mv_chan->dummy_dst, MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/* allocate coherent memory for hardware descriptors
106862306a36Sopenharmony_ci	 * note: writecombine gives slightly better performance, but
106962306a36Sopenharmony_ci	 * requires that we explicitly flush the writes
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	mv_chan->dma_desc_pool_virt =
107262306a36Sopenharmony_ci	  dma_alloc_wc(&pdev->dev, MV_XOR_POOL_SIZE, &mv_chan->dma_desc_pool,
107362306a36Sopenharmony_ci		       GFP_KERNEL);
107462306a36Sopenharmony_ci	if (!mv_chan->dma_desc_pool_virt)
107562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* discover transaction capabilites from the platform data */
107862306a36Sopenharmony_ci	dma_dev->cap_mask = cap_mask;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	INIT_LIST_HEAD(&dma_dev->channels);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	/* set base routines */
108362306a36Sopenharmony_ci	dma_dev->device_alloc_chan_resources = mv_xor_alloc_chan_resources;
108462306a36Sopenharmony_ci	dma_dev->device_free_chan_resources = mv_xor_free_chan_resources;
108562306a36Sopenharmony_ci	dma_dev->device_tx_status = mv_xor_status;
108662306a36Sopenharmony_ci	dma_dev->device_issue_pending = mv_xor_issue_pending;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/* set prep routines based on capability */
108962306a36Sopenharmony_ci	if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask))
109062306a36Sopenharmony_ci		dma_dev->device_prep_dma_interrupt = mv_xor_prep_dma_interrupt;
109162306a36Sopenharmony_ci	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask))
109262306a36Sopenharmony_ci		dma_dev->device_prep_dma_memcpy = mv_xor_prep_dma_memcpy;
109362306a36Sopenharmony_ci	if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
109462306a36Sopenharmony_ci		dma_dev->max_xor = 8;
109562306a36Sopenharmony_ci		dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	mv_chan->mmr_base = xordev->xor_base;
109962306a36Sopenharmony_ci	mv_chan->mmr_high_base = xordev->xor_high_base;
110062306a36Sopenharmony_ci	tasklet_setup(&mv_chan->irq_tasklet, mv_xor_tasklet);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/* clear errors before enabling interrupts */
110362306a36Sopenharmony_ci	mv_chan_clear_err_status(mv_chan);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ret = request_irq(mv_chan->irq, mv_xor_interrupt_handler,
110662306a36Sopenharmony_ci			  0, dev_name(&pdev->dev), mv_chan);
110762306a36Sopenharmony_ci	if (ret)
110862306a36Sopenharmony_ci		goto err_free_dma;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	mv_chan_unmask_interrupts(mv_chan);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (mv_chan->op_in_desc == XOR_MODE_IN_DESC)
111362306a36Sopenharmony_ci		mv_chan_set_mode(mv_chan, XOR_OPERATION_MODE_IN_DESC);
111462306a36Sopenharmony_ci	else
111562306a36Sopenharmony_ci		mv_chan_set_mode(mv_chan, XOR_OPERATION_MODE_XOR);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	spin_lock_init(&mv_chan->lock);
111862306a36Sopenharmony_ci	INIT_LIST_HEAD(&mv_chan->chain);
111962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mv_chan->completed_slots);
112062306a36Sopenharmony_ci	INIT_LIST_HEAD(&mv_chan->free_slots);
112162306a36Sopenharmony_ci	INIT_LIST_HEAD(&mv_chan->allocated_slots);
112262306a36Sopenharmony_ci	mv_chan->dmachan.device = dma_dev;
112362306a36Sopenharmony_ci	dma_cookie_init(&mv_chan->dmachan);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	list_add_tail(&mv_chan->dmachan.device_node, &dma_dev->channels);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
112862306a36Sopenharmony_ci		ret = mv_chan_memcpy_self_test(mv_chan);
112962306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
113062306a36Sopenharmony_ci		if (ret)
113162306a36Sopenharmony_ci			goto err_free_irq;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
113562306a36Sopenharmony_ci		ret = mv_chan_xor_self_test(mv_chan);
113662306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
113762306a36Sopenharmony_ci		if (ret)
113862306a36Sopenharmony_ci			goto err_free_irq;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	dev_info(&pdev->dev, "Marvell XOR (%s): ( %s%s%s)\n",
114262306a36Sopenharmony_ci		 mv_chan->op_in_desc ? "Descriptor Mode" : "Registers Mode",
114362306a36Sopenharmony_ci		 dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
114462306a36Sopenharmony_ci		 dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
114562306a36Sopenharmony_ci		 dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	ret = dma_async_device_register(dma_dev);
114862306a36Sopenharmony_ci	if (ret)
114962306a36Sopenharmony_ci		goto err_free_irq;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return mv_chan;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cierr_free_irq:
115462306a36Sopenharmony_ci	free_irq(mv_chan->irq, mv_chan);
115562306a36Sopenharmony_cierr_free_dma:
115662306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE,
115762306a36Sopenharmony_ci			  mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
115862306a36Sopenharmony_ci	return ERR_PTR(ret);
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic void
116262306a36Sopenharmony_cimv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
116362306a36Sopenharmony_ci			 const struct mbus_dram_target_info *dram)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	void __iomem *base = xordev->xor_high_base;
116662306a36Sopenharmony_ci	u32 win_enable = 0;
116762306a36Sopenharmony_ci	int i;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
117062306a36Sopenharmony_ci		writel(0, base + WINDOW_BASE(i));
117162306a36Sopenharmony_ci		writel(0, base + WINDOW_SIZE(i));
117262306a36Sopenharmony_ci		if (i < 4)
117362306a36Sopenharmony_ci			writel(0, base + WINDOW_REMAP_HIGH(i));
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	for (i = 0; i < dram->num_cs; i++) {
117762306a36Sopenharmony_ci		const struct mbus_dram_window *cs = dram->cs + i;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		writel((cs->base & 0xffff0000) |
118062306a36Sopenharmony_ci		       (cs->mbus_attr << 8) |
118162306a36Sopenharmony_ci		       dram->mbus_dram_target_id, base + WINDOW_BASE(i));
118262306a36Sopenharmony_ci		writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i));
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		/* Fill the caching variables for later use */
118562306a36Sopenharmony_ci		xordev->win_start[i] = cs->base;
118662306a36Sopenharmony_ci		xordev->win_end[i] = cs->base + cs->size - 1;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		win_enable |= (1 << i);
118962306a36Sopenharmony_ci		win_enable |= 3 << (16 + (2 * i));
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(0));
119362306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(1));
119462306a36Sopenharmony_ci	writel(0, base + WINDOW_OVERRIDE_CTRL(0));
119562306a36Sopenharmony_ci	writel(0, base + WINDOW_OVERRIDE_CTRL(1));
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic void
119962306a36Sopenharmony_cimv_xor_conf_mbus_windows_a3700(struct mv_xor_device *xordev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	void __iomem *base = xordev->xor_high_base;
120262306a36Sopenharmony_ci	u32 win_enable = 0;
120362306a36Sopenharmony_ci	int i;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
120662306a36Sopenharmony_ci		writel(0, base + WINDOW_BASE(i));
120762306a36Sopenharmony_ci		writel(0, base + WINDOW_SIZE(i));
120862306a36Sopenharmony_ci		if (i < 4)
120962306a36Sopenharmony_ci			writel(0, base + WINDOW_REMAP_HIGH(i));
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci	/*
121262306a36Sopenharmony_ci	 * For Armada3700 open default 4GB Mbus window. The dram
121362306a36Sopenharmony_ci	 * related configuration are done at AXIS level.
121462306a36Sopenharmony_ci	 */
121562306a36Sopenharmony_ci	writel(0xffff0000, base + WINDOW_SIZE(0));
121662306a36Sopenharmony_ci	win_enable |= 1;
121762306a36Sopenharmony_ci	win_enable |= 3 << 16;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(0));
122062306a36Sopenharmony_ci	writel(win_enable, base + WINDOW_BAR_ENABLE(1));
122162306a36Sopenharmony_ci	writel(0, base + WINDOW_OVERRIDE_CTRL(0));
122262306a36Sopenharmony_ci	writel(0, base + WINDOW_OVERRIDE_CTRL(1));
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci/*
122662306a36Sopenharmony_ci * Since this XOR driver is basically used only for RAID5, we don't
122762306a36Sopenharmony_ci * need to care about synchronizing ->suspend with DMA activity,
122862306a36Sopenharmony_ci * because the DMA engine will naturally be quiet due to the block
122962306a36Sopenharmony_ci * devices being suspended.
123062306a36Sopenharmony_ci */
123162306a36Sopenharmony_cistatic int mv_xor_suspend(struct platform_device *pdev, pm_message_t state)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	struct mv_xor_device *xordev = platform_get_drvdata(pdev);
123462306a36Sopenharmony_ci	int i;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
123762306a36Sopenharmony_ci		struct mv_xor_chan *mv_chan = xordev->channels[i];
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		if (!mv_chan)
124062306a36Sopenharmony_ci			continue;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		mv_chan->saved_config_reg =
124362306a36Sopenharmony_ci			readl_relaxed(XOR_CONFIG(mv_chan));
124462306a36Sopenharmony_ci		mv_chan->saved_int_mask_reg =
124562306a36Sopenharmony_ci			readl_relaxed(XOR_INTR_MASK(mv_chan));
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic int mv_xor_resume(struct platform_device *dev)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct mv_xor_device *xordev = platform_get_drvdata(dev);
125462306a36Sopenharmony_ci	const struct mbus_dram_target_info *dram;
125562306a36Sopenharmony_ci	int i;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
125862306a36Sopenharmony_ci		struct mv_xor_chan *mv_chan = xordev->channels[i];
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		if (!mv_chan)
126162306a36Sopenharmony_ci			continue;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		writel_relaxed(mv_chan->saved_config_reg,
126462306a36Sopenharmony_ci			       XOR_CONFIG(mv_chan));
126562306a36Sopenharmony_ci		writel_relaxed(mv_chan->saved_int_mask_reg,
126662306a36Sopenharmony_ci			       XOR_INTR_MASK(mv_chan));
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (xordev->xor_type == XOR_ARMADA_37XX) {
127062306a36Sopenharmony_ci		mv_xor_conf_mbus_windows_a3700(xordev);
127162306a36Sopenharmony_ci		return 0;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	dram = mv_mbus_dram_info();
127562306a36Sopenharmony_ci	if (dram)
127662306a36Sopenharmony_ci		mv_xor_conf_mbus_windows(xordev, dram);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	return 0;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic const struct of_device_id mv_xor_dt_ids[] = {
128262306a36Sopenharmony_ci	{ .compatible = "marvell,orion-xor", .data = (void *)XOR_ORION },
128362306a36Sopenharmony_ci	{ .compatible = "marvell,armada-380-xor", .data = (void *)XOR_ARMADA_38X },
128462306a36Sopenharmony_ci	{ .compatible = "marvell,armada-3700-xor", .data = (void *)XOR_ARMADA_37XX },
128562306a36Sopenharmony_ci	{},
128662306a36Sopenharmony_ci};
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic unsigned int mv_xor_engine_count;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic int mv_xor_probe(struct platform_device *pdev)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	const struct mbus_dram_target_info *dram;
129362306a36Sopenharmony_ci	struct mv_xor_device *xordev;
129462306a36Sopenharmony_ci	struct mv_xor_platform_data *pdata = dev_get_platdata(&pdev->dev);
129562306a36Sopenharmony_ci	struct resource *res;
129662306a36Sopenharmony_ci	unsigned int max_engines, max_channels;
129762306a36Sopenharmony_ci	int i, ret;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	dev_notice(&pdev->dev, "Marvell shared XOR driver\n");
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL);
130262306a36Sopenharmony_ci	if (!xordev)
130362306a36Sopenharmony_ci		return -ENOMEM;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130662306a36Sopenharmony_ci	if (!res)
130762306a36Sopenharmony_ci		return -ENODEV;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	xordev->xor_base = devm_ioremap(&pdev->dev, res->start,
131062306a36Sopenharmony_ci					resource_size(res));
131162306a36Sopenharmony_ci	if (!xordev->xor_base)
131262306a36Sopenharmony_ci		return -EBUSY;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
131562306a36Sopenharmony_ci	if (!res)
131662306a36Sopenharmony_ci		return -ENODEV;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	xordev->xor_high_base = devm_ioremap(&pdev->dev, res->start,
131962306a36Sopenharmony_ci					     resource_size(res));
132062306a36Sopenharmony_ci	if (!xordev->xor_high_base)
132162306a36Sopenharmony_ci		return -EBUSY;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	platform_set_drvdata(pdev, xordev);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/*
132762306a36Sopenharmony_ci	 * We need to know which type of XOR device we use before
132862306a36Sopenharmony_ci	 * setting up. In non-dt case it can only be the legacy one.
132962306a36Sopenharmony_ci	 */
133062306a36Sopenharmony_ci	xordev->xor_type = XOR_ORION;
133162306a36Sopenharmony_ci	if (pdev->dev.of_node) {
133262306a36Sopenharmony_ci		const struct of_device_id *of_id =
133362306a36Sopenharmony_ci			of_match_device(mv_xor_dt_ids,
133462306a36Sopenharmony_ci					&pdev->dev);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci		xordev->xor_type = (uintptr_t)of_id->data;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/*
134062306a36Sopenharmony_ci	 * (Re-)program MBUS remapping windows if we are asked to.
134162306a36Sopenharmony_ci	 */
134262306a36Sopenharmony_ci	if (xordev->xor_type == XOR_ARMADA_37XX) {
134362306a36Sopenharmony_ci		mv_xor_conf_mbus_windows_a3700(xordev);
134462306a36Sopenharmony_ci	} else {
134562306a36Sopenharmony_ci		dram = mv_mbus_dram_info();
134662306a36Sopenharmony_ci		if (dram)
134762306a36Sopenharmony_ci			mv_xor_conf_mbus_windows(xordev, dram);
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* Not all platforms can gate the clock, so it is not
135162306a36Sopenharmony_ci	 * an error if the clock does not exists.
135262306a36Sopenharmony_ci	 */
135362306a36Sopenharmony_ci	xordev->clk = clk_get(&pdev->dev, NULL);
135462306a36Sopenharmony_ci	if (!IS_ERR(xordev->clk))
135562306a36Sopenharmony_ci		clk_prepare_enable(xordev->clk);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/*
135862306a36Sopenharmony_ci	 * We don't want to have more than one channel per CPU in
135962306a36Sopenharmony_ci	 * order for async_tx to perform well. So we limit the number
136062306a36Sopenharmony_ci	 * of engines and channels so that we take into account this
136162306a36Sopenharmony_ci	 * constraint. Note that we also want to use channels from
136262306a36Sopenharmony_ci	 * separate engines when possible.  For dual-CPU Armada 3700
136362306a36Sopenharmony_ci	 * SoC with single XOR engine allow using its both channels.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci	max_engines = num_present_cpus();
136662306a36Sopenharmony_ci	if (xordev->xor_type == XOR_ARMADA_37XX)
136762306a36Sopenharmony_ci		max_channels =	num_present_cpus();
136862306a36Sopenharmony_ci	else
136962306a36Sopenharmony_ci		max_channels = min_t(unsigned int,
137062306a36Sopenharmony_ci				     MV_XOR_MAX_CHANNELS,
137162306a36Sopenharmony_ci				     DIV_ROUND_UP(num_present_cpus(), 2));
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (mv_xor_engine_count >= max_engines)
137462306a36Sopenharmony_ci		return 0;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (pdev->dev.of_node) {
137762306a36Sopenharmony_ci		struct device_node *np;
137862306a36Sopenharmony_ci		int i = 0;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		for_each_child_of_node(pdev->dev.of_node, np) {
138162306a36Sopenharmony_ci			struct mv_xor_chan *chan;
138262306a36Sopenharmony_ci			dma_cap_mask_t cap_mask;
138362306a36Sopenharmony_ci			int irq;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci			if (i >= max_channels)
138662306a36Sopenharmony_ci				continue;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci			dma_cap_zero(cap_mask);
138962306a36Sopenharmony_ci			dma_cap_set(DMA_MEMCPY, cap_mask);
139062306a36Sopenharmony_ci			dma_cap_set(DMA_XOR, cap_mask);
139162306a36Sopenharmony_ci			dma_cap_set(DMA_INTERRUPT, cap_mask);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci			irq = irq_of_parse_and_map(np, 0);
139462306a36Sopenharmony_ci			if (!irq) {
139562306a36Sopenharmony_ci				ret = -ENODEV;
139662306a36Sopenharmony_ci				goto err_channel_add;
139762306a36Sopenharmony_ci			}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci			chan = mv_xor_channel_add(xordev, pdev, i,
140062306a36Sopenharmony_ci						  cap_mask, irq);
140162306a36Sopenharmony_ci			if (IS_ERR(chan)) {
140262306a36Sopenharmony_ci				ret = PTR_ERR(chan);
140362306a36Sopenharmony_ci				irq_dispose_mapping(irq);
140462306a36Sopenharmony_ci				goto err_channel_add;
140562306a36Sopenharmony_ci			}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci			xordev->channels[i] = chan;
140862306a36Sopenharmony_ci			i++;
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci	} else if (pdata && pdata->channels) {
141162306a36Sopenharmony_ci		for (i = 0; i < max_channels; i++) {
141262306a36Sopenharmony_ci			struct mv_xor_channel_data *cd;
141362306a36Sopenharmony_ci			struct mv_xor_chan *chan;
141462306a36Sopenharmony_ci			int irq;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci			cd = &pdata->channels[i];
141762306a36Sopenharmony_ci			irq = platform_get_irq(pdev, i);
141862306a36Sopenharmony_ci			if (irq < 0) {
141962306a36Sopenharmony_ci				ret = irq;
142062306a36Sopenharmony_ci				goto err_channel_add;
142162306a36Sopenharmony_ci			}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci			chan = mv_xor_channel_add(xordev, pdev, i,
142462306a36Sopenharmony_ci						  cd->cap_mask, irq);
142562306a36Sopenharmony_ci			if (IS_ERR(chan)) {
142662306a36Sopenharmony_ci				ret = PTR_ERR(chan);
142762306a36Sopenharmony_ci				goto err_channel_add;
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci			xordev->channels[i] = chan;
143162306a36Sopenharmony_ci		}
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	return 0;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cierr_channel_add:
143762306a36Sopenharmony_ci	for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
143862306a36Sopenharmony_ci		if (xordev->channels[i]) {
143962306a36Sopenharmony_ci			mv_xor_channel_remove(xordev->channels[i]);
144062306a36Sopenharmony_ci			if (pdev->dev.of_node)
144162306a36Sopenharmony_ci				irq_dispose_mapping(xordev->channels[i]->irq);
144262306a36Sopenharmony_ci		}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	if (!IS_ERR(xordev->clk)) {
144562306a36Sopenharmony_ci		clk_disable_unprepare(xordev->clk);
144662306a36Sopenharmony_ci		clk_put(xordev->clk);
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	return ret;
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic struct platform_driver mv_xor_driver = {
145362306a36Sopenharmony_ci	.probe		= mv_xor_probe,
145462306a36Sopenharmony_ci	.suspend        = mv_xor_suspend,
145562306a36Sopenharmony_ci	.resume         = mv_xor_resume,
145662306a36Sopenharmony_ci	.driver		= {
145762306a36Sopenharmony_ci		.name	        = MV_XOR_NAME,
145862306a36Sopenharmony_ci		.of_match_table = mv_xor_dt_ids,
145962306a36Sopenharmony_ci	},
146062306a36Sopenharmony_ci};
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cibuiltin_platform_driver(mv_xor_driver);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci/*
146562306a36Sopenharmony_ciMODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>");
146662306a36Sopenharmony_ciMODULE_DESCRIPTION("DMA engine driver for Marvell's XOR engine");
146762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
146862306a36Sopenharmony_ci*/
1469