162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006-2009 DENX Software Engineering.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Yuri Tikhonov <yur@emcraft.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Further porting to arch/powerpc by
862306a36Sopenharmony_ci * 	Anatolij Gustschin <agust@denx.de>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * This driver supports the asynchrounous DMA copy and RAID engines available
1362306a36Sopenharmony_ci * on the AMCC PPC440SPe Processors.
1462306a36Sopenharmony_ci * Based on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x)
1562306a36Sopenharmony_ci * ADMA driver written by D.Williams.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/async_tx.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2362306a36Sopenharmony_ci#include <linux/spinlock.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/uaccess.h>
2762306a36Sopenharmony_ci#include <linux/proc_fs.h>
2862306a36Sopenharmony_ci#include <linux/of.h>
2962306a36Sopenharmony_ci#include <linux/of_address.h>
3062306a36Sopenharmony_ci#include <linux/of_irq.h>
3162306a36Sopenharmony_ci#include <linux/platform_device.h>
3262306a36Sopenharmony_ci#include <asm/dcr.h>
3362306a36Sopenharmony_ci#include <asm/dcr-regs.h>
3462306a36Sopenharmony_ci#include "adma.h"
3562306a36Sopenharmony_ci#include "../dmaengine.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum ppc_adma_init_code {
3862306a36Sopenharmony_ci	PPC_ADMA_INIT_OK = 0,
3962306a36Sopenharmony_ci	PPC_ADMA_INIT_MEMRES,
4062306a36Sopenharmony_ci	PPC_ADMA_INIT_MEMREG,
4162306a36Sopenharmony_ci	PPC_ADMA_INIT_ALLOC,
4262306a36Sopenharmony_ci	PPC_ADMA_INIT_COHERENT,
4362306a36Sopenharmony_ci	PPC_ADMA_INIT_CHANNEL,
4462306a36Sopenharmony_ci	PPC_ADMA_INIT_IRQ1,
4562306a36Sopenharmony_ci	PPC_ADMA_INIT_IRQ2,
4662306a36Sopenharmony_ci	PPC_ADMA_INIT_REGISTER
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic char *ppc_adma_errors[] = {
5062306a36Sopenharmony_ci	[PPC_ADMA_INIT_OK] = "ok",
5162306a36Sopenharmony_ci	[PPC_ADMA_INIT_MEMRES] = "failed to get memory resource",
5262306a36Sopenharmony_ci	[PPC_ADMA_INIT_MEMREG] = "failed to request memory region",
5362306a36Sopenharmony_ci	[PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev "
5462306a36Sopenharmony_ci				"structure",
5562306a36Sopenharmony_ci	[PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for "
5662306a36Sopenharmony_ci				   "hardware descriptors",
5762306a36Sopenharmony_ci	[PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel",
5862306a36Sopenharmony_ci	[PPC_ADMA_INIT_IRQ1] = "failed to request first irq",
5962306a36Sopenharmony_ci	[PPC_ADMA_INIT_IRQ2] = "failed to request second irq",
6062306a36Sopenharmony_ci	[PPC_ADMA_INIT_REGISTER] = "failed to register dma async device",
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic enum ppc_adma_init_code
6462306a36Sopenharmony_cippc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM];
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct ppc_dma_chan_ref {
6762306a36Sopenharmony_ci	struct dma_chan *chan;
6862306a36Sopenharmony_ci	struct list_head node;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* The list of channels exported by ppc440spe ADMA */
7262306a36Sopenharmony_cistatic struct list_head
7362306a36Sopenharmony_cippc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* This flag is set when want to refetch the xor chain in the interrupt
7662306a36Sopenharmony_ci * handler
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic u32 do_xor_refetch;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Pointer to DMA0, DMA1 CP/CS FIFO */
8162306a36Sopenharmony_cistatic void *ppc440spe_dma_fifo_buf;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Pointers to last submitted to DMA0, DMA1 CDBs */
8462306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *chan_last_sub[3];
8562306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *chan_first_cdb[3];
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Pointer to last linked and submitted xor CB */
8862306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *xor_last_linked;
8962306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *xor_last_submit;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* This array is used in data-check operations for storing a pattern */
9262306a36Sopenharmony_cistatic char ppc440spe_qword[16];
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic atomic_t ppc440spe_adma_err_irq_ref;
9562306a36Sopenharmony_cistatic dcr_host_t ppc440spe_mq_dcr_host;
9662306a36Sopenharmony_cistatic unsigned int ppc440spe_mq_dcr_len;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Since RXOR operations use the common register (MQ0_CF2H) for setting-up
9962306a36Sopenharmony_ci * the block size in transactions, then we do not allow to activate more than
10062306a36Sopenharmony_ci * only one RXOR transactions simultaneously. So use this var to store
10162306a36Sopenharmony_ci * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is
10262306a36Sopenharmony_ci * set) or not (PPC440SPE_RXOR_RUN is clear).
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cistatic unsigned long ppc440spe_rxor_state;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* These are used in enable & check routines
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic u32 ppc440spe_r6_enabled;
10962306a36Sopenharmony_cistatic struct ppc440spe_adma_chan *ppc440spe_r6_tchan;
11062306a36Sopenharmony_cistatic struct completion ppc440spe_r6_test_comp;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int ppc440spe_adma_dma2rxor_prep_src(
11362306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
11462306a36Sopenharmony_ci		struct ppc440spe_rxor *cursor, int index,
11562306a36Sopenharmony_ci		int src_cnt, u32 addr);
11662306a36Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_src(
11762306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
11862306a36Sopenharmony_ci		int index, dma_addr_t addr);
11962306a36Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_mult(
12062306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
12162306a36Sopenharmony_ci		int index, u8 mult);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#ifdef ADMA_LL_DEBUG
12462306a36Sopenharmony_ci#define ADMA_LL_DBG(x) ({ if (1) x; 0; })
12562306a36Sopenharmony_ci#else
12662306a36Sopenharmony_ci#define ADMA_LL_DBG(x) ({ if (0) x; 0; })
12762306a36Sopenharmony_ci#endif
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void print_cb(struct ppc440spe_adma_chan *chan, void *block)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct dma_cdb *cdb;
13262306a36Sopenharmony_ci	struct xor_cb *cb;
13362306a36Sopenharmony_ci	int i;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	switch (chan->device->id) {
13662306a36Sopenharmony_ci	case 0:
13762306a36Sopenharmony_ci	case 1:
13862306a36Sopenharmony_ci		cdb = block;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		pr_debug("CDB at %p [%d]:\n"
14162306a36Sopenharmony_ci			"\t attr 0x%02x opc 0x%02x cnt 0x%08x\n"
14262306a36Sopenharmony_ci			"\t sg1u 0x%08x sg1l 0x%08x\n"
14362306a36Sopenharmony_ci			"\t sg2u 0x%08x sg2l 0x%08x\n"
14462306a36Sopenharmony_ci			"\t sg3u 0x%08x sg3l 0x%08x\n",
14562306a36Sopenharmony_ci			cdb, chan->device->id,
14662306a36Sopenharmony_ci			cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt),
14762306a36Sopenharmony_ci			le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l),
14862306a36Sopenharmony_ci			le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l),
14962306a36Sopenharmony_ci			le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l)
15062306a36Sopenharmony_ci		);
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	case 2:
15362306a36Sopenharmony_ci		cb = block;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		pr_debug("CB at %p [%d]:\n"
15662306a36Sopenharmony_ci			"\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n"
15762306a36Sopenharmony_ci			"\t cbtah 0x%08x cbtal 0x%08x\n"
15862306a36Sopenharmony_ci			"\t cblah 0x%08x cblal 0x%08x\n",
15962306a36Sopenharmony_ci			cb, chan->device->id,
16062306a36Sopenharmony_ci			cb->cbc, cb->cbbc, cb->cbs,
16162306a36Sopenharmony_ci			cb->cbtah, cb->cbtal,
16262306a36Sopenharmony_ci			cb->cblah, cb->cblal);
16362306a36Sopenharmony_ci		for (i = 0; i < 16; i++) {
16462306a36Sopenharmony_ci			if (i && !cb->ops[i].h && !cb->ops[i].l)
16562306a36Sopenharmony_ci				continue;
16662306a36Sopenharmony_ci			pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n",
16762306a36Sopenharmony_ci				i, cb->ops[i].h, cb->ops[i].l);
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void print_cb_list(struct ppc440spe_adma_chan *chan,
17462306a36Sopenharmony_ci			  struct ppc440spe_adma_desc_slot *iter)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	for (; iter; iter = iter->hw_next)
17762306a36Sopenharmony_ci		print_cb(chan, iter->hw_desc);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src,
18162306a36Sopenharmony_ci			     unsigned int src_cnt)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	int i;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pr_debug("\n%s(%d):\nsrc: ", __func__, id);
18662306a36Sopenharmony_ci	for (i = 0; i < src_cnt; i++)
18762306a36Sopenharmony_ci		pr_debug("\t0x%016llx ", src[i]);
18862306a36Sopenharmony_ci	pr_debug("dst:\n\t0x%016llx\n", dst);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
19262306a36Sopenharmony_ci			    unsigned int src_cnt)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int i;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	pr_debug("\n%s(%d):\nsrc: ", __func__, id);
19762306a36Sopenharmony_ci	for (i = 0; i < src_cnt; i++)
19862306a36Sopenharmony_ci		pr_debug("\t0x%016llx ", src[i]);
19962306a36Sopenharmony_ci	pr_debug("dst: ");
20062306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
20162306a36Sopenharmony_ci		pr_debug("\t0x%016llx ", dst[i]);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
20562306a36Sopenharmony_ci				    unsigned int src_cnt,
20662306a36Sopenharmony_ci				    const unsigned char *scf)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int i;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	pr_debug("\n%s(%d):\nsrc(coef): ", __func__, id);
21162306a36Sopenharmony_ci	if (scf) {
21262306a36Sopenharmony_ci		for (i = 0; i < src_cnt; i++)
21362306a36Sopenharmony_ci			pr_debug("\t0x%016llx(0x%02x) ", src[i], scf[i]);
21462306a36Sopenharmony_ci	} else {
21562306a36Sopenharmony_ci		for (i = 0; i < src_cnt; i++)
21662306a36Sopenharmony_ci			pr_debug("\t0x%016llx(no) ", src[i]);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	pr_debug("dst: ");
22062306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
22162306a36Sopenharmony_ci		pr_debug("\t0x%016llx ", src[src_cnt + i]);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/******************************************************************************
22562306a36Sopenharmony_ci * Command (Descriptor) Blocks low-level routines
22662306a36Sopenharmony_ci ******************************************************************************/
22762306a36Sopenharmony_ci/**
22862306a36Sopenharmony_ci * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT
22962306a36Sopenharmony_ci * pseudo operation
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot *desc,
23262306a36Sopenharmony_ci					  struct ppc440spe_adma_chan *chan)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct xor_cb *p;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	switch (chan->device->id) {
23762306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
23862306a36Sopenharmony_ci		p = desc->hw_desc;
23962306a36Sopenharmony_ci		memset(desc->hw_desc, 0, sizeof(struct xor_cb));
24062306a36Sopenharmony_ci		/* NOP with Command Block Complete Enable */
24162306a36Sopenharmony_ci		p->cbc = XOR_CBCR_CBCE_BIT;
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
24462306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
24562306a36Sopenharmony_ci		memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
24662306a36Sopenharmony_ci		/* NOP with interrupt */
24762306a36Sopenharmony_ci		set_bit(PPC440SPE_DESC_INT, &desc->flags);
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	default:
25062306a36Sopenharmony_ci		printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id,
25162306a36Sopenharmony_ci				__func__);
25262306a36Sopenharmony_ci		break;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/**
25762306a36Sopenharmony_ci * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR
25862306a36Sopenharmony_ci * pseudo operation
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_cistatic void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
26362306a36Sopenharmony_ci	desc->hw_next = NULL;
26462306a36Sopenharmony_ci	desc->src_cnt = 0;
26562306a36Sopenharmony_ci	desc->dst_cnt = 1;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/**
26962306a36Sopenharmony_ci * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc,
27262306a36Sopenharmony_ci					 int src_cnt, unsigned long flags)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct xor_cb *hw_desc = desc->hw_desc;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
27762306a36Sopenharmony_ci	desc->hw_next = NULL;
27862306a36Sopenharmony_ci	desc->src_cnt = src_cnt;
27962306a36Sopenharmony_ci	desc->dst_cnt = 1;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt;
28262306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
28362306a36Sopenharmony_ci		/* Enable interrupt on completion */
28462306a36Sopenharmony_ci		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/**
28862306a36Sopenharmony_ci * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ
28962306a36Sopenharmony_ci * operation in DMA2 controller
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc,
29262306a36Sopenharmony_ci		int dst_cnt, int src_cnt, unsigned long flags)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct xor_cb *hw_desc = desc->hw_desc;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
29762306a36Sopenharmony_ci	desc->hw_next = NULL;
29862306a36Sopenharmony_ci	desc->src_cnt = src_cnt;
29962306a36Sopenharmony_ci	desc->dst_cnt = dst_cnt;
30062306a36Sopenharmony_ci	memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags));
30162306a36Sopenharmony_ci	desc->descs_per_op = 0;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	hw_desc->cbc = XOR_CBCR_TGT_BIT;
30462306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
30562306a36Sopenharmony_ci		/* Enable interrupt on completion */
30662306a36Sopenharmony_ci		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci#define DMA_CTRL_FLAGS_LAST	DMA_PREP_FENCE
31062306a36Sopenharmony_ci#define DMA_PREP_ZERO_P		(DMA_CTRL_FLAGS_LAST << 1)
31162306a36Sopenharmony_ci#define DMA_PREP_ZERO_Q		(DMA_PREP_ZERO_P << 1)
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation
31562306a36Sopenharmony_ci * with DMA0/1
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistatic void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc,
31862306a36Sopenharmony_ci				int dst_cnt, int src_cnt, unsigned long flags,
31962306a36Sopenharmony_ci				unsigned long op)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct dma_cdb *hw_desc;
32262306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter;
32362306a36Sopenharmony_ci	u8 dopc;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Common initialization of a PQ descriptors chain */
32662306a36Sopenharmony_ci	set_bits(op, &desc->flags);
32762306a36Sopenharmony_ci	desc->src_cnt = src_cnt;
32862306a36Sopenharmony_ci	desc->dst_cnt = dst_cnt;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* WXOR MULTICAST if both P and Q are being computed
33162306a36Sopenharmony_ci	 * MV_SG1_SG2 if Q only
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ?
33462306a36Sopenharmony_ci		DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	list_for_each_entry(iter, &desc->group_list, chain_node) {
33762306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
33862306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		if (likely(!list_is_last(&iter->chain_node,
34162306a36Sopenharmony_ci				&desc->group_list))) {
34262306a36Sopenharmony_ci			/* set 'next' pointer */
34362306a36Sopenharmony_ci			iter->hw_next = list_entry(iter->chain_node.next,
34462306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot, chain_node);
34562306a36Sopenharmony_ci			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
34662306a36Sopenharmony_ci		} else {
34762306a36Sopenharmony_ci			/* this is the last descriptor.
34862306a36Sopenharmony_ci			 * this slot will be pasted from ADMA level
34962306a36Sopenharmony_ci			 * each time it wants to configure parameters
35062306a36Sopenharmony_ci			 * of the transaction (src, dst, ...)
35162306a36Sopenharmony_ci			 */
35262306a36Sopenharmony_ci			iter->hw_next = NULL;
35362306a36Sopenharmony_ci			if (flags & DMA_PREP_INTERRUPT)
35462306a36Sopenharmony_ci				set_bit(PPC440SPE_DESC_INT, &iter->flags);
35562306a36Sopenharmony_ci			else
35662306a36Sopenharmony_ci				clear_bit(PPC440SPE_DESC_INT, &iter->flags);
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Set OPS depending on WXOR/RXOR type of operation */
36162306a36Sopenharmony_ci	if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) {
36262306a36Sopenharmony_ci		/* This is a WXOR only chain:
36362306a36Sopenharmony_ci		 * - first descriptors are for zeroing destinations
36462306a36Sopenharmony_ci		 *   if PPC440SPE_ZERO_P/Q set;
36562306a36Sopenharmony_ci		 * - descriptors remained are for GF-XOR operations.
36662306a36Sopenharmony_ci		 */
36762306a36Sopenharmony_ci		iter = list_first_entry(&desc->group_list,
36862306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
36962306a36Sopenharmony_ci					chain_node);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) {
37262306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
37362306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
37462306a36Sopenharmony_ci			iter = list_first_entry(&iter->chain_node,
37562306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
37662306a36Sopenharmony_ci					chain_node);
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) {
38062306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
38162306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
38262306a36Sopenharmony_ci			iter = list_first_entry(&iter->chain_node,
38362306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
38462306a36Sopenharmony_ci					chain_node);
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		list_for_each_entry_from(iter, &desc->group_list, chain_node) {
38862306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
38962306a36Sopenharmony_ci			hw_desc->opc = dopc;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci	} else {
39262306a36Sopenharmony_ci		/* This is either RXOR-only or mixed RXOR/WXOR */
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		/* The first 1 or 2 slots in chain are always RXOR,
39562306a36Sopenharmony_ci		 * if need to calculate P & Q, then there are two
39662306a36Sopenharmony_ci		 * RXOR slots; if only P or only Q, then there is one
39762306a36Sopenharmony_ci		 */
39862306a36Sopenharmony_ci		iter = list_first_entry(&desc->group_list,
39962306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
40062306a36Sopenharmony_ci					chain_node);
40162306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
40262306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (desc->dst_cnt == DMA_DEST_MAX_NUM) {
40562306a36Sopenharmony_ci			iter = list_first_entry(&iter->chain_node,
40662306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
40762306a36Sopenharmony_ci						chain_node);
40862306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
40962306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* The remaining descs (if any) are WXORs */
41362306a36Sopenharmony_ci		if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) {
41462306a36Sopenharmony_ci			iter = list_first_entry(&iter->chain_node,
41562306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
41662306a36Sopenharmony_ci						chain_node);
41762306a36Sopenharmony_ci			list_for_each_entry_from(iter, &desc->group_list,
41862306a36Sopenharmony_ci						chain_node) {
41962306a36Sopenharmony_ci				hw_desc = iter->hw_desc;
42062306a36Sopenharmony_ci				hw_desc->opc = dopc;
42162306a36Sopenharmony_ci			}
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/**
42762306a36Sopenharmony_ci * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor
42862306a36Sopenharmony_ci * for PQ_ZERO_SUM operation
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_cistatic void ppc440spe_desc_init_dma01pqzero_sum(
43162306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *desc,
43262306a36Sopenharmony_ci				int dst_cnt, int src_cnt)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct dma_cdb *hw_desc;
43562306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter;
43662306a36Sopenharmony_ci	int i = 0;
43762306a36Sopenharmony_ci	u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST :
43862306a36Sopenharmony_ci				   DMA_CDB_OPC_MV_SG1_SG2;
43962306a36Sopenharmony_ci	/*
44062306a36Sopenharmony_ci	 * Initialize starting from 2nd or 3rd descriptor dependent
44162306a36Sopenharmony_ci	 * on dst_cnt. First one or two slots are for cloning P
44262306a36Sopenharmony_ci	 * and/or Q to chan->pdest and/or chan->qdest as we have
44362306a36Sopenharmony_ci	 * to preserve original P/Q.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	iter = list_first_entry(&desc->group_list,
44662306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot, chain_node);
44762306a36Sopenharmony_ci	iter = list_entry(iter->chain_node.next,
44862306a36Sopenharmony_ci			  struct ppc440spe_adma_desc_slot, chain_node);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (dst_cnt > 1) {
45162306a36Sopenharmony_ci		iter = list_entry(iter->chain_node.next,
45262306a36Sopenharmony_ci				  struct ppc440spe_adma_desc_slot, chain_node);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci	/* initialize each source descriptor in chain */
45562306a36Sopenharmony_ci	list_for_each_entry_from(iter, &desc->group_list, chain_node) {
45662306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
45762306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
45862306a36Sopenharmony_ci		iter->src_cnt = 0;
45962306a36Sopenharmony_ci		iter->dst_cnt = 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* This is a ZERO_SUM operation:
46262306a36Sopenharmony_ci		 * - <src_cnt> descriptors starting from 2nd or 3rd
46362306a36Sopenharmony_ci		 *   descriptor are for GF-XOR operations;
46462306a36Sopenharmony_ci		 * - remaining <dst_cnt> descriptors are for checking the result
46562306a36Sopenharmony_ci		 */
46662306a36Sopenharmony_ci		if (i++ < src_cnt)
46762306a36Sopenharmony_ci			/* MV_SG1_SG2 if only Q is being verified
46862306a36Sopenharmony_ci			 * MULTICAST if both P and Q are being verified
46962306a36Sopenharmony_ci			 */
47062306a36Sopenharmony_ci			hw_desc->opc = dopc;
47162306a36Sopenharmony_ci		else
47262306a36Sopenharmony_ci			/* DMA_CDB_OPC_DCHECK128 operation */
47362306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_DCHECK128;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		if (likely(!list_is_last(&iter->chain_node,
47662306a36Sopenharmony_ci					 &desc->group_list))) {
47762306a36Sopenharmony_ci			/* set 'next' pointer */
47862306a36Sopenharmony_ci			iter->hw_next = list_entry(iter->chain_node.next,
47962306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
48062306a36Sopenharmony_ci						chain_node);
48162306a36Sopenharmony_ci		} else {
48262306a36Sopenharmony_ci			/* this is the last descriptor.
48362306a36Sopenharmony_ci			 * this slot will be pasted from ADMA level
48462306a36Sopenharmony_ci			 * each time it wants to configure parameters
48562306a36Sopenharmony_ci			 * of the transaction (src, dst, ...)
48662306a36Sopenharmony_ci			 */
48762306a36Sopenharmony_ci			iter->hw_next = NULL;
48862306a36Sopenharmony_ci			/* always enable interrupt generation since we get
48962306a36Sopenharmony_ci			 * the status of pqzero from the handler
49062306a36Sopenharmony_ci			 */
49162306a36Sopenharmony_ci			set_bit(PPC440SPE_DESC_INT, &iter->flags);
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci	desc->src_cnt = src_cnt;
49562306a36Sopenharmony_ci	desc->dst_cnt = dst_cnt;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/**
49962306a36Sopenharmony_ci * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc,
50262306a36Sopenharmony_ci					unsigned long flags)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct dma_cdb *hw_desc = desc->hw_desc;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
50762306a36Sopenharmony_ci	desc->hw_next = NULL;
50862306a36Sopenharmony_ci	desc->src_cnt = 1;
50962306a36Sopenharmony_ci	desc->dst_cnt = 1;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
51262306a36Sopenharmony_ci		set_bit(PPC440SPE_DESC_INT, &desc->flags);
51362306a36Sopenharmony_ci	else
51462306a36Sopenharmony_ci		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/**
52062306a36Sopenharmony_ci * ppc440spe_desc_set_src_addr - set source address into the descriptor
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_cistatic void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc,
52362306a36Sopenharmony_ci					struct ppc440spe_adma_chan *chan,
52462306a36Sopenharmony_ci					int src_idx, dma_addr_t addrh,
52562306a36Sopenharmony_ci					dma_addr_t addrl)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct dma_cdb *dma_hw_desc;
52862306a36Sopenharmony_ci	struct xor_cb *xor_hw_desc;
52962306a36Sopenharmony_ci	phys_addr_t addr64, tmplow, tmphi;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	switch (chan->device->id) {
53262306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
53362306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
53462306a36Sopenharmony_ci		if (!addrh) {
53562306a36Sopenharmony_ci			addr64 = addrl;
53662306a36Sopenharmony_ci			tmphi = (addr64 >> 32);
53762306a36Sopenharmony_ci			tmplow = (addr64 & 0xFFFFFFFF);
53862306a36Sopenharmony_ci		} else {
53962306a36Sopenharmony_ci			tmphi = addrh;
54062306a36Sopenharmony_ci			tmplow = addrl;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci		dma_hw_desc = desc->hw_desc;
54362306a36Sopenharmony_ci		dma_hw_desc->sg1l = cpu_to_le32((u32)tmplow);
54462306a36Sopenharmony_ci		dma_hw_desc->sg1u |= cpu_to_le32((u32)tmphi);
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
54762306a36Sopenharmony_ci		xor_hw_desc = desc->hw_desc;
54862306a36Sopenharmony_ci		xor_hw_desc->ops[src_idx].l = addrl;
54962306a36Sopenharmony_ci		xor_hw_desc->ops[src_idx].h |= addrh;
55062306a36Sopenharmony_ci		break;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/**
55562306a36Sopenharmony_ci * ppc440spe_desc_set_src_mult - set source address mult into the descriptor
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_cistatic void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
55862306a36Sopenharmony_ci			struct ppc440spe_adma_chan *chan, u32 mult_index,
55962306a36Sopenharmony_ci			int sg_index, unsigned char mult_value)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct dma_cdb *dma_hw_desc;
56262306a36Sopenharmony_ci	u32 *psgu;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	switch (chan->device->id) {
56562306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
56662306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
56762306a36Sopenharmony_ci		dma_hw_desc = desc->hw_desc;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		switch (sg_index) {
57062306a36Sopenharmony_ci		/* for RXOR operations set multiplier
57162306a36Sopenharmony_ci		 * into source cued address
57262306a36Sopenharmony_ci		 */
57362306a36Sopenharmony_ci		case DMA_CDB_SG_SRC:
57462306a36Sopenharmony_ci			psgu = &dma_hw_desc->sg1u;
57562306a36Sopenharmony_ci			break;
57662306a36Sopenharmony_ci		/* for WXOR operations set multiplier
57762306a36Sopenharmony_ci		 * into destination cued address(es)
57862306a36Sopenharmony_ci		 */
57962306a36Sopenharmony_ci		case DMA_CDB_SG_DST1:
58062306a36Sopenharmony_ci			psgu = &dma_hw_desc->sg2u;
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		case DMA_CDB_SG_DST2:
58362306a36Sopenharmony_ci			psgu = &dma_hw_desc->sg3u;
58462306a36Sopenharmony_ci			break;
58562306a36Sopenharmony_ci		default:
58662306a36Sopenharmony_ci			BUG();
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		*psgu |= cpu_to_le32(mult_value << mult_index);
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	default:
59462306a36Sopenharmony_ci		BUG();
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/**
59962306a36Sopenharmony_ci * ppc440spe_desc_set_dest_addr - set destination address into the descriptor
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_cistatic void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc,
60262306a36Sopenharmony_ci				struct ppc440spe_adma_chan *chan,
60362306a36Sopenharmony_ci				dma_addr_t addrh, dma_addr_t addrl,
60462306a36Sopenharmony_ci				u32 dst_idx)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct dma_cdb *dma_hw_desc;
60762306a36Sopenharmony_ci	struct xor_cb *xor_hw_desc;
60862306a36Sopenharmony_ci	phys_addr_t addr64, tmphi, tmplow;
60962306a36Sopenharmony_ci	u32 *psgu, *psgl;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	switch (chan->device->id) {
61262306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
61362306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
61462306a36Sopenharmony_ci		if (!addrh) {
61562306a36Sopenharmony_ci			addr64 = addrl;
61662306a36Sopenharmony_ci			tmphi = (addr64 >> 32);
61762306a36Sopenharmony_ci			tmplow = (addr64 & 0xFFFFFFFF);
61862306a36Sopenharmony_ci		} else {
61962306a36Sopenharmony_ci			tmphi = addrh;
62062306a36Sopenharmony_ci			tmplow = addrl;
62162306a36Sopenharmony_ci		}
62262306a36Sopenharmony_ci		dma_hw_desc = desc->hw_desc;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u;
62562306a36Sopenharmony_ci		psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		*psgl = cpu_to_le32((u32)tmplow);
62862306a36Sopenharmony_ci		*psgu |= cpu_to_le32((u32)tmphi);
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
63162306a36Sopenharmony_ci		xor_hw_desc = desc->hw_desc;
63262306a36Sopenharmony_ci		xor_hw_desc->cbtal = addrl;
63362306a36Sopenharmony_ci		xor_hw_desc->cbtah |= addrh;
63462306a36Sopenharmony_ci		break;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * ppc440spe_desc_set_byte_count - set number of data bytes involved
64062306a36Sopenharmony_ci * into the operation
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot *desc,
64362306a36Sopenharmony_ci				struct ppc440spe_adma_chan *chan,
64462306a36Sopenharmony_ci				u32 byte_count)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct dma_cdb *dma_hw_desc;
64762306a36Sopenharmony_ci	struct xor_cb *xor_hw_desc;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	switch (chan->device->id) {
65062306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
65162306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
65262306a36Sopenharmony_ci		dma_hw_desc = desc->hw_desc;
65362306a36Sopenharmony_ci		dma_hw_desc->cnt = cpu_to_le32(byte_count);
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
65662306a36Sopenharmony_ci		xor_hw_desc = desc->hw_desc;
65762306a36Sopenharmony_ci		xor_hw_desc->cbbc = byte_count;
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/**
66362306a36Sopenharmony_ci * ppc440spe_desc_set_rxor_block_size - set RXOR block size
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_cistatic inline void ppc440spe_desc_set_rxor_block_size(u32 byte_count)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	/* assume that byte_count is aligned on the 512-boundary;
66862306a36Sopenharmony_ci	 * thus write it directly to the register (bits 23:31 are
66962306a36Sopenharmony_ci	 * reserved there).
67062306a36Sopenharmony_ci	 */
67162306a36Sopenharmony_ci	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count);
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/**
67562306a36Sopenharmony_ci * ppc440spe_desc_set_dcheck - set CHECK pattern
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_cistatic void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc,
67862306a36Sopenharmony_ci				struct ppc440spe_adma_chan *chan, u8 *qword)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct dma_cdb *dma_hw_desc;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	switch (chan->device->id) {
68362306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
68462306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
68562306a36Sopenharmony_ci		dma_hw_desc = desc->hw_desc;
68662306a36Sopenharmony_ci		iowrite32(qword[0], &dma_hw_desc->sg3l);
68762306a36Sopenharmony_ci		iowrite32(qword[4], &dma_hw_desc->sg3u);
68862306a36Sopenharmony_ci		iowrite32(qword[8], &dma_hw_desc->sg2l);
68962306a36Sopenharmony_ci		iowrite32(qword[12], &dma_hw_desc->sg2u);
69062306a36Sopenharmony_ci		break;
69162306a36Sopenharmony_ci	default:
69262306a36Sopenharmony_ci		BUG();
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci/**
69762306a36Sopenharmony_ci * ppc440spe_xor_set_link - set link address in xor CB
69862306a36Sopenharmony_ci */
69962306a36Sopenharmony_cistatic void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
70062306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *next_desc)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct xor_cb *xor_hw_desc = prev_desc->hw_desc;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (unlikely(!next_desc || !(next_desc->phys))) {
70562306a36Sopenharmony_ci		printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n",
70662306a36Sopenharmony_ci			__func__, next_desc,
70762306a36Sopenharmony_ci			next_desc ? next_desc->phys : 0);
70862306a36Sopenharmony_ci		BUG();
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	xor_hw_desc->cbs = 0;
71262306a36Sopenharmony_ci	xor_hw_desc->cblal = next_desc->phys;
71362306a36Sopenharmony_ci	xor_hw_desc->cblah = 0;
71462306a36Sopenharmony_ci	xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci/**
71862306a36Sopenharmony_ci * ppc440spe_desc_set_link - set the address of descriptor following this
71962306a36Sopenharmony_ci * descriptor in chain
72062306a36Sopenharmony_ci */
72162306a36Sopenharmony_cistatic void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
72262306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *prev_desc,
72362306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *next_desc)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	unsigned long flags;
72662306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *tail = next_desc;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (unlikely(!prev_desc || !next_desc ||
72962306a36Sopenharmony_ci		(prev_desc->hw_next && prev_desc->hw_next != next_desc))) {
73062306a36Sopenharmony_ci		/* If previous next is overwritten something is wrong.
73162306a36Sopenharmony_ci		 * though we may refetch from append to initiate list
73262306a36Sopenharmony_ci		 * processing; in this case - it's ok.
73362306a36Sopenharmony_ci		 */
73462306a36Sopenharmony_ci		printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; "
73562306a36Sopenharmony_ci			"prev->hw_next=0x%p\n", __func__, prev_desc,
73662306a36Sopenharmony_ci			next_desc, prev_desc ? prev_desc->hw_next : 0);
73762306a36Sopenharmony_ci		BUG();
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	local_irq_save(flags);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/* do s/w chaining both for DMA and XOR descriptors */
74362306a36Sopenharmony_ci	prev_desc->hw_next = next_desc;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	switch (chan->device->id) {
74662306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
74762306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
75062306a36Sopenharmony_ci		/* bind descriptor to the chain */
75162306a36Sopenharmony_ci		while (tail->hw_next)
75262306a36Sopenharmony_ci			tail = tail->hw_next;
75362306a36Sopenharmony_ci		xor_last_linked = tail;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (prev_desc == xor_last_submit)
75662306a36Sopenharmony_ci			/* do not link to the last submitted CB */
75762306a36Sopenharmony_ci			break;
75862306a36Sopenharmony_ci		ppc440spe_xor_set_link(prev_desc, next_desc);
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	local_irq_restore(flags);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/**
76662306a36Sopenharmony_ci * ppc440spe_desc_get_link - get the address of the descriptor that
76762306a36Sopenharmony_ci * follows this one
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_cistatic inline u32 ppc440spe_desc_get_link(struct ppc440spe_adma_desc_slot *desc,
77062306a36Sopenharmony_ci					struct ppc440spe_adma_chan *chan)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	if (!desc->hw_next)
77362306a36Sopenharmony_ci		return 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return desc->hw_next->phys;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci/**
77962306a36Sopenharmony_ci * ppc440spe_desc_is_aligned - check alignment
78062306a36Sopenharmony_ci */
78162306a36Sopenharmony_cistatic inline int ppc440spe_desc_is_aligned(
78262306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *desc, int num_slots)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	return (desc->idx & (num_slots - 1)) ? 0 : 1;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/**
78862306a36Sopenharmony_ci * ppc440spe_chan_xor_slot_count - get the number of slots necessary for
78962306a36Sopenharmony_ci * XOR operation
79062306a36Sopenharmony_ci */
79162306a36Sopenharmony_cistatic int ppc440spe_chan_xor_slot_count(size_t len, int src_cnt,
79262306a36Sopenharmony_ci			int *slots_per_op)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	int slot_cnt;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* each XOR descriptor provides up to 16 source operands */
79762306a36Sopenharmony_ci	slot_cnt = *slots_per_op = (src_cnt + XOR_MAX_OPS - 1)/XOR_MAX_OPS;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (likely(len <= PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT))
80062306a36Sopenharmony_ci		return slot_cnt;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	printk(KERN_ERR "%s: len %d > max %d !!\n",
80362306a36Sopenharmony_ci		__func__, len, PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
80462306a36Sopenharmony_ci	BUG();
80562306a36Sopenharmony_ci	return slot_cnt;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/**
80962306a36Sopenharmony_ci * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for
81062306a36Sopenharmony_ci * DMA2 PQ operation
81162306a36Sopenharmony_ci */
81262306a36Sopenharmony_cistatic int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs,
81362306a36Sopenharmony_ci		int src_cnt, size_t len)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	signed long long order = 0;
81662306a36Sopenharmony_ci	int state = 0;
81762306a36Sopenharmony_ci	int addr_count = 0;
81862306a36Sopenharmony_ci	int i;
81962306a36Sopenharmony_ci	for (i = 1; i < src_cnt; i++) {
82062306a36Sopenharmony_ci		dma_addr_t cur_addr = srcs[i];
82162306a36Sopenharmony_ci		dma_addr_t old_addr = srcs[i-1];
82262306a36Sopenharmony_ci		switch (state) {
82362306a36Sopenharmony_ci		case 0:
82462306a36Sopenharmony_ci			if (cur_addr == old_addr + len) {
82562306a36Sopenharmony_ci				/* direct RXOR */
82662306a36Sopenharmony_ci				order = 1;
82762306a36Sopenharmony_ci				state = 1;
82862306a36Sopenharmony_ci				if (i == src_cnt-1)
82962306a36Sopenharmony_ci					addr_count++;
83062306a36Sopenharmony_ci			} else if (old_addr == cur_addr + len) {
83162306a36Sopenharmony_ci				/* reverse RXOR */
83262306a36Sopenharmony_ci				order = -1;
83362306a36Sopenharmony_ci				state = 1;
83462306a36Sopenharmony_ci				if (i == src_cnt-1)
83562306a36Sopenharmony_ci					addr_count++;
83662306a36Sopenharmony_ci			} else {
83762306a36Sopenharmony_ci				state = 3;
83862306a36Sopenharmony_ci			}
83962306a36Sopenharmony_ci			break;
84062306a36Sopenharmony_ci		case 1:
84162306a36Sopenharmony_ci			if (i == src_cnt-2 || (order == -1
84262306a36Sopenharmony_ci				&& cur_addr != old_addr - len)) {
84362306a36Sopenharmony_ci				order = 0;
84462306a36Sopenharmony_ci				state = 0;
84562306a36Sopenharmony_ci				addr_count++;
84662306a36Sopenharmony_ci			} else if (cur_addr == old_addr + len*order) {
84762306a36Sopenharmony_ci				state = 2;
84862306a36Sopenharmony_ci				if (i == src_cnt-1)
84962306a36Sopenharmony_ci					addr_count++;
85062306a36Sopenharmony_ci			} else if (cur_addr == old_addr + 2*len) {
85162306a36Sopenharmony_ci				state = 2;
85262306a36Sopenharmony_ci				if (i == src_cnt-1)
85362306a36Sopenharmony_ci					addr_count++;
85462306a36Sopenharmony_ci			} else if (cur_addr == old_addr + 3*len) {
85562306a36Sopenharmony_ci				state = 2;
85662306a36Sopenharmony_ci				if (i == src_cnt-1)
85762306a36Sopenharmony_ci					addr_count++;
85862306a36Sopenharmony_ci			} else {
85962306a36Sopenharmony_ci				order = 0;
86062306a36Sopenharmony_ci				state = 0;
86162306a36Sopenharmony_ci				addr_count++;
86262306a36Sopenharmony_ci			}
86362306a36Sopenharmony_ci			break;
86462306a36Sopenharmony_ci		case 2:
86562306a36Sopenharmony_ci			order = 0;
86662306a36Sopenharmony_ci			state = 0;
86762306a36Sopenharmony_ci			addr_count++;
86862306a36Sopenharmony_ci				break;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci		if (state == 3)
87162306a36Sopenharmony_ci			break;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	if (src_cnt <= 1 || (state != 1 && state != 2)) {
87462306a36Sopenharmony_ci		pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n",
87562306a36Sopenharmony_ci			__func__, src_cnt, state, addr_count, order);
87662306a36Sopenharmony_ci		for (i = 0; i < src_cnt; i++)
87762306a36Sopenharmony_ci			pr_err("\t[%d] 0x%llx \n", i, srcs[i]);
87862306a36Sopenharmony_ci		BUG();
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci/******************************************************************************
88662306a36Sopenharmony_ci * ADMA channel low-level routines
88762306a36Sopenharmony_ci ******************************************************************************/
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic u32
89062306a36Sopenharmony_cippc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan);
89162306a36Sopenharmony_cistatic void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci/**
89462306a36Sopenharmony_ci * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_cistatic void ppc440spe_adma_device_clear_eot_status(
89762306a36Sopenharmony_ci					struct ppc440spe_adma_chan *chan)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct dma_regs *dma_reg;
90062306a36Sopenharmony_ci	struct xor_regs *xor_reg;
90162306a36Sopenharmony_ci	u8 *p = chan->device->dma_desc_pool_virt;
90262306a36Sopenharmony_ci	struct dma_cdb *cdb;
90362306a36Sopenharmony_ci	u32 rv, i;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	switch (chan->device->id) {
90662306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
90762306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
90862306a36Sopenharmony_ci		/* read FIFO to ack */
90962306a36Sopenharmony_ci		dma_reg = chan->device->dma_reg;
91062306a36Sopenharmony_ci		while ((rv = ioread32(&dma_reg->csfpl))) {
91162306a36Sopenharmony_ci			i = rv & DMA_CDB_ADDR_MSK;
91262306a36Sopenharmony_ci			cdb = (struct dma_cdb *)&p[i -
91362306a36Sopenharmony_ci			    (u32)chan->device->dma_desc_pool];
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci			/* Clear opcode to ack. This is necessary for
91662306a36Sopenharmony_ci			 * ZeroSum operations only
91762306a36Sopenharmony_ci			 */
91862306a36Sopenharmony_ci			cdb->opc = 0;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci			if (test_bit(PPC440SPE_RXOR_RUN,
92162306a36Sopenharmony_ci			    &ppc440spe_rxor_state)) {
92262306a36Sopenharmony_ci				/* probably this is a completed RXOR op,
92362306a36Sopenharmony_ci				 * get pointer to CDB using the fact that
92462306a36Sopenharmony_ci				 * physical and virtual addresses of CDB
92562306a36Sopenharmony_ci				 * in pools have the same offsets
92662306a36Sopenharmony_ci				 */
92762306a36Sopenharmony_ci				if (le32_to_cpu(cdb->sg1u) &
92862306a36Sopenharmony_ci				    DMA_CUED_XOR_BASE) {
92962306a36Sopenharmony_ci					/* this is a RXOR */
93062306a36Sopenharmony_ci					clear_bit(PPC440SPE_RXOR_RUN,
93162306a36Sopenharmony_ci						  &ppc440spe_rxor_state);
93262306a36Sopenharmony_ci				}
93362306a36Sopenharmony_ci			}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci			if (rv & DMA_CDB_STATUS_MSK) {
93662306a36Sopenharmony_ci				/* ZeroSum check failed
93762306a36Sopenharmony_ci				 */
93862306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *iter;
93962306a36Sopenharmony_ci				dma_addr_t phys = rv & ~DMA_CDB_MSK;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci				/*
94262306a36Sopenharmony_ci				 * Update the status of corresponding
94362306a36Sopenharmony_ci				 * descriptor.
94462306a36Sopenharmony_ci				 */
94562306a36Sopenharmony_ci				list_for_each_entry(iter, &chan->chain,
94662306a36Sopenharmony_ci				    chain_node) {
94762306a36Sopenharmony_ci					if (iter->phys == phys)
94862306a36Sopenharmony_ci						break;
94962306a36Sopenharmony_ci				}
95062306a36Sopenharmony_ci				/*
95162306a36Sopenharmony_ci				 * if cannot find the corresponding
95262306a36Sopenharmony_ci				 * slot it's a bug
95362306a36Sopenharmony_ci				 */
95462306a36Sopenharmony_ci				BUG_ON(&iter->chain_node == &chan->chain);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci				if (iter->xor_check_result) {
95762306a36Sopenharmony_ci					if (test_bit(PPC440SPE_DESC_PCHECK,
95862306a36Sopenharmony_ci						     &iter->flags)) {
95962306a36Sopenharmony_ci						*iter->xor_check_result |=
96062306a36Sopenharmony_ci							SUM_CHECK_P_RESULT;
96162306a36Sopenharmony_ci					} else
96262306a36Sopenharmony_ci					if (test_bit(PPC440SPE_DESC_QCHECK,
96362306a36Sopenharmony_ci						     &iter->flags)) {
96462306a36Sopenharmony_ci						*iter->xor_check_result |=
96562306a36Sopenharmony_ci							SUM_CHECK_Q_RESULT;
96662306a36Sopenharmony_ci					} else
96762306a36Sopenharmony_ci						BUG();
96862306a36Sopenharmony_ci				}
96962306a36Sopenharmony_ci			}
97062306a36Sopenharmony_ci		}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		rv = ioread32(&dma_reg->dsts);
97362306a36Sopenharmony_ci		if (rv) {
97462306a36Sopenharmony_ci			pr_err("DMA%d err status: 0x%x\n",
97562306a36Sopenharmony_ci			       chan->device->id, rv);
97662306a36Sopenharmony_ci			/* write back to clear */
97762306a36Sopenharmony_ci			iowrite32(rv, &dma_reg->dsts);
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
98162306a36Sopenharmony_ci		/* reset status bits to ack */
98262306a36Sopenharmony_ci		xor_reg = chan->device->xor_reg;
98362306a36Sopenharmony_ci		rv = ioread32be(&xor_reg->sr);
98462306a36Sopenharmony_ci		iowrite32be(rv, &xor_reg->sr);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		if (rv & (XOR_IE_ICBIE_BIT|XOR_IE_ICIE_BIT|XOR_IE_RPTIE_BIT)) {
98762306a36Sopenharmony_ci			if (rv & XOR_IE_RPTIE_BIT) {
98862306a36Sopenharmony_ci				/* Read PLB Timeout Error.
98962306a36Sopenharmony_ci				 * Try to resubmit the CB
99062306a36Sopenharmony_ci				 */
99162306a36Sopenharmony_ci				u32 val = ioread32be(&xor_reg->ccbalr);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci				iowrite32be(val, &xor_reg->cblalr);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci				val = ioread32be(&xor_reg->crsr);
99662306a36Sopenharmony_ci				iowrite32be(val | XOR_CRSR_XAE_BIT,
99762306a36Sopenharmony_ci					    &xor_reg->crsr);
99862306a36Sopenharmony_ci			} else
99962306a36Sopenharmony_ci				pr_err("XOR ERR 0x%x status\n", rv);
100062306a36Sopenharmony_ci			break;
100162306a36Sopenharmony_ci		}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		/*  if the XORcore is idle, but there are unprocessed CBs
100462306a36Sopenharmony_ci		 * then refetch the s/w chain here
100562306a36Sopenharmony_ci		 */
100662306a36Sopenharmony_ci		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) &&
100762306a36Sopenharmony_ci		    do_xor_refetch)
100862306a36Sopenharmony_ci			ppc440spe_chan_append(chan);
100962306a36Sopenharmony_ci		break;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/**
101462306a36Sopenharmony_ci * ppc440spe_chan_is_busy - get the channel status
101562306a36Sopenharmony_ci */
101662306a36Sopenharmony_cistatic int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct dma_regs *dma_reg;
101962306a36Sopenharmony_ci	struct xor_regs *xor_reg;
102062306a36Sopenharmony_ci	int busy = 0;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	switch (chan->device->id) {
102362306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
102462306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
102562306a36Sopenharmony_ci		dma_reg = chan->device->dma_reg;
102662306a36Sopenharmony_ci		/*  if command FIFO's head and tail pointers are equal and
102762306a36Sopenharmony_ci		 * status tail is the same as command, then channel is free
102862306a36Sopenharmony_ci		 */
102962306a36Sopenharmony_ci		if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) ||
103062306a36Sopenharmony_ci		    ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp))
103162306a36Sopenharmony_ci			busy = 1;
103262306a36Sopenharmony_ci		break;
103362306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
103462306a36Sopenharmony_ci		/* use the special status bit for the XORcore
103562306a36Sopenharmony_ci		 */
103662306a36Sopenharmony_ci		xor_reg = chan->device->xor_reg;
103762306a36Sopenharmony_ci		busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0;
103862306a36Sopenharmony_ci		break;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	return busy;
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci/**
104562306a36Sopenharmony_ci * ppc440spe_chan_set_first_xor_descriptor -  init XORcore chain
104662306a36Sopenharmony_ci */
104762306a36Sopenharmony_cistatic void ppc440spe_chan_set_first_xor_descriptor(
104862306a36Sopenharmony_ci				struct ppc440spe_adma_chan *chan,
104962306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot *next_desc)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	struct xor_regs *xor_reg = chan->device->xor_reg;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)
105462306a36Sopenharmony_ci		printk(KERN_INFO "%s: Warn: XORcore is running "
105562306a36Sopenharmony_ci			"when try to set the first CDB!\n",
105662306a36Sopenharmony_ci			__func__);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	xor_last_submit = xor_last_linked = next_desc;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	iowrite32be(next_desc->phys, &xor_reg->cblalr);
106362306a36Sopenharmony_ci	iowrite32be(0, &xor_reg->cblahr);
106462306a36Sopenharmony_ci	iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT,
106562306a36Sopenharmony_ci		    &xor_reg->cbcr);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	chan->hw_chain_inited = 1;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci/**
107162306a36Sopenharmony_ci * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO.
107262306a36Sopenharmony_ci * called with irqs disabled
107362306a36Sopenharmony_ci */
107462306a36Sopenharmony_cistatic void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
107562306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	u32 pcdb;
107862306a36Sopenharmony_ci	struct dma_regs *dma_reg = chan->device->dma_reg;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	pcdb = desc->phys;
108162306a36Sopenharmony_ci	if (!test_bit(PPC440SPE_DESC_INT, &desc->flags))
108262306a36Sopenharmony_ci		pcdb |= DMA_CDB_NO_INT;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	chan_last_sub[chan->device->id] = desc;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ADMA_LL_DBG(print_cb(chan, desc->hw_desc));
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	iowrite32(pcdb, &dma_reg->cpfpl);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci/**
109262306a36Sopenharmony_ci * ppc440spe_chan_append - update the h/w chain in the channel
109362306a36Sopenharmony_ci */
109462306a36Sopenharmony_cistatic void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	struct xor_regs *xor_reg;
109762306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter;
109862306a36Sopenharmony_ci	struct xor_cb *xcb;
109962306a36Sopenharmony_ci	u32 cur_desc;
110062306a36Sopenharmony_ci	unsigned long flags;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	local_irq_save(flags);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	switch (chan->device->id) {
110562306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
110662306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
110762306a36Sopenharmony_ci		cur_desc = ppc440spe_chan_get_current_descriptor(chan);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		if (likely(cur_desc)) {
111062306a36Sopenharmony_ci			iter = chan_last_sub[chan->device->id];
111162306a36Sopenharmony_ci			BUG_ON(!iter);
111262306a36Sopenharmony_ci		} else {
111362306a36Sopenharmony_ci			/* first peer */
111462306a36Sopenharmony_ci			iter = chan_first_cdb[chan->device->id];
111562306a36Sopenharmony_ci			BUG_ON(!iter);
111662306a36Sopenharmony_ci			ppc440spe_dma_put_desc(chan, iter);
111762306a36Sopenharmony_ci			chan->hw_chain_inited = 1;
111862306a36Sopenharmony_ci		}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		/* is there something new to append */
112162306a36Sopenharmony_ci		if (!iter->hw_next)
112262306a36Sopenharmony_ci			break;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		/* flush descriptors from the s/w queue to fifo */
112562306a36Sopenharmony_ci		list_for_each_entry_continue(iter, &chan->chain, chain_node) {
112662306a36Sopenharmony_ci			ppc440spe_dma_put_desc(chan, iter);
112762306a36Sopenharmony_ci			if (!iter->hw_next)
112862306a36Sopenharmony_ci				break;
112962306a36Sopenharmony_ci		}
113062306a36Sopenharmony_ci		break;
113162306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
113262306a36Sopenharmony_ci		/* update h/w links and refetch */
113362306a36Sopenharmony_ci		if (!xor_last_submit->hw_next)
113462306a36Sopenharmony_ci			break;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci		xor_reg = chan->device->xor_reg;
113762306a36Sopenharmony_ci		/* the last linked CDB has to generate an interrupt
113862306a36Sopenharmony_ci		 * that we'd be able to append the next lists to h/w
113962306a36Sopenharmony_ci		 * regardless of the XOR engine state at the moment of
114062306a36Sopenharmony_ci		 * appending of these next lists
114162306a36Sopenharmony_ci		 */
114262306a36Sopenharmony_ci		xcb = xor_last_linked->hw_desc;
114362306a36Sopenharmony_ci		xcb->cbc |= XOR_CBCR_CBCE_BIT;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) {
114662306a36Sopenharmony_ci			/* XORcore is idle. Refetch now */
114762306a36Sopenharmony_ci			do_xor_refetch = 0;
114862306a36Sopenharmony_ci			ppc440spe_xor_set_link(xor_last_submit,
114962306a36Sopenharmony_ci				xor_last_submit->hw_next);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci			ADMA_LL_DBG(print_cb_list(chan,
115262306a36Sopenharmony_ci				xor_last_submit->hw_next));
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci			xor_last_submit = xor_last_linked;
115562306a36Sopenharmony_ci			iowrite32be(ioread32be(&xor_reg->crsr) |
115662306a36Sopenharmony_ci				    XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT,
115762306a36Sopenharmony_ci				    &xor_reg->crsr);
115862306a36Sopenharmony_ci		} else {
115962306a36Sopenharmony_ci			/* XORcore is running. Refetch later in the handler */
116062306a36Sopenharmony_ci			do_xor_refetch = 1;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci		break;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	local_irq_restore(flags);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci/**
117062306a36Sopenharmony_ci * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor
117162306a36Sopenharmony_ci */
117262306a36Sopenharmony_cistatic u32
117362306a36Sopenharmony_cippc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct dma_regs *dma_reg;
117662306a36Sopenharmony_ci	struct xor_regs *xor_reg;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (unlikely(!chan->hw_chain_inited))
117962306a36Sopenharmony_ci		/* h/w descriptor chain is not initialized yet */
118062306a36Sopenharmony_ci		return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	switch (chan->device->id) {
118362306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
118462306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
118562306a36Sopenharmony_ci		dma_reg = chan->device->dma_reg;
118662306a36Sopenharmony_ci		return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK);
118762306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
118862306a36Sopenharmony_ci		xor_reg = chan->device->xor_reg;
118962306a36Sopenharmony_ci		return ioread32be(&xor_reg->ccbalr);
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci	return 0;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci/**
119562306a36Sopenharmony_ci * ppc440spe_chan_run - enable the channel
119662306a36Sopenharmony_ci */
119762306a36Sopenharmony_cistatic void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct xor_regs *xor_reg;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	switch (chan->device->id) {
120262306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
120362306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
120462306a36Sopenharmony_ci		/* DMAs are always enabled, do nothing */
120562306a36Sopenharmony_ci		break;
120662306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
120762306a36Sopenharmony_ci		/* drain write buffer */
120862306a36Sopenharmony_ci		xor_reg = chan->device->xor_reg;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		/* fetch descriptor pointed to in <link> */
121162306a36Sopenharmony_ci		iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT,
121262306a36Sopenharmony_ci			    &xor_reg->crsr);
121362306a36Sopenharmony_ci		break;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci/******************************************************************************
121862306a36Sopenharmony_ci * ADMA device level
121962306a36Sopenharmony_ci ******************************************************************************/
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan);
122262306a36Sopenharmony_cistatic int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic dma_cookie_t
122562306a36Sopenharmony_cippc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *tx,
122862306a36Sopenharmony_ci				    dma_addr_t addr, int index);
122962306a36Sopenharmony_cistatic void
123062306a36Sopenharmony_cippc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot *tx,
123162306a36Sopenharmony_ci				  dma_addr_t addr, int index);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic void
123462306a36Sopenharmony_cippc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *tx,
123562306a36Sopenharmony_ci			   dma_addr_t *paddr, unsigned long flags);
123662306a36Sopenharmony_cistatic void
123762306a36Sopenharmony_cippc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *tx,
123862306a36Sopenharmony_ci			  dma_addr_t addr, int index);
123962306a36Sopenharmony_cistatic void
124062306a36Sopenharmony_cippc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot *tx,
124162306a36Sopenharmony_ci			       unsigned char mult, int index, int dst_pos);
124262306a36Sopenharmony_cistatic void
124362306a36Sopenharmony_cippc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot *tx,
124462306a36Sopenharmony_ci				   dma_addr_t paddr, dma_addr_t qaddr);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic struct page *ppc440spe_rxor_srcs[32];
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci/**
124962306a36Sopenharmony_ci * ppc440spe_can_rxor - check if the operands may be processed with RXOR
125062306a36Sopenharmony_ci */
125162306a36Sopenharmony_cistatic int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	int i, order = 0, state = 0;
125462306a36Sopenharmony_ci	int idx = 0;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (unlikely(!(src_cnt > 1)))
125762306a36Sopenharmony_ci		return 0;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs));
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/* Skip holes in the source list before checking */
126262306a36Sopenharmony_ci	for (i = 0; i < src_cnt; i++) {
126362306a36Sopenharmony_ci		if (!srcs[i])
126462306a36Sopenharmony_ci			continue;
126562306a36Sopenharmony_ci		ppc440spe_rxor_srcs[idx++] = srcs[i];
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci	src_cnt = idx;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	for (i = 1; i < src_cnt; i++) {
127062306a36Sopenharmony_ci		char *cur_addr = page_address(ppc440spe_rxor_srcs[i]);
127162306a36Sopenharmony_ci		char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		switch (state) {
127462306a36Sopenharmony_ci		case 0:
127562306a36Sopenharmony_ci			if (cur_addr == old_addr + len) {
127662306a36Sopenharmony_ci				/* direct RXOR */
127762306a36Sopenharmony_ci				order = 1;
127862306a36Sopenharmony_ci				state = 1;
127962306a36Sopenharmony_ci			} else if (old_addr == cur_addr + len) {
128062306a36Sopenharmony_ci				/* reverse RXOR */
128162306a36Sopenharmony_ci				order = -1;
128262306a36Sopenharmony_ci				state = 1;
128362306a36Sopenharmony_ci			} else
128462306a36Sopenharmony_ci				goto out;
128562306a36Sopenharmony_ci			break;
128662306a36Sopenharmony_ci		case 1:
128762306a36Sopenharmony_ci			if ((i == src_cnt - 2) ||
128862306a36Sopenharmony_ci			    (order == -1 && cur_addr != old_addr - len)) {
128962306a36Sopenharmony_ci				order = 0;
129062306a36Sopenharmony_ci				state = 0;
129162306a36Sopenharmony_ci			} else if ((cur_addr == old_addr + len * order) ||
129262306a36Sopenharmony_ci				   (cur_addr == old_addr + 2 * len) ||
129362306a36Sopenharmony_ci				   (cur_addr == old_addr + 3 * len)) {
129462306a36Sopenharmony_ci				state = 2;
129562306a36Sopenharmony_ci			} else {
129662306a36Sopenharmony_ci				order = 0;
129762306a36Sopenharmony_ci				state = 0;
129862306a36Sopenharmony_ci			}
129962306a36Sopenharmony_ci			break;
130062306a36Sopenharmony_ci		case 2:
130162306a36Sopenharmony_ci			order = 0;
130262306a36Sopenharmony_ci			state = 0;
130362306a36Sopenharmony_ci			break;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ciout:
130862306a36Sopenharmony_ci	if (state == 1 || state == 2)
130962306a36Sopenharmony_ci		return 1;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	return 0;
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci/**
131562306a36Sopenharmony_ci * ppc440spe_adma_device_estimate - estimate the efficiency of processing
131662306a36Sopenharmony_ci *	the operation given on this channel. It's assumed that 'chan' is
131762306a36Sopenharmony_ci *	capable to process 'cap' type of operation.
131862306a36Sopenharmony_ci * @chan: channel to use
131962306a36Sopenharmony_ci * @cap: type of transaction
132062306a36Sopenharmony_ci * @dst_lst: array of destination pointers
132162306a36Sopenharmony_ci * @dst_cnt: number of destination operands
132262306a36Sopenharmony_ci * @src_lst: array of source pointers
132362306a36Sopenharmony_ci * @src_cnt: number of source operands
132462306a36Sopenharmony_ci * @src_sz: size of each source operand
132562306a36Sopenharmony_ci */
132662306a36Sopenharmony_cistatic int ppc440spe_adma_estimate(struct dma_chan *chan,
132762306a36Sopenharmony_ci	enum dma_transaction_type cap, struct page **dst_lst, int dst_cnt,
132862306a36Sopenharmony_ci	struct page **src_lst, int src_cnt, size_t src_sz)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	int ef = 1;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (cap == DMA_PQ || cap == DMA_PQ_VAL) {
133362306a36Sopenharmony_ci		/* If RAID-6 capabilities were not activated don't try
133462306a36Sopenharmony_ci		 * to use them
133562306a36Sopenharmony_ci		 */
133662306a36Sopenharmony_ci		if (unlikely(!ppc440spe_r6_enabled))
133762306a36Sopenharmony_ci			return -1;
133862306a36Sopenharmony_ci	}
133962306a36Sopenharmony_ci	/*  In the current implementation of ppc440spe ADMA driver it
134062306a36Sopenharmony_ci	 * makes sense to pick out only pq case, because it may be
134162306a36Sopenharmony_ci	 * processed:
134262306a36Sopenharmony_ci	 * (1) either using Biskup method on DMA2;
134362306a36Sopenharmony_ci	 * (2) or on DMA0/1.
134462306a36Sopenharmony_ci	 *  Thus we give a favour to (1) if the sources are suitable;
134562306a36Sopenharmony_ci	 * else let it be processed on one of the DMA0/1 engines.
134662306a36Sopenharmony_ci	 *  In the sum_product case where destination is also the
134762306a36Sopenharmony_ci	 * source process it on DMA0/1 only.
134862306a36Sopenharmony_ci	 */
134962306a36Sopenharmony_ci	if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) {
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci		if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1])
135262306a36Sopenharmony_ci			ef = 0; /* sum_product case, process on DMA0/1 */
135362306a36Sopenharmony_ci		else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz))
135462306a36Sopenharmony_ci			ef = 3; /* override (DMA0/1 + idle) */
135562306a36Sopenharmony_ci		else
135662306a36Sopenharmony_ci			ef = 0; /* can't process on DMA2 if !rxor */
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	/* channel idleness increases the priority */
136062306a36Sopenharmony_ci	if (likely(ef) &&
136162306a36Sopenharmony_ci	    !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan)))
136262306a36Sopenharmony_ci		ef++;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return ef;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistruct dma_chan *
136862306a36Sopenharmony_cippc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,
136962306a36Sopenharmony_ci	struct page **dst_lst, int dst_cnt, struct page **src_lst,
137062306a36Sopenharmony_ci	int src_cnt, size_t src_sz)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	struct dma_chan *best_chan = NULL;
137362306a36Sopenharmony_ci	struct ppc_dma_chan_ref *ref;
137462306a36Sopenharmony_ci	int best_rank = -1;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (unlikely(!src_sz))
137762306a36Sopenharmony_ci		return NULL;
137862306a36Sopenharmony_ci	if (src_sz > PAGE_SIZE) {
137962306a36Sopenharmony_ci		/*
138062306a36Sopenharmony_ci		 * should a user of the api ever pass > PAGE_SIZE requests
138162306a36Sopenharmony_ci		 * we sort out cases where temporary page-sized buffers
138262306a36Sopenharmony_ci		 * are used.
138362306a36Sopenharmony_ci		 */
138462306a36Sopenharmony_ci		switch (cap) {
138562306a36Sopenharmony_ci		case DMA_PQ:
138662306a36Sopenharmony_ci			if (src_cnt == 1 && dst_lst[1] == src_lst[0])
138762306a36Sopenharmony_ci				return NULL;
138862306a36Sopenharmony_ci			if (src_cnt == 2 && dst_lst[1] == src_lst[1])
138962306a36Sopenharmony_ci				return NULL;
139062306a36Sopenharmony_ci			break;
139162306a36Sopenharmony_ci		case DMA_PQ_VAL:
139262306a36Sopenharmony_ci		case DMA_XOR_VAL:
139362306a36Sopenharmony_ci			return NULL;
139462306a36Sopenharmony_ci		default:
139562306a36Sopenharmony_ci			break;
139662306a36Sopenharmony_ci		}
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) {
140062306a36Sopenharmony_ci		if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
140162306a36Sopenharmony_ci			int rank;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci			rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst,
140462306a36Sopenharmony_ci					dst_cnt, src_lst, src_cnt, src_sz);
140562306a36Sopenharmony_ci			if (rank > best_rank) {
140662306a36Sopenharmony_ci				best_rank = rank;
140762306a36Sopenharmony_ci				best_chan = ref->chan;
140862306a36Sopenharmony_ci			}
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	return best_chan;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci/**
141762306a36Sopenharmony_ci * ppc440spe_get_group_entry - get group entry with index idx
141862306a36Sopenharmony_ci * @tdesc: is the last allocated slot in the group.
141962306a36Sopenharmony_ci */
142062306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *
142162306a36Sopenharmony_cippc440spe_get_group_entry(struct ppc440spe_adma_desc_slot *tdesc, u32 entry_idx)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
142462306a36Sopenharmony_ci	int i = 0;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
142762306a36Sopenharmony_ci		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
142862306a36Sopenharmony_ci			__func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
142962306a36Sopenharmony_ci		BUG();
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
143362306a36Sopenharmony_ci		if (i++ == entry_idx)
143462306a36Sopenharmony_ci			break;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci	return iter;
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci/**
144062306a36Sopenharmony_ci * ppc440spe_adma_free_slots - flags descriptor slots for reuse
144162306a36Sopenharmony_ci * @slot: Slot to free
144262306a36Sopenharmony_ci * Caller must hold &ppc440spe_chan->lock while calling this function
144362306a36Sopenharmony_ci */
144462306a36Sopenharmony_cistatic void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
144562306a36Sopenharmony_ci				      struct ppc440spe_adma_chan *chan)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	int stride = slot->slots_per_op;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	while (stride--) {
145062306a36Sopenharmony_ci		slot->slots_per_op = 0;
145162306a36Sopenharmony_ci		slot = list_entry(slot->slot_node.next,
145262306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot,
145362306a36Sopenharmony_ci				slot_node);
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci/**
145862306a36Sopenharmony_ci * ppc440spe_adma_run_tx_complete_actions - call functions to be called
145962306a36Sopenharmony_ci * upon completion
146062306a36Sopenharmony_ci */
146162306a36Sopenharmony_cistatic dma_cookie_t ppc440spe_adma_run_tx_complete_actions(
146262306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
146362306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan,
146462306a36Sopenharmony_ci		dma_cookie_t cookie)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	BUG_ON(desc->async_tx.cookie < 0);
146762306a36Sopenharmony_ci	if (desc->async_tx.cookie > 0) {
146862306a36Sopenharmony_ci		cookie = desc->async_tx.cookie;
146962306a36Sopenharmony_ci		desc->async_tx.cookie = 0;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		dma_descriptor_unmap(&desc->async_tx);
147262306a36Sopenharmony_ci		/* call the callback (must not sleep or submit new
147362306a36Sopenharmony_ci		 * operations to this channel)
147462306a36Sopenharmony_ci		 */
147562306a36Sopenharmony_ci		dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* run dependent operations */
147962306a36Sopenharmony_ci	dma_run_dependencies(&desc->async_tx);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	return cookie;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci/**
148562306a36Sopenharmony_ci * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set)
148662306a36Sopenharmony_ci */
148762306a36Sopenharmony_cistatic int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc,
148862306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	/* the client is allowed to attach dependent operations
149162306a36Sopenharmony_ci	 * until 'ack' is set
149262306a36Sopenharmony_ci	 */
149362306a36Sopenharmony_ci	if (!async_tx_test_ack(&desc->async_tx))
149462306a36Sopenharmony_ci		return 0;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* leave the last descriptor in the chain
149762306a36Sopenharmony_ci	 * so we can append to it
149862306a36Sopenharmony_ci	 */
149962306a36Sopenharmony_ci	if (list_is_last(&desc->chain_node, &chan->chain) ||
150062306a36Sopenharmony_ci	    desc->phys == ppc440spe_chan_get_current_descriptor(chan))
150162306a36Sopenharmony_ci		return 1;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (chan->device->id != PPC440SPE_XOR_ID) {
150462306a36Sopenharmony_ci		/* our DMA interrupt handler clears opc field of
150562306a36Sopenharmony_ci		 * each processed descriptor. For all types of
150662306a36Sopenharmony_ci		 * operations except for ZeroSum we do not actually
150762306a36Sopenharmony_ci		 * need ack from the interrupt handler. ZeroSum is a
150862306a36Sopenharmony_ci		 * special case since the result of this operation
150962306a36Sopenharmony_ci		 * is available from the handler only, so if we see
151062306a36Sopenharmony_ci		 * such type of descriptor (which is unprocessed yet)
151162306a36Sopenharmony_ci		 * then leave it in chain.
151262306a36Sopenharmony_ci		 */
151362306a36Sopenharmony_ci		struct dma_cdb *cdb = desc->hw_desc;
151462306a36Sopenharmony_ci		if (cdb->opc == DMA_CDB_OPC_DCHECK128)
151562306a36Sopenharmony_ci			return 1;
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n",
151962306a36Sopenharmony_ci		desc->phys, desc->idx, desc->slots_per_op);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	list_del(&desc->chain_node);
152262306a36Sopenharmony_ci	ppc440spe_adma_free_slots(desc, chan);
152362306a36Sopenharmony_ci	return 0;
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci/**
152762306a36Sopenharmony_ci * __ppc440spe_adma_slot_cleanup - this is the common clean-up routine
152862306a36Sopenharmony_ci *	which runs through the channel CDBs list until reach the descriptor
152962306a36Sopenharmony_ci *	currently processed. When routine determines that all CDBs of group
153062306a36Sopenharmony_ci *	are completed then corresponding callbacks (if any) are called and slots
153162306a36Sopenharmony_ci *	are freed.
153262306a36Sopenharmony_ci */
153362306a36Sopenharmony_cistatic void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter, *_iter, *group_start = NULL;
153662306a36Sopenharmony_ci	dma_cookie_t cookie = 0;
153762306a36Sopenharmony_ci	u32 current_desc = ppc440spe_chan_get_current_descriptor(chan);
153862306a36Sopenharmony_ci	int busy = ppc440spe_chan_is_busy(chan);
153962306a36Sopenharmony_ci	int seen_current = 0, slot_cnt = 0, slots_per_op = 0;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev, "ppc440spe adma%d: %s\n",
154262306a36Sopenharmony_ci		chan->device->id, __func__);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (!current_desc) {
154562306a36Sopenharmony_ci		/*  There were no transactions yet, so
154662306a36Sopenharmony_ci		 * nothing to clean
154762306a36Sopenharmony_ci		 */
154862306a36Sopenharmony_ci		return;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	/* free completed slots from the chain starting with
155262306a36Sopenharmony_ci	 * the oldest descriptor
155362306a36Sopenharmony_ci	 */
155462306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &chan->chain,
155562306a36Sopenharmony_ci					chain_node) {
155662306a36Sopenharmony_ci		dev_dbg(chan->device->common.dev, "\tcookie: %d slot: %d "
155762306a36Sopenharmony_ci		    "busy: %d this_desc: %#llx next_desc: %#x "
155862306a36Sopenharmony_ci		    "cur: %#x ack: %d\n",
155962306a36Sopenharmony_ci		    iter->async_tx.cookie, iter->idx, busy, iter->phys,
156062306a36Sopenharmony_ci		    ppc440spe_desc_get_link(iter, chan), current_desc,
156162306a36Sopenharmony_ci		    async_tx_test_ack(&iter->async_tx));
156262306a36Sopenharmony_ci		prefetch(_iter);
156362306a36Sopenharmony_ci		prefetch(&_iter->async_tx);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		/* do not advance past the current descriptor loaded into the
156662306a36Sopenharmony_ci		 * hardware channel,subsequent descriptors are either in process
156762306a36Sopenharmony_ci		 * or have not been submitted
156862306a36Sopenharmony_ci		 */
156962306a36Sopenharmony_ci		if (seen_current)
157062306a36Sopenharmony_ci			break;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		/* stop the search if we reach the current descriptor and the
157362306a36Sopenharmony_ci		 * channel is busy, or if it appears that the current descriptor
157462306a36Sopenharmony_ci		 * needs to be re-read (i.e. has been appended to)
157562306a36Sopenharmony_ci		 */
157662306a36Sopenharmony_ci		if (iter->phys == current_desc) {
157762306a36Sopenharmony_ci			BUG_ON(seen_current++);
157862306a36Sopenharmony_ci			if (busy || ppc440spe_desc_get_link(iter, chan)) {
157962306a36Sopenharmony_ci				/* not all descriptors of the group have
158062306a36Sopenharmony_ci				 * been completed; exit.
158162306a36Sopenharmony_ci				 */
158262306a36Sopenharmony_ci				break;
158362306a36Sopenharmony_ci			}
158462306a36Sopenharmony_ci		}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		/* detect the start of a group transaction */
158762306a36Sopenharmony_ci		if (!slot_cnt && !slots_per_op) {
158862306a36Sopenharmony_ci			slot_cnt = iter->slot_cnt;
158962306a36Sopenharmony_ci			slots_per_op = iter->slots_per_op;
159062306a36Sopenharmony_ci			if (slot_cnt <= slots_per_op) {
159162306a36Sopenharmony_ci				slot_cnt = 0;
159262306a36Sopenharmony_ci				slots_per_op = 0;
159362306a36Sopenharmony_ci			}
159462306a36Sopenharmony_ci		}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci		if (slot_cnt) {
159762306a36Sopenharmony_ci			if (!group_start)
159862306a36Sopenharmony_ci				group_start = iter;
159962306a36Sopenharmony_ci			slot_cnt -= slots_per_op;
160062306a36Sopenharmony_ci		}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		/* all the members of a group are complete */
160362306a36Sopenharmony_ci		if (slots_per_op != 0 && slot_cnt == 0) {
160462306a36Sopenharmony_ci			struct ppc440spe_adma_desc_slot *grp_iter, *_grp_iter;
160562306a36Sopenharmony_ci			int end_of_chain = 0;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci			/* clean up the group */
160862306a36Sopenharmony_ci			slot_cnt = group_start->slot_cnt;
160962306a36Sopenharmony_ci			grp_iter = group_start;
161062306a36Sopenharmony_ci			list_for_each_entry_safe_from(grp_iter, _grp_iter,
161162306a36Sopenharmony_ci				&chan->chain, chain_node) {
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci				cookie = ppc440spe_adma_run_tx_complete_actions(
161462306a36Sopenharmony_ci					grp_iter, chan, cookie);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci				slot_cnt -= slots_per_op;
161762306a36Sopenharmony_ci				end_of_chain = ppc440spe_adma_clean_slot(
161862306a36Sopenharmony_ci				    grp_iter, chan);
161962306a36Sopenharmony_ci				if (end_of_chain && slot_cnt) {
162062306a36Sopenharmony_ci					/* Should wait for ZeroSum completion */
162162306a36Sopenharmony_ci					if (cookie > 0)
162262306a36Sopenharmony_ci						chan->common.completed_cookie = cookie;
162362306a36Sopenharmony_ci					return;
162462306a36Sopenharmony_ci				}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci				if (slot_cnt == 0 || end_of_chain)
162762306a36Sopenharmony_ci					break;
162862306a36Sopenharmony_ci			}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci			/* the group should be complete at this point */
163162306a36Sopenharmony_ci			BUG_ON(slot_cnt);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci			slots_per_op = 0;
163462306a36Sopenharmony_ci			group_start = NULL;
163562306a36Sopenharmony_ci			if (end_of_chain)
163662306a36Sopenharmony_ci				break;
163762306a36Sopenharmony_ci			else
163862306a36Sopenharmony_ci				continue;
163962306a36Sopenharmony_ci		} else if (slots_per_op) /* wait for group completion */
164062306a36Sopenharmony_ci			continue;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		cookie = ppc440spe_adma_run_tx_complete_actions(iter, chan,
164362306a36Sopenharmony_ci		    cookie);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci		if (ppc440spe_adma_clean_slot(iter, chan))
164662306a36Sopenharmony_ci			break;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	BUG_ON(!seen_current);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (cookie > 0) {
165262306a36Sopenharmony_ci		chan->common.completed_cookie = cookie;
165362306a36Sopenharmony_ci		pr_debug("\tcompleted cookie %d\n", cookie);
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci/**
165962306a36Sopenharmony_ci * ppc440spe_adma_tasklet - clean up watch-dog initiator
166062306a36Sopenharmony_ci */
166162306a36Sopenharmony_cistatic void ppc440spe_adma_tasklet(struct tasklet_struct *t)
166262306a36Sopenharmony_ci{
166362306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan = from_tasklet(chan, t, irq_tasklet);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	spin_lock_nested(&chan->lock, SINGLE_DEPTH_NESTING);
166662306a36Sopenharmony_ci	__ppc440spe_adma_slot_cleanup(chan);
166762306a36Sopenharmony_ci	spin_unlock(&chan->lock);
166862306a36Sopenharmony_ci}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci/**
167162306a36Sopenharmony_ci * ppc440spe_adma_slot_cleanup - clean up scheduled initiator
167262306a36Sopenharmony_ci */
167362306a36Sopenharmony_cistatic void ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	spin_lock_bh(&chan->lock);
167662306a36Sopenharmony_ci	__ppc440spe_adma_slot_cleanup(chan);
167762306a36Sopenharmony_ci	spin_unlock_bh(&chan->lock);
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci/**
168162306a36Sopenharmony_ci * ppc440spe_adma_alloc_slots - allocate free slots (if any)
168262306a36Sopenharmony_ci */
168362306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
168462306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan, int num_slots,
168562306a36Sopenharmony_ci		int slots_per_op)
168662306a36Sopenharmony_ci{
168762306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter = NULL, *_iter;
168862306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *alloc_start = NULL;
168962306a36Sopenharmony_ci	int slots_found, retry = 0;
169062306a36Sopenharmony_ci	LIST_HEAD(chain);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	BUG_ON(!num_slots || !slots_per_op);
169462306a36Sopenharmony_ci	/* start search from the last allocated descrtiptor
169562306a36Sopenharmony_ci	 * if a contiguous allocation can not be found start searching
169662306a36Sopenharmony_ci	 * from the beginning of the list
169762306a36Sopenharmony_ci	 */
169862306a36Sopenharmony_ciretry:
169962306a36Sopenharmony_ci	slots_found = 0;
170062306a36Sopenharmony_ci	if (retry == 0)
170162306a36Sopenharmony_ci		iter = chan->last_used;
170262306a36Sopenharmony_ci	else
170362306a36Sopenharmony_ci		iter = list_entry(&chan->all_slots,
170462306a36Sopenharmony_ci				  struct ppc440spe_adma_desc_slot,
170562306a36Sopenharmony_ci				  slot_node);
170662306a36Sopenharmony_ci	list_for_each_entry_safe_continue(iter, _iter, &chan->all_slots,
170762306a36Sopenharmony_ci	    slot_node) {
170862306a36Sopenharmony_ci		prefetch(_iter);
170962306a36Sopenharmony_ci		prefetch(&_iter->async_tx);
171062306a36Sopenharmony_ci		if (iter->slots_per_op) {
171162306a36Sopenharmony_ci			slots_found = 0;
171262306a36Sopenharmony_ci			continue;
171362306a36Sopenharmony_ci		}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		/* start the allocation if the slot is correctly aligned */
171662306a36Sopenharmony_ci		if (!slots_found++)
171762306a36Sopenharmony_ci			alloc_start = iter;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci		if (slots_found == num_slots) {
172062306a36Sopenharmony_ci			struct ppc440spe_adma_desc_slot *alloc_tail = NULL;
172162306a36Sopenharmony_ci			struct ppc440spe_adma_desc_slot *last_used = NULL;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci			iter = alloc_start;
172462306a36Sopenharmony_ci			while (num_slots) {
172562306a36Sopenharmony_ci				int i;
172662306a36Sopenharmony_ci				/* pre-ack all but the last descriptor */
172762306a36Sopenharmony_ci				if (num_slots != slots_per_op)
172862306a36Sopenharmony_ci					async_tx_ack(&iter->async_tx);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci				list_add_tail(&iter->chain_node, &chain);
173162306a36Sopenharmony_ci				alloc_tail = iter;
173262306a36Sopenharmony_ci				iter->async_tx.cookie = 0;
173362306a36Sopenharmony_ci				iter->hw_next = NULL;
173462306a36Sopenharmony_ci				iter->flags = 0;
173562306a36Sopenharmony_ci				iter->slot_cnt = num_slots;
173662306a36Sopenharmony_ci				iter->xor_check_result = NULL;
173762306a36Sopenharmony_ci				for (i = 0; i < slots_per_op; i++) {
173862306a36Sopenharmony_ci					iter->slots_per_op = slots_per_op - i;
173962306a36Sopenharmony_ci					last_used = iter;
174062306a36Sopenharmony_ci					iter = list_entry(iter->slot_node.next,
174162306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
174262306a36Sopenharmony_ci						slot_node);
174362306a36Sopenharmony_ci				}
174462306a36Sopenharmony_ci				num_slots -= slots_per_op;
174562306a36Sopenharmony_ci			}
174662306a36Sopenharmony_ci			alloc_tail->group_head = alloc_start;
174762306a36Sopenharmony_ci			alloc_tail->async_tx.cookie = -EBUSY;
174862306a36Sopenharmony_ci			list_splice(&chain, &alloc_tail->group_list);
174962306a36Sopenharmony_ci			chan->last_used = last_used;
175062306a36Sopenharmony_ci			return alloc_tail;
175162306a36Sopenharmony_ci		}
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci	if (!retry++)
175462306a36Sopenharmony_ci		goto retry;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	/* try to free some slots if the allocation fails */
175762306a36Sopenharmony_ci	tasklet_schedule(&chan->irq_tasklet);
175862306a36Sopenharmony_ci	return NULL;
175962306a36Sopenharmony_ci}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci/**
176262306a36Sopenharmony_ci * ppc440spe_adma_alloc_chan_resources -  allocate pools for CDB slots
176362306a36Sopenharmony_ci */
176462306a36Sopenharmony_cistatic int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
176762306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *slot = NULL;
176862306a36Sopenharmony_ci	char *hw_desc;
176962306a36Sopenharmony_ci	int i, db_sz;
177062306a36Sopenharmony_ci	int init;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
177362306a36Sopenharmony_ci	init = ppc440spe_chan->slots_allocated ? 0 : 1;
177462306a36Sopenharmony_ci	chan->chan_id = ppc440spe_chan->device->id;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	/* Allocate descriptor slots */
177762306a36Sopenharmony_ci	i = ppc440spe_chan->slots_allocated;
177862306a36Sopenharmony_ci	if (ppc440spe_chan->device->id != PPC440SPE_XOR_ID)
177962306a36Sopenharmony_ci		db_sz = sizeof(struct dma_cdb);
178062306a36Sopenharmony_ci	else
178162306a36Sopenharmony_ci		db_sz = sizeof(struct xor_cb);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	for (; i < (ppc440spe_chan->device->pool_size / db_sz); i++) {
178462306a36Sopenharmony_ci		slot = kzalloc(sizeof(struct ppc440spe_adma_desc_slot),
178562306a36Sopenharmony_ci			       GFP_KERNEL);
178662306a36Sopenharmony_ci		if (!slot) {
178762306a36Sopenharmony_ci			printk(KERN_INFO "SPE ADMA Channel only initialized"
178862306a36Sopenharmony_ci				" %d descriptor slots", i--);
178962306a36Sopenharmony_ci			break;
179062306a36Sopenharmony_ci		}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci		hw_desc = (char *) ppc440spe_chan->device->dma_desc_pool_virt;
179362306a36Sopenharmony_ci		slot->hw_desc = (void *) &hw_desc[i * db_sz];
179462306a36Sopenharmony_ci		dma_async_tx_descriptor_init(&slot->async_tx, chan);
179562306a36Sopenharmony_ci		slot->async_tx.tx_submit = ppc440spe_adma_tx_submit;
179662306a36Sopenharmony_ci		INIT_LIST_HEAD(&slot->chain_node);
179762306a36Sopenharmony_ci		INIT_LIST_HEAD(&slot->slot_node);
179862306a36Sopenharmony_ci		INIT_LIST_HEAD(&slot->group_list);
179962306a36Sopenharmony_ci		slot->phys = ppc440spe_chan->device->dma_desc_pool + i * db_sz;
180062306a36Sopenharmony_ci		slot->idx = i;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		spin_lock_bh(&ppc440spe_chan->lock);
180362306a36Sopenharmony_ci		ppc440spe_chan->slots_allocated++;
180462306a36Sopenharmony_ci		list_add_tail(&slot->slot_node, &ppc440spe_chan->all_slots);
180562306a36Sopenharmony_ci		spin_unlock_bh(&ppc440spe_chan->lock);
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	if (i && !ppc440spe_chan->last_used) {
180962306a36Sopenharmony_ci		ppc440spe_chan->last_used =
181062306a36Sopenharmony_ci			list_entry(ppc440spe_chan->all_slots.next,
181162306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot,
181262306a36Sopenharmony_ci				slot_node);
181362306a36Sopenharmony_ci	}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
181662306a36Sopenharmony_ci		"ppc440spe adma%d: allocated %d descriptor slots\n",
181762306a36Sopenharmony_ci		ppc440spe_chan->device->id, i);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	/* initialize the channel and the chain with a null operation */
182062306a36Sopenharmony_ci	if (init) {
182162306a36Sopenharmony_ci		switch (ppc440spe_chan->device->id) {
182262306a36Sopenharmony_ci		case PPC440SPE_DMA0_ID:
182362306a36Sopenharmony_ci		case PPC440SPE_DMA1_ID:
182462306a36Sopenharmony_ci			ppc440spe_chan->hw_chain_inited = 0;
182562306a36Sopenharmony_ci			/* Use WXOR for self-testing */
182662306a36Sopenharmony_ci			if (!ppc440spe_r6_tchan)
182762306a36Sopenharmony_ci				ppc440spe_r6_tchan = ppc440spe_chan;
182862306a36Sopenharmony_ci			break;
182962306a36Sopenharmony_ci		case PPC440SPE_XOR_ID:
183062306a36Sopenharmony_ci			ppc440spe_chan_start_null_xor(ppc440spe_chan);
183162306a36Sopenharmony_ci			break;
183262306a36Sopenharmony_ci		default:
183362306a36Sopenharmony_ci			BUG();
183462306a36Sopenharmony_ci		}
183562306a36Sopenharmony_ci		ppc440spe_chan->needs_unmap = 1;
183662306a36Sopenharmony_ci	}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	return (i > 0) ? i : -ENOMEM;
183962306a36Sopenharmony_ci}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci/**
184262306a36Sopenharmony_ci * ppc440spe_rxor_set_region_data -
184362306a36Sopenharmony_ci */
184462306a36Sopenharmony_cistatic void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
184562306a36Sopenharmony_ci	u8 xor_arg_no, u32 mask)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	struct xor_cb *xcb = desc->hw_desc;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	xcb->ops[xor_arg_no].h |= mask;
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci/**
185362306a36Sopenharmony_ci * ppc440spe_rxor_set_src -
185462306a36Sopenharmony_ci */
185562306a36Sopenharmony_cistatic void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc,
185662306a36Sopenharmony_ci	u8 xor_arg_no, dma_addr_t addr)
185762306a36Sopenharmony_ci{
185862306a36Sopenharmony_ci	struct xor_cb *xcb = desc->hw_desc;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE;
186162306a36Sopenharmony_ci	xcb->ops[xor_arg_no].l = addr;
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci/**
186562306a36Sopenharmony_ci * ppc440spe_rxor_set_mult -
186662306a36Sopenharmony_ci */
186762306a36Sopenharmony_cistatic void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
186862306a36Sopenharmony_ci	u8 xor_arg_no, u8 idx, u8 mult)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct xor_cb *xcb = desc->hw_desc;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8);
187362306a36Sopenharmony_ci}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci/**
187662306a36Sopenharmony_ci * ppc440spe_adma_check_threshold - append CDBs to h/w chain if threshold
187762306a36Sopenharmony_ci *	has been achieved
187862306a36Sopenharmony_ci */
187962306a36Sopenharmony_cistatic void ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan *chan)
188062306a36Sopenharmony_ci{
188162306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev, "ppc440spe adma%d: pending: %d\n",
188262306a36Sopenharmony_ci		chan->device->id, chan->pending);
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	if (chan->pending >= PPC440SPE_ADMA_THRESHOLD) {
188562306a36Sopenharmony_ci		chan->pending = 0;
188662306a36Sopenharmony_ci		ppc440spe_chan_append(chan);
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci/**
189162306a36Sopenharmony_ci * ppc440spe_adma_tx_submit - submit new descriptor group to the channel
189262306a36Sopenharmony_ci *	(it's not necessary that descriptors will be submitted to the h/w
189362306a36Sopenharmony_ci *	chains too right now)
189462306a36Sopenharmony_ci */
189562306a36Sopenharmony_cistatic dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc;
189862306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(tx->chan);
189962306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *group_start, *old_chain_tail;
190062306a36Sopenharmony_ci	int slot_cnt;
190162306a36Sopenharmony_ci	int slots_per_op;
190262306a36Sopenharmony_ci	dma_cookie_t cookie;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	sw_desc = tx_to_ppc440spe_adma_slot(tx);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	group_start = sw_desc->group_head;
190762306a36Sopenharmony_ci	slot_cnt = group_start->slot_cnt;
190862306a36Sopenharmony_ci	slots_per_op = group_start->slots_per_op;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	spin_lock_bh(&chan->lock);
191162306a36Sopenharmony_ci	cookie = dma_cookie_assign(tx);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	if (unlikely(list_empty(&chan->chain))) {
191462306a36Sopenharmony_ci		/* first peer */
191562306a36Sopenharmony_ci		list_splice_init(&sw_desc->group_list, &chan->chain);
191662306a36Sopenharmony_ci		chan_first_cdb[chan->device->id] = group_start;
191762306a36Sopenharmony_ci	} else {
191862306a36Sopenharmony_ci		/* isn't first peer, bind CDBs to chain */
191962306a36Sopenharmony_ci		old_chain_tail = list_entry(chan->chain.prev,
192062306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
192162306a36Sopenharmony_ci					chain_node);
192262306a36Sopenharmony_ci		list_splice_init(&sw_desc->group_list,
192362306a36Sopenharmony_ci		    &old_chain_tail->chain_node);
192462306a36Sopenharmony_ci		/* fix up the hardware chain */
192562306a36Sopenharmony_ci		ppc440spe_desc_set_link(chan, old_chain_tail, group_start);
192662306a36Sopenharmony_ci	}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	/* increment the pending count by the number of operations */
192962306a36Sopenharmony_ci	chan->pending += slot_cnt / slots_per_op;
193062306a36Sopenharmony_ci	ppc440spe_adma_check_threshold(chan);
193162306a36Sopenharmony_ci	spin_unlock_bh(&chan->lock);
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev,
193462306a36Sopenharmony_ci		"ppc440spe adma%d: %s cookie: %d slot: %d tx %p\n",
193562306a36Sopenharmony_ci		chan->device->id, __func__,
193662306a36Sopenharmony_ci		sw_desc->async_tx.cookie, sw_desc->idx, sw_desc);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	return cookie;
193962306a36Sopenharmony_ci}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci/**
194262306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_interrupt - prepare CDB for a pseudo DMA operation
194362306a36Sopenharmony_ci */
194462306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_interrupt(
194562306a36Sopenharmony_ci		struct dma_chan *chan, unsigned long flags)
194662306a36Sopenharmony_ci{
194762306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
194862306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
194962306a36Sopenharmony_ci	int slot_cnt, slots_per_op;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
195462306a36Sopenharmony_ci		"ppc440spe adma%d: %s\n", ppc440spe_chan->device->id,
195562306a36Sopenharmony_ci		__func__);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
195862306a36Sopenharmony_ci	slot_cnt = slots_per_op = 1;
195962306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
196062306a36Sopenharmony_ci			slots_per_op);
196162306a36Sopenharmony_ci	if (sw_desc) {
196262306a36Sopenharmony_ci		group_start = sw_desc->group_head;
196362306a36Sopenharmony_ci		ppc440spe_desc_init_interrupt(group_start, ppc440spe_chan);
196462306a36Sopenharmony_ci		group_start->unmap_len = 0;
196562306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
197062306a36Sopenharmony_ci}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci/**
197362306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_memcpy - prepare CDB for a MEMCPY operation
197462306a36Sopenharmony_ci */
197562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy(
197662306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t dma_dest,
197762306a36Sopenharmony_ci		dma_addr_t dma_src, size_t len, unsigned long flags)
197862306a36Sopenharmony_ci{
197962306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
198062306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
198162306a36Sopenharmony_ci	int slot_cnt, slots_per_op;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	if (unlikely(!len))
198662306a36Sopenharmony_ci		return NULL;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT);
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
199362306a36Sopenharmony_ci		"ppc440spe adma%d: %s len: %u int_en %d\n",
199462306a36Sopenharmony_ci		ppc440spe_chan->device->id, __func__, len,
199562306a36Sopenharmony_ci		flags & DMA_PREP_INTERRUPT ? 1 : 0);
199662306a36Sopenharmony_ci	slot_cnt = slots_per_op = 1;
199762306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
199862306a36Sopenharmony_ci		slots_per_op);
199962306a36Sopenharmony_ci	if (sw_desc) {
200062306a36Sopenharmony_ci		group_start = sw_desc->group_head;
200162306a36Sopenharmony_ci		ppc440spe_desc_init_memcpy(group_start, flags);
200262306a36Sopenharmony_ci		ppc440spe_adma_set_dest(group_start, dma_dest, 0);
200362306a36Sopenharmony_ci		ppc440spe_adma_memcpy_xor_set_src(group_start, dma_src, 0);
200462306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
200562306a36Sopenharmony_ci		sw_desc->unmap_len = len;
200662306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
201162306a36Sopenharmony_ci}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci/**
201462306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_xor - prepare CDB for a XOR operation
201562306a36Sopenharmony_ci */
201662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor(
201762306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t dma_dest,
201862306a36Sopenharmony_ci		dma_addr_t *dma_src, u32 src_cnt, size_t len,
201962306a36Sopenharmony_ci		unsigned long flags)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
202262306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
202362306a36Sopenharmony_ci	int slot_cnt, slots_per_op;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	ADMA_LL_DBG(prep_dma_xor_dbg(ppc440spe_chan->device->id,
202862306a36Sopenharmony_ci				     dma_dest, dma_src, src_cnt));
202962306a36Sopenharmony_ci	if (unlikely(!len))
203062306a36Sopenharmony_ci		return NULL;
203162306a36Sopenharmony_ci	BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
203462306a36Sopenharmony_ci		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
203562306a36Sopenharmony_ci		ppc440spe_chan->device->id, __func__, src_cnt, len,
203662306a36Sopenharmony_ci		flags & DMA_PREP_INTERRUPT ? 1 : 0);
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
203962306a36Sopenharmony_ci	slot_cnt = ppc440spe_chan_xor_slot_count(len, src_cnt, &slots_per_op);
204062306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
204162306a36Sopenharmony_ci			slots_per_op);
204262306a36Sopenharmony_ci	if (sw_desc) {
204362306a36Sopenharmony_ci		group_start = sw_desc->group_head;
204462306a36Sopenharmony_ci		ppc440spe_desc_init_xor(group_start, src_cnt, flags);
204562306a36Sopenharmony_ci		ppc440spe_adma_set_dest(group_start, dma_dest, 0);
204662306a36Sopenharmony_ci		while (src_cnt--)
204762306a36Sopenharmony_ci			ppc440spe_adma_memcpy_xor_set_src(group_start,
204862306a36Sopenharmony_ci				dma_src[src_cnt], src_cnt);
204962306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
205062306a36Sopenharmony_ci		sw_desc->unmap_len = len;
205162306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cistatic inline void
205962306a36Sopenharmony_cippc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot *desc,
206062306a36Sopenharmony_ci				int src_cnt);
206162306a36Sopenharmony_cistatic void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci/**
206462306a36Sopenharmony_ci * ppc440spe_adma_init_dma2rxor_slot -
206562306a36Sopenharmony_ci */
206662306a36Sopenharmony_cistatic void ppc440spe_adma_init_dma2rxor_slot(
206762306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
206862306a36Sopenharmony_ci		dma_addr_t *src, int src_cnt)
206962306a36Sopenharmony_ci{
207062306a36Sopenharmony_ci	int i;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* initialize CDB */
207362306a36Sopenharmony_ci	for (i = 0; i < src_cnt; i++) {
207462306a36Sopenharmony_ci		ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i,
207562306a36Sopenharmony_ci						 desc->src_cnt, (u32)src[i]);
207662306a36Sopenharmony_ci	}
207762306a36Sopenharmony_ci}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci/**
208062306a36Sopenharmony_ci * ppc440spe_dma01_prep_mult -
208162306a36Sopenharmony_ci * for Q operation where destination is also the source
208262306a36Sopenharmony_ci */
208362306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_mult(
208462306a36Sopenharmony_ci		struct ppc440spe_adma_chan *ppc440spe_chan,
208562306a36Sopenharmony_ci		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
208662306a36Sopenharmony_ci		const unsigned char *scf, size_t len, unsigned long flags)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
208962306a36Sopenharmony_ci	unsigned long op = 0;
209062306a36Sopenharmony_ci	int slot_cnt;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	set_bit(PPC440SPE_DESC_WXOR, &op);
209362306a36Sopenharmony_ci	slot_cnt = 2;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	/* use WXOR, each descriptor occupies one slot */
209862306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
209962306a36Sopenharmony_ci	if (sw_desc) {
210062306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan;
210162306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *iter;
210262306a36Sopenharmony_ci		struct dma_cdb *hw_desc;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
210562306a36Sopenharmony_ci		set_bits(op, &sw_desc->flags);
210662306a36Sopenharmony_ci		sw_desc->src_cnt = src_cnt;
210762306a36Sopenharmony_ci		sw_desc->dst_cnt = dst_cnt;
210862306a36Sopenharmony_ci		/* First descriptor, zero data in the destination and copy it
210962306a36Sopenharmony_ci		 * to q page using MULTICAST transfer.
211062306a36Sopenharmony_ci		 */
211162306a36Sopenharmony_ci		iter = list_first_entry(&sw_desc->group_list,
211262306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
211362306a36Sopenharmony_ci					chain_node);
211462306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
211562306a36Sopenharmony_ci		/* set 'next' pointer */
211662306a36Sopenharmony_ci		iter->hw_next = list_entry(iter->chain_node.next,
211762306a36Sopenharmony_ci					   struct ppc440spe_adma_desc_slot,
211862306a36Sopenharmony_ci					   chain_node);
211962306a36Sopenharmony_ci		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
212062306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
212162306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan,
212462306a36Sopenharmony_ci					     DMA_CUED_XOR_BASE, dst[0], 0);
212562306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1);
212662306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
212762306a36Sopenharmony_ci					    src[0]);
212862306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
212962306a36Sopenharmony_ci		iter->unmap_len = len;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci		/*
213262306a36Sopenharmony_ci		 * Second descriptor, multiply data from the q page
213362306a36Sopenharmony_ci		 * and store the result in real destination.
213462306a36Sopenharmony_ci		 */
213562306a36Sopenharmony_ci		iter = list_first_entry(&iter->chain_node,
213662306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
213762306a36Sopenharmony_ci					chain_node);
213862306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
213962306a36Sopenharmony_ci		iter->hw_next = NULL;
214062306a36Sopenharmony_ci		if (flags & DMA_PREP_INTERRUPT)
214162306a36Sopenharmony_ci			set_bit(PPC440SPE_DESC_INT, &iter->flags);
214262306a36Sopenharmony_ci		else
214362306a36Sopenharmony_ci			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
214662306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
214762306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(iter, chan, 0,
214862306a36Sopenharmony_ci					    DMA_CUED_XOR_HB, dst[1]);
214962306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan,
215062306a36Sopenharmony_ci					     DMA_CUED_XOR_BASE, dst[0], 0);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
215362306a36Sopenharmony_ci					    DMA_CDB_SG_DST1, scf[0]);
215462306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
215562306a36Sopenharmony_ci		iter->unmap_len = len;
215662306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	return sw_desc;
216262306a36Sopenharmony_ci}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci/**
216562306a36Sopenharmony_ci * ppc440spe_dma01_prep_sum_product -
216662306a36Sopenharmony_ci * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also
216762306a36Sopenharmony_ci * the source.
216862306a36Sopenharmony_ci */
216962306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_sum_product(
217062306a36Sopenharmony_ci		struct ppc440spe_adma_chan *ppc440spe_chan,
217162306a36Sopenharmony_ci		dma_addr_t *dst, dma_addr_t *src, int src_cnt,
217262306a36Sopenharmony_ci		const unsigned char *scf, size_t len, unsigned long flags)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
217562306a36Sopenharmony_ci	unsigned long op = 0;
217662306a36Sopenharmony_ci	int slot_cnt;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	set_bit(PPC440SPE_DESC_WXOR, &op);
217962306a36Sopenharmony_ci	slot_cnt = 3;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	/* WXOR, each descriptor occupies one slot */
218462306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
218562306a36Sopenharmony_ci	if (sw_desc) {
218662306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan;
218762306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *iter;
218862306a36Sopenharmony_ci		struct dma_cdb *hw_desc;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
219162306a36Sopenharmony_ci		set_bits(op, &sw_desc->flags);
219262306a36Sopenharmony_ci		sw_desc->src_cnt = src_cnt;
219362306a36Sopenharmony_ci		sw_desc->dst_cnt = 1;
219462306a36Sopenharmony_ci		/* 1st descriptor, src[1] data to q page and zero destination */
219562306a36Sopenharmony_ci		iter = list_first_entry(&sw_desc->group_list,
219662306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
219762306a36Sopenharmony_ci					chain_node);
219862306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
219962306a36Sopenharmony_ci		iter->hw_next = list_entry(iter->chain_node.next,
220062306a36Sopenharmony_ci					   struct ppc440spe_adma_desc_slot,
220162306a36Sopenharmony_ci					   chain_node);
220262306a36Sopenharmony_ci		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
220362306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
220462306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
220762306a36Sopenharmony_ci					     *dst, 0);
220862306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan, 0,
220962306a36Sopenharmony_ci					     ppc440spe_chan->qdest, 1);
221062306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
221162306a36Sopenharmony_ci					    src[1]);
221262306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
221362306a36Sopenharmony_ci		iter->unmap_len = len;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		/* 2nd descriptor, multiply src[1] data and store the
221662306a36Sopenharmony_ci		 * result in destination */
221762306a36Sopenharmony_ci		iter = list_first_entry(&iter->chain_node,
221862306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
221962306a36Sopenharmony_ci					chain_node);
222062306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
222162306a36Sopenharmony_ci		/* set 'next' pointer */
222262306a36Sopenharmony_ci		iter->hw_next = list_entry(iter->chain_node.next,
222362306a36Sopenharmony_ci					   struct ppc440spe_adma_desc_slot,
222462306a36Sopenharmony_ci					   chain_node);
222562306a36Sopenharmony_ci		if (flags & DMA_PREP_INTERRUPT)
222662306a36Sopenharmony_ci			set_bit(PPC440SPE_DESC_INT, &iter->flags);
222762306a36Sopenharmony_ci		else
222862306a36Sopenharmony_ci			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
223162306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
223262306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
223362306a36Sopenharmony_ci					    ppc440spe_chan->qdest);
223462306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
223562306a36Sopenharmony_ci					     *dst, 0);
223662306a36Sopenharmony_ci		ppc440spe_desc_set_src_mult(iter, chan,	DMA_CUED_MULT1_OFF,
223762306a36Sopenharmony_ci					    DMA_CDB_SG_DST1, scf[1]);
223862306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
223962306a36Sopenharmony_ci		iter->unmap_len = len;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci		/*
224262306a36Sopenharmony_ci		 * 3rd descriptor, multiply src[0] data and xor it
224362306a36Sopenharmony_ci		 * with destination
224462306a36Sopenharmony_ci		 */
224562306a36Sopenharmony_ci		iter = list_first_entry(&iter->chain_node,
224662306a36Sopenharmony_ci					struct ppc440spe_adma_desc_slot,
224762306a36Sopenharmony_ci					chain_node);
224862306a36Sopenharmony_ci		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
224962306a36Sopenharmony_ci		iter->hw_next = NULL;
225062306a36Sopenharmony_ci		if (flags & DMA_PREP_INTERRUPT)
225162306a36Sopenharmony_ci			set_bit(PPC440SPE_DESC_INT, &iter->flags);
225262306a36Sopenharmony_ci		else
225362306a36Sopenharmony_ci			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci		hw_desc = iter->hw_desc;
225662306a36Sopenharmony_ci		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
225762306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
225862306a36Sopenharmony_ci					    src[0]);
225962306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
226062306a36Sopenharmony_ci					     *dst, 0);
226162306a36Sopenharmony_ci		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
226262306a36Sopenharmony_ci					    DMA_CDB_SG_DST1, scf[0]);
226362306a36Sopenharmony_ci		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
226462306a36Sopenharmony_ci		iter->unmap_len = len;
226562306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
226662306a36Sopenharmony_ci	}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	return sw_desc;
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq(
227462306a36Sopenharmony_ci		struct ppc440spe_adma_chan *ppc440spe_chan,
227562306a36Sopenharmony_ci		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
227662306a36Sopenharmony_ci		const unsigned char *scf, size_t len, unsigned long flags)
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	int slot_cnt;
227962306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
228062306a36Sopenharmony_ci	unsigned long op = 0;
228162306a36Sopenharmony_ci	unsigned char mult = 1;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
228462306a36Sopenharmony_ci		 __func__, dst_cnt, src_cnt, len);
228562306a36Sopenharmony_ci	/*  select operations WXOR/RXOR depending on the
228662306a36Sopenharmony_ci	 * source addresses of operators and the number
228762306a36Sopenharmony_ci	 * of destinations (RXOR support only Q-parity calculations)
228862306a36Sopenharmony_ci	 */
228962306a36Sopenharmony_ci	set_bit(PPC440SPE_DESC_WXOR, &op);
229062306a36Sopenharmony_ci	if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
229162306a36Sopenharmony_ci		/* no active RXOR;
229262306a36Sopenharmony_ci		 * do RXOR if:
229362306a36Sopenharmony_ci		 * - there are more than 1 source,
229462306a36Sopenharmony_ci		 * - len is aligned on 512-byte boundary,
229562306a36Sopenharmony_ci		 * - source addresses fit to one of 4 possible regions.
229662306a36Sopenharmony_ci		 */
229762306a36Sopenharmony_ci		if (src_cnt > 1 &&
229862306a36Sopenharmony_ci		    !(len & MQ0_CF2H_RXOR_BS_MASK) &&
229962306a36Sopenharmony_ci		    (src[0] + len) == src[1]) {
230062306a36Sopenharmony_ci			/* may do RXOR R1 R2 */
230162306a36Sopenharmony_ci			set_bit(PPC440SPE_DESC_RXOR, &op);
230262306a36Sopenharmony_ci			if (src_cnt != 2) {
230362306a36Sopenharmony_ci				/* may try to enhance region of RXOR */
230462306a36Sopenharmony_ci				if ((src[1] + len) == src[2]) {
230562306a36Sopenharmony_ci					/* do RXOR R1 R2 R3 */
230662306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_RXOR123,
230762306a36Sopenharmony_ci						&op);
230862306a36Sopenharmony_ci				} else if ((src[1] + len * 2) == src[2]) {
230962306a36Sopenharmony_ci					/* do RXOR R1 R2 R4 */
231062306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_RXOR124, &op);
231162306a36Sopenharmony_ci				} else if ((src[1] + len * 3) == src[2]) {
231262306a36Sopenharmony_ci					/* do RXOR R1 R2 R5 */
231362306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_RXOR125,
231462306a36Sopenharmony_ci						&op);
231562306a36Sopenharmony_ci				} else {
231662306a36Sopenharmony_ci					/* do RXOR R1 R2 */
231762306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_RXOR12,
231862306a36Sopenharmony_ci						&op);
231962306a36Sopenharmony_ci				}
232062306a36Sopenharmony_ci			} else {
232162306a36Sopenharmony_ci				/* do RXOR R1 R2 */
232262306a36Sopenharmony_ci				set_bit(PPC440SPE_DESC_RXOR12, &op);
232362306a36Sopenharmony_ci			}
232462306a36Sopenharmony_ci		}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci		if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
232762306a36Sopenharmony_ci			/* can not do this operation with RXOR */
232862306a36Sopenharmony_ci			clear_bit(PPC440SPE_RXOR_RUN,
232962306a36Sopenharmony_ci				&ppc440spe_rxor_state);
233062306a36Sopenharmony_ci		} else {
233162306a36Sopenharmony_ci			/* can do; set block size right now */
233262306a36Sopenharmony_ci			ppc440spe_desc_set_rxor_block_size(len);
233362306a36Sopenharmony_ci		}
233462306a36Sopenharmony_ci	}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	/* Number of necessary slots depends on operation type selected */
233762306a36Sopenharmony_ci	if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
233862306a36Sopenharmony_ci		/*  This is a WXOR only chain. Need descriptors for each
233962306a36Sopenharmony_ci		 * source to GF-XOR them with WXOR, and need descriptors
234062306a36Sopenharmony_ci		 * for each destination to zero them with WXOR
234162306a36Sopenharmony_ci		 */
234262306a36Sopenharmony_ci		slot_cnt = src_cnt;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_P) {
234562306a36Sopenharmony_ci			slot_cnt++;
234662306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_P, &op);
234762306a36Sopenharmony_ci		}
234862306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_Q) {
234962306a36Sopenharmony_ci			slot_cnt++;
235062306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_Q, &op);
235162306a36Sopenharmony_ci		}
235262306a36Sopenharmony_ci	} else {
235362306a36Sopenharmony_ci		/*  Need 1/2 descriptor for RXOR operation, and
235462306a36Sopenharmony_ci		 * need (src_cnt - (2 or 3)) for WXOR of sources
235562306a36Sopenharmony_ci		 * remained (if any)
235662306a36Sopenharmony_ci		 */
235762306a36Sopenharmony_ci		slot_cnt = dst_cnt;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_P)
236062306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_P, &op);
236162306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_Q)
236262306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_Q, &op);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci		if (test_bit(PPC440SPE_DESC_RXOR12, &op))
236562306a36Sopenharmony_ci			slot_cnt += src_cnt - 2;
236662306a36Sopenharmony_ci		else
236762306a36Sopenharmony_ci			slot_cnt += src_cnt - 3;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci		/*  Thus we have either RXOR only chain or
237062306a36Sopenharmony_ci		 * mixed RXOR/WXOR
237162306a36Sopenharmony_ci		 */
237262306a36Sopenharmony_ci		if (slot_cnt == dst_cnt)
237362306a36Sopenharmony_ci			/* RXOR only chain */
237462306a36Sopenharmony_ci			clear_bit(PPC440SPE_DESC_WXOR, &op);
237562306a36Sopenharmony_ci	}
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
237862306a36Sopenharmony_ci	/* for both RXOR/WXOR each descriptor occupies one slot */
237962306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
238062306a36Sopenharmony_ci	if (sw_desc) {
238162306a36Sopenharmony_ci		ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt,
238262306a36Sopenharmony_ci				flags, op);
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci		/* setup dst/src/mult */
238562306a36Sopenharmony_ci		pr_debug("%s: set dst descriptor 0, 1: 0x%016llx, 0x%016llx\n",
238662306a36Sopenharmony_ci			 __func__, dst[0], dst[1]);
238762306a36Sopenharmony_ci		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
238862306a36Sopenharmony_ci		while (src_cnt--) {
238962306a36Sopenharmony_ci			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
239062306a36Sopenharmony_ci						  src_cnt);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci			/* NOTE: "Multi = 0 is equivalent to = 1" as it
239362306a36Sopenharmony_ci			 * stated in 440SPSPe_RAID6_Addendum_UM_1_17.pdf
239462306a36Sopenharmony_ci			 * doesn't work for RXOR with DMA0/1! Instead, multi=0
239562306a36Sopenharmony_ci			 * leads to zeroing source data after RXOR.
239662306a36Sopenharmony_ci			 * So, for P case set-up mult=1 explicitly.
239762306a36Sopenharmony_ci			 */
239862306a36Sopenharmony_ci			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
239962306a36Sopenharmony_ci				mult = scf[src_cnt];
240062306a36Sopenharmony_ci			ppc440spe_adma_pq_set_src_mult(sw_desc,
240162306a36Sopenharmony_ci				mult, src_cnt,  dst_cnt - 1);
240262306a36Sopenharmony_ci		}
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci		/* Setup byte count foreach slot just allocated */
240562306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
240662306a36Sopenharmony_ci		list_for_each_entry(iter, &sw_desc->group_list,
240762306a36Sopenharmony_ci				chain_node) {
240862306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter,
240962306a36Sopenharmony_ci				ppc440spe_chan, len);
241062306a36Sopenharmony_ci			iter->unmap_len = len;
241162306a36Sopenharmony_ci		}
241262306a36Sopenharmony_ci	}
241362306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	return sw_desc;
241662306a36Sopenharmony_ci}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma2_prep_pq(
241962306a36Sopenharmony_ci		struct ppc440spe_adma_chan *ppc440spe_chan,
242062306a36Sopenharmony_ci		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
242162306a36Sopenharmony_ci		const unsigned char *scf, size_t len, unsigned long flags)
242262306a36Sopenharmony_ci{
242362306a36Sopenharmony_ci	int slot_cnt, descs_per_op;
242462306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
242562306a36Sopenharmony_ci	unsigned long op = 0;
242662306a36Sopenharmony_ci	unsigned char mult = 1;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	BUG_ON(!dst_cnt);
242962306a36Sopenharmony_ci	/*pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
243062306a36Sopenharmony_ci		 __func__, dst_cnt, src_cnt, len);*/
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
243362306a36Sopenharmony_ci	descs_per_op = ppc440spe_dma2_pq_slot_count(src, src_cnt, len);
243462306a36Sopenharmony_ci	if (descs_per_op < 0) {
243562306a36Sopenharmony_ci		spin_unlock_bh(&ppc440spe_chan->lock);
243662306a36Sopenharmony_ci		return NULL;
243762306a36Sopenharmony_ci	}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	/* depending on number of sources we have 1 or 2 RXOR chains */
244062306a36Sopenharmony_ci	slot_cnt = descs_per_op * dst_cnt;
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
244362306a36Sopenharmony_ci	if (sw_desc) {
244462306a36Sopenharmony_ci		op = slot_cnt;
244562306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
244662306a36Sopenharmony_ci		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
244762306a36Sopenharmony_ci			ppc440spe_desc_init_dma2pq(iter, dst_cnt, src_cnt,
244862306a36Sopenharmony_ci				--op ? 0 : flags);
244962306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
245062306a36Sopenharmony_ci				len);
245162306a36Sopenharmony_ci			iter->unmap_len = len;
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci			ppc440spe_init_rxor_cursor(&(iter->rxor_cursor));
245462306a36Sopenharmony_ci			iter->rxor_cursor.len = len;
245562306a36Sopenharmony_ci			iter->descs_per_op = descs_per_op;
245662306a36Sopenharmony_ci		}
245762306a36Sopenharmony_ci		op = 0;
245862306a36Sopenharmony_ci		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
245962306a36Sopenharmony_ci			op++;
246062306a36Sopenharmony_ci			if (op % descs_per_op == 0)
246162306a36Sopenharmony_ci				ppc440spe_adma_init_dma2rxor_slot(iter, src,
246262306a36Sopenharmony_ci								  src_cnt);
246362306a36Sopenharmony_ci			if (likely(!list_is_last(&iter->chain_node,
246462306a36Sopenharmony_ci						 &sw_desc->group_list))) {
246562306a36Sopenharmony_ci				/* set 'next' pointer */
246662306a36Sopenharmony_ci				iter->hw_next =
246762306a36Sopenharmony_ci					list_entry(iter->chain_node.next,
246862306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
246962306a36Sopenharmony_ci						chain_node);
247062306a36Sopenharmony_ci				ppc440spe_xor_set_link(iter, iter->hw_next);
247162306a36Sopenharmony_ci			} else {
247262306a36Sopenharmony_ci				/* this is the last descriptor. */
247362306a36Sopenharmony_ci				iter->hw_next = NULL;
247462306a36Sopenharmony_ci			}
247562306a36Sopenharmony_ci		}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci		/* fixup head descriptor */
247862306a36Sopenharmony_ci		sw_desc->dst_cnt = dst_cnt;
247962306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_P)
248062306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_P, &sw_desc->flags);
248162306a36Sopenharmony_ci		if (flags & DMA_PREP_ZERO_Q)
248262306a36Sopenharmony_ci			set_bit(PPC440SPE_ZERO_Q, &sw_desc->flags);
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci		/* setup dst/src/mult */
248562306a36Sopenharmony_ci		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci		while (src_cnt--) {
248862306a36Sopenharmony_ci			/* handle descriptors (if dst_cnt == 2) inside
248962306a36Sopenharmony_ci			 * the ppc440spe_adma_pq_set_srcxxx() functions
249062306a36Sopenharmony_ci			 */
249162306a36Sopenharmony_ci			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
249262306a36Sopenharmony_ci						  src_cnt);
249362306a36Sopenharmony_ci			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
249462306a36Sopenharmony_ci				mult = scf[src_cnt];
249562306a36Sopenharmony_ci			ppc440spe_adma_pq_set_src_mult(sw_desc,
249662306a36Sopenharmony_ci					mult, src_cnt, dst_cnt - 1);
249762306a36Sopenharmony_ci		}
249862306a36Sopenharmony_ci	}
249962306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
250062306a36Sopenharmony_ci	ppc440spe_desc_set_rxor_block_size(len);
250162306a36Sopenharmony_ci	return sw_desc;
250262306a36Sopenharmony_ci}
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci/**
250562306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_pq - prepare CDB (group) for a GF-XOR operation
250662306a36Sopenharmony_ci */
250762306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq(
250862306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
250962306a36Sopenharmony_ci		unsigned int src_cnt, const unsigned char *scf,
251062306a36Sopenharmony_ci		size_t len, unsigned long flags)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
251362306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
251462306a36Sopenharmony_ci	int dst_cnt = 0;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id,
251962306a36Sopenharmony_ci				    dst, src, src_cnt));
252062306a36Sopenharmony_ci	BUG_ON(!len);
252162306a36Sopenharmony_ci	BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
252262306a36Sopenharmony_ci	BUG_ON(!src_cnt);
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	if (src_cnt == 1 && dst[1] == src[0]) {
252562306a36Sopenharmony_ci		dma_addr_t dest[2];
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci		/* dst[1] is real destination (Q) */
252862306a36Sopenharmony_ci		dest[0] = dst[1];
252962306a36Sopenharmony_ci		/* this is the page to multicast source data to */
253062306a36Sopenharmony_ci		dest[1] = ppc440spe_chan->qdest;
253162306a36Sopenharmony_ci		sw_desc = ppc440spe_dma01_prep_mult(ppc440spe_chan,
253262306a36Sopenharmony_ci				dest, 2, src, src_cnt, scf, len, flags);
253362306a36Sopenharmony_ci		return sw_desc ? &sw_desc->async_tx : NULL;
253462306a36Sopenharmony_ci	}
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	if (src_cnt == 2 && dst[1] == src[1]) {
253762306a36Sopenharmony_ci		sw_desc = ppc440spe_dma01_prep_sum_product(ppc440spe_chan,
253862306a36Sopenharmony_ci					&dst[1], src, 2, scf, len, flags);
253962306a36Sopenharmony_ci		return sw_desc ? &sw_desc->async_tx : NULL;
254062306a36Sopenharmony_ci	}
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	if (!(flags & DMA_PREP_PQ_DISABLE_P)) {
254362306a36Sopenharmony_ci		BUG_ON(!dst[0]);
254462306a36Sopenharmony_ci		dst_cnt++;
254562306a36Sopenharmony_ci		flags |= DMA_PREP_ZERO_P;
254662306a36Sopenharmony_ci	}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	if (!(flags & DMA_PREP_PQ_DISABLE_Q)) {
254962306a36Sopenharmony_ci		BUG_ON(!dst[1]);
255062306a36Sopenharmony_ci		dst_cnt++;
255162306a36Sopenharmony_ci		flags |= DMA_PREP_ZERO_Q;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	BUG_ON(!dst_cnt);
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
255762306a36Sopenharmony_ci		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
255862306a36Sopenharmony_ci		ppc440spe_chan->device->id, __func__, src_cnt, len,
255962306a36Sopenharmony_ci		flags & DMA_PREP_INTERRUPT ? 1 : 0);
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	switch (ppc440spe_chan->device->id) {
256262306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
256362306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
256462306a36Sopenharmony_ci		sw_desc = ppc440spe_dma01_prep_pq(ppc440spe_chan,
256562306a36Sopenharmony_ci				dst, dst_cnt, src, src_cnt, scf,
256662306a36Sopenharmony_ci				len, flags);
256762306a36Sopenharmony_ci		break;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
257062306a36Sopenharmony_ci		sw_desc = ppc440spe_dma2_prep_pq(ppc440spe_chan,
257162306a36Sopenharmony_ci				dst, dst_cnt, src, src_cnt, scf,
257262306a36Sopenharmony_ci				len, flags);
257362306a36Sopenharmony_ci		break;
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
257762306a36Sopenharmony_ci}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci/**
258062306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_pqzero_sum - prepare CDB group for
258162306a36Sopenharmony_ci * a PQ_ZERO_SUM operation
258262306a36Sopenharmony_ci */
258362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pqzero_sum(
258462306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
258562306a36Sopenharmony_ci		unsigned int src_cnt, const unsigned char *scf, size_t len,
258662306a36Sopenharmony_ci		enum sum_check_flags *pqres, unsigned long flags)
258762306a36Sopenharmony_ci{
258862306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
258962306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
259062306a36Sopenharmony_ci	dma_addr_t pdest, qdest;
259162306a36Sopenharmony_ci	int slot_cnt, slots_per_op, idst, dst_cnt;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	if (flags & DMA_PREP_PQ_DISABLE_P)
259662306a36Sopenharmony_ci		pdest = 0;
259762306a36Sopenharmony_ci	else
259862306a36Sopenharmony_ci		pdest = pq[0];
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	if (flags & DMA_PREP_PQ_DISABLE_Q)
260162306a36Sopenharmony_ci		qdest = 0;
260262306a36Sopenharmony_ci	else
260362306a36Sopenharmony_ci		qdest = pq[1];
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	ADMA_LL_DBG(prep_dma_pqzero_sum_dbg(ppc440spe_chan->device->id,
260662306a36Sopenharmony_ci					    src, src_cnt, scf));
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	/* Always use WXOR for P/Q calculations (two destinations).
260962306a36Sopenharmony_ci	 * Need 1 or 2 extra slots to verify results are zero.
261062306a36Sopenharmony_ci	 */
261162306a36Sopenharmony_ci	idst = dst_cnt = (pdest && qdest) ? 2 : 1;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	/* One additional slot per destination to clone P/Q
261462306a36Sopenharmony_ci	 * before calculation (we have to preserve destinations).
261562306a36Sopenharmony_ci	 */
261662306a36Sopenharmony_ci	slot_cnt = src_cnt + dst_cnt * 2;
261762306a36Sopenharmony_ci	slots_per_op = 1;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
262062306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
262162306a36Sopenharmony_ci					     slots_per_op);
262262306a36Sopenharmony_ci	if (sw_desc) {
262362306a36Sopenharmony_ci		ppc440spe_desc_init_dma01pqzero_sum(sw_desc, dst_cnt, src_cnt);
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci		/* Setup byte count for each slot just allocated */
262662306a36Sopenharmony_ci		sw_desc->async_tx.flags = flags;
262762306a36Sopenharmony_ci		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
262862306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
262962306a36Sopenharmony_ci						      len);
263062306a36Sopenharmony_ci			iter->unmap_len = len;
263162306a36Sopenharmony_ci		}
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci		if (pdest) {
263462306a36Sopenharmony_ci			struct dma_cdb *hw_desc;
263562306a36Sopenharmony_ci			struct ppc440spe_adma_chan *chan;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci			iter = sw_desc->group_head;
263862306a36Sopenharmony_ci			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
263962306a36Sopenharmony_ci			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
264062306a36Sopenharmony_ci			iter->hw_next = list_entry(iter->chain_node.next,
264162306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
264262306a36Sopenharmony_ci						chain_node);
264362306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
264462306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
264562306a36Sopenharmony_ci			iter->src_cnt = 0;
264662306a36Sopenharmony_ci			iter->dst_cnt = 0;
264762306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan, 0,
264862306a36Sopenharmony_ci						     ppc440spe_chan->pdest, 0);
264962306a36Sopenharmony_ci			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, pdest);
265062306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
265162306a36Sopenharmony_ci						      len);
265262306a36Sopenharmony_ci			iter->unmap_len = 0;
265362306a36Sopenharmony_ci			/* override pdest to preserve original P */
265462306a36Sopenharmony_ci			pdest = ppc440spe_chan->pdest;
265562306a36Sopenharmony_ci		}
265662306a36Sopenharmony_ci		if (qdest) {
265762306a36Sopenharmony_ci			struct dma_cdb *hw_desc;
265862306a36Sopenharmony_ci			struct ppc440spe_adma_chan *chan;
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci			iter = list_first_entry(&sw_desc->group_list,
266162306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
266262306a36Sopenharmony_ci						chain_node);
266362306a36Sopenharmony_ci			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci			if (pdest) {
266662306a36Sopenharmony_ci				iter = list_entry(iter->chain_node.next,
266762306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
266862306a36Sopenharmony_ci						chain_node);
266962306a36Sopenharmony_ci			}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
267262306a36Sopenharmony_ci			iter->hw_next = list_entry(iter->chain_node.next,
267362306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
267462306a36Sopenharmony_ci						chain_node);
267562306a36Sopenharmony_ci			hw_desc = iter->hw_desc;
267662306a36Sopenharmony_ci			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
267762306a36Sopenharmony_ci			iter->src_cnt = 0;
267862306a36Sopenharmony_ci			iter->dst_cnt = 0;
267962306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan, 0,
268062306a36Sopenharmony_ci						     ppc440spe_chan->qdest, 0);
268162306a36Sopenharmony_ci			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, qdest);
268262306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
268362306a36Sopenharmony_ci						      len);
268462306a36Sopenharmony_ci			iter->unmap_len = 0;
268562306a36Sopenharmony_ci			/* override qdest to preserve original Q */
268662306a36Sopenharmony_ci			qdest = ppc440spe_chan->qdest;
268762306a36Sopenharmony_ci		}
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci		/* Setup destinations for P/Q ops */
269062306a36Sopenharmony_ci		ppc440spe_adma_pqzero_sum_set_dest(sw_desc, pdest, qdest);
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci		/* Setup zero QWORDs into DCHECK CDBs */
269362306a36Sopenharmony_ci		idst = dst_cnt;
269462306a36Sopenharmony_ci		list_for_each_entry_reverse(iter, &sw_desc->group_list,
269562306a36Sopenharmony_ci					    chain_node) {
269662306a36Sopenharmony_ci			/*
269762306a36Sopenharmony_ci			 * The last CDB corresponds to Q-parity check,
269862306a36Sopenharmony_ci			 * the one before last CDB corresponds
269962306a36Sopenharmony_ci			 * P-parity check
270062306a36Sopenharmony_ci			 */
270162306a36Sopenharmony_ci			if (idst == DMA_DEST_MAX_NUM) {
270262306a36Sopenharmony_ci				if (idst == dst_cnt) {
270362306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_QCHECK,
270462306a36Sopenharmony_ci						&iter->flags);
270562306a36Sopenharmony_ci				} else {
270662306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_PCHECK,
270762306a36Sopenharmony_ci						&iter->flags);
270862306a36Sopenharmony_ci				}
270962306a36Sopenharmony_ci			} else {
271062306a36Sopenharmony_ci				if (qdest) {
271162306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_QCHECK,
271262306a36Sopenharmony_ci						&iter->flags);
271362306a36Sopenharmony_ci				} else {
271462306a36Sopenharmony_ci					set_bit(PPC440SPE_DESC_PCHECK,
271562306a36Sopenharmony_ci						&iter->flags);
271662306a36Sopenharmony_ci				}
271762306a36Sopenharmony_ci			}
271862306a36Sopenharmony_ci			iter->xor_check_result = pqres;
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci			/*
272162306a36Sopenharmony_ci			 * set it to zero, if check fail then result will
272262306a36Sopenharmony_ci			 * be updated
272362306a36Sopenharmony_ci			 */
272462306a36Sopenharmony_ci			*iter->xor_check_result = 0;
272562306a36Sopenharmony_ci			ppc440spe_desc_set_dcheck(iter, ppc440spe_chan,
272662306a36Sopenharmony_ci				ppc440spe_qword);
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci			if (!(--dst_cnt))
272962306a36Sopenharmony_ci				break;
273062306a36Sopenharmony_ci		}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci		/* Setup sources and mults for P/Q ops */
273362306a36Sopenharmony_ci		list_for_each_entry_continue_reverse(iter, &sw_desc->group_list,
273462306a36Sopenharmony_ci						     chain_node) {
273562306a36Sopenharmony_ci			struct ppc440spe_adma_chan *chan;
273662306a36Sopenharmony_ci			u32 mult_dst;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
273962306a36Sopenharmony_ci			ppc440spe_desc_set_src_addr(iter, chan, 0,
274062306a36Sopenharmony_ci						    DMA_CUED_XOR_HB,
274162306a36Sopenharmony_ci						    src[src_cnt - 1]);
274262306a36Sopenharmony_ci			if (qdest) {
274362306a36Sopenharmony_ci				mult_dst = (dst_cnt - 1) ? DMA_CDB_SG_DST2 :
274462306a36Sopenharmony_ci							   DMA_CDB_SG_DST1;
274562306a36Sopenharmony_ci				ppc440spe_desc_set_src_mult(iter, chan,
274662306a36Sopenharmony_ci							    DMA_CUED_MULT1_OFF,
274762306a36Sopenharmony_ci							    mult_dst,
274862306a36Sopenharmony_ci							    scf[src_cnt - 1]);
274962306a36Sopenharmony_ci			}
275062306a36Sopenharmony_ci			if (!(--src_cnt))
275162306a36Sopenharmony_ci				break;
275262306a36Sopenharmony_ci		}
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
275562306a36Sopenharmony_ci	return sw_desc ? &sw_desc->async_tx : NULL;
275662306a36Sopenharmony_ci}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci/**
275962306a36Sopenharmony_ci * ppc440spe_adma_prep_dma_xor_zero_sum - prepare CDB group for
276062306a36Sopenharmony_ci * XOR ZERO_SUM operation
276162306a36Sopenharmony_ci */
276262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor_zero_sum(
276362306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt,
276462306a36Sopenharmony_ci		size_t len, enum sum_check_flags *result, unsigned long flags)
276562306a36Sopenharmony_ci{
276662306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
276762306a36Sopenharmony_ci	dma_addr_t pq[2];
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	/* validate P, disable Q */
277062306a36Sopenharmony_ci	pq[0] = src[0];
277162306a36Sopenharmony_ci	pq[1] = 0;
277262306a36Sopenharmony_ci	flags |= DMA_PREP_PQ_DISABLE_Q;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	tx = ppc440spe_adma_prep_dma_pqzero_sum(chan, pq, &src[1],
277562306a36Sopenharmony_ci						src_cnt - 1, 0, len,
277662306a36Sopenharmony_ci						result, flags);
277762306a36Sopenharmony_ci	return tx;
277862306a36Sopenharmony_ci}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci/**
278162306a36Sopenharmony_ci * ppc440spe_adma_set_dest - set destination address into descriptor
278262306a36Sopenharmony_ci */
278362306a36Sopenharmony_cistatic void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
278462306a36Sopenharmony_ci		dma_addr_t addr, int index)
278562306a36Sopenharmony_ci{
278662306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	BUG_ON(index >= sw_desc->dst_cnt);
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	switch (chan->device->id) {
279362306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
279462306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
279562306a36Sopenharmony_ci		/* to do: support transfers lengths >
279662306a36Sopenharmony_ci		 * PPC440SPE_ADMA_DMA/XOR_MAX_BYTE_COUNT
279762306a36Sopenharmony_ci		 */
279862306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(sw_desc->group_head,
279962306a36Sopenharmony_ci			chan, 0, addr, index);
280062306a36Sopenharmony_ci		break;
280162306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
280262306a36Sopenharmony_ci		sw_desc = ppc440spe_get_group_entry(sw_desc, index);
280362306a36Sopenharmony_ci		ppc440spe_desc_set_dest_addr(sw_desc,
280462306a36Sopenharmony_ci			chan, 0, addr, index);
280562306a36Sopenharmony_ci		break;
280662306a36Sopenharmony_ci	}
280762306a36Sopenharmony_ci}
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_cistatic void ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot *iter,
281062306a36Sopenharmony_ci		struct ppc440spe_adma_chan *chan, dma_addr_t addr)
281162306a36Sopenharmony_ci{
281262306a36Sopenharmony_ci	/*  To clear destinations update the descriptor
281362306a36Sopenharmony_ci	 * (P or Q depending on index) as follows:
281462306a36Sopenharmony_ci	 * addr is destination (0 corresponds to SG2):
281562306a36Sopenharmony_ci	 */
281662306a36Sopenharmony_ci	ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, addr, 0);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	/* ... and the addr is source: */
281962306a36Sopenharmony_ci	ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, addr);
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	/* addr is always SG2 then the mult is always DST1 */
282262306a36Sopenharmony_ci	ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
282362306a36Sopenharmony_ci				    DMA_CDB_SG_DST1, 1);
282462306a36Sopenharmony_ci}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci/**
282762306a36Sopenharmony_ci * ppc440spe_adma_pq_set_dest - set destination address into descriptor
282862306a36Sopenharmony_ci * for the PQXOR operation
282962306a36Sopenharmony_ci */
283062306a36Sopenharmony_cistatic void ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
283162306a36Sopenharmony_ci		dma_addr_t *addrs, unsigned long flags)
283262306a36Sopenharmony_ci{
283362306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter;
283462306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
283562306a36Sopenharmony_ci	dma_addr_t paddr, qaddr;
283662306a36Sopenharmony_ci	dma_addr_t addr = 0, ppath, qpath;
283762306a36Sopenharmony_ci	int index = 0, i;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	if (flags & DMA_PREP_PQ_DISABLE_P)
284262306a36Sopenharmony_ci		paddr = 0;
284362306a36Sopenharmony_ci	else
284462306a36Sopenharmony_ci		paddr = addrs[0];
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci	if (flags & DMA_PREP_PQ_DISABLE_Q)
284762306a36Sopenharmony_ci		qaddr = 0;
284862306a36Sopenharmony_ci	else
284962306a36Sopenharmony_ci		qaddr = addrs[1];
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	if (!paddr || !qaddr)
285262306a36Sopenharmony_ci		addr = paddr ? paddr : qaddr;
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	switch (chan->device->id) {
285562306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
285662306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
285762306a36Sopenharmony_ci		/* walk through the WXOR source list and set P/Q-destinations
285862306a36Sopenharmony_ci		 * for each slot:
285962306a36Sopenharmony_ci		 */
286062306a36Sopenharmony_ci		if (!test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
286162306a36Sopenharmony_ci			/* This is WXOR-only chain; may have 1/2 zero descs */
286262306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
286362306a36Sopenharmony_ci				index++;
286462306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
286562306a36Sopenharmony_ci				index++;
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc, index);
286862306a36Sopenharmony_ci			if (addr) {
286962306a36Sopenharmony_ci				/* one destination */
287062306a36Sopenharmony_ci				list_for_each_entry_from(iter,
287162306a36Sopenharmony_ci					&sw_desc->group_list, chain_node)
287262306a36Sopenharmony_ci					ppc440spe_desc_set_dest_addr(iter, chan,
287362306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, addr, 0);
287462306a36Sopenharmony_ci			} else {
287562306a36Sopenharmony_ci				/* two destinations */
287662306a36Sopenharmony_ci				list_for_each_entry_from(iter,
287762306a36Sopenharmony_ci					&sw_desc->group_list, chain_node) {
287862306a36Sopenharmony_ci					ppc440spe_desc_set_dest_addr(iter, chan,
287962306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, paddr, 0);
288062306a36Sopenharmony_ci					ppc440spe_desc_set_dest_addr(iter, chan,
288162306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, qaddr, 1);
288262306a36Sopenharmony_ci				}
288362306a36Sopenharmony_ci			}
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci			if (index) {
288662306a36Sopenharmony_ci				/*  To clear destinations update the descriptor
288762306a36Sopenharmony_ci				 * (1st,2nd, or both depending on flags)
288862306a36Sopenharmony_ci				 */
288962306a36Sopenharmony_ci				index = 0;
289062306a36Sopenharmony_ci				if (test_bit(PPC440SPE_ZERO_P,
289162306a36Sopenharmony_ci						&sw_desc->flags)) {
289262306a36Sopenharmony_ci					iter = ppc440spe_get_group_entry(
289362306a36Sopenharmony_ci							sw_desc, index++);
289462306a36Sopenharmony_ci					ppc440spe_adma_pq_zero_op(iter, chan,
289562306a36Sopenharmony_ci							paddr);
289662306a36Sopenharmony_ci				}
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci				if (test_bit(PPC440SPE_ZERO_Q,
289962306a36Sopenharmony_ci						&sw_desc->flags)) {
290062306a36Sopenharmony_ci					iter = ppc440spe_get_group_entry(
290162306a36Sopenharmony_ci							sw_desc, index++);
290262306a36Sopenharmony_ci					ppc440spe_adma_pq_zero_op(iter, chan,
290362306a36Sopenharmony_ci							qaddr);
290462306a36Sopenharmony_ci				}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci				return;
290762306a36Sopenharmony_ci			}
290862306a36Sopenharmony_ci		} else {
290962306a36Sopenharmony_ci			/* This is RXOR-only or RXOR/WXOR mixed chain */
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci			/* If we want to include destination into calculations,
291262306a36Sopenharmony_ci			 * then make dest addresses cued with mult=1 (XOR).
291362306a36Sopenharmony_ci			 */
291462306a36Sopenharmony_ci			ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
291562306a36Sopenharmony_ci					DMA_CUED_XOR_HB :
291662306a36Sopenharmony_ci					DMA_CUED_XOR_BASE |
291762306a36Sopenharmony_ci						(1 << DMA_CUED_MULT1_OFF);
291862306a36Sopenharmony_ci			qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
291962306a36Sopenharmony_ci					DMA_CUED_XOR_HB :
292062306a36Sopenharmony_ci					DMA_CUED_XOR_BASE |
292162306a36Sopenharmony_ci						(1 << DMA_CUED_MULT1_OFF);
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci			/* Setup destination(s) in RXOR slot(s) */
292462306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc, index++);
292562306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan,
292662306a36Sopenharmony_ci						paddr ? ppath : qpath,
292762306a36Sopenharmony_ci						paddr ? paddr : qaddr, 0);
292862306a36Sopenharmony_ci			if (!addr) {
292962306a36Sopenharmony_ci				/* two destinations */
293062306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc,
293162306a36Sopenharmony_ci								 index++);
293262306a36Sopenharmony_ci				ppc440spe_desc_set_dest_addr(iter, chan,
293362306a36Sopenharmony_ci						qpath, qaddr, 0);
293462306a36Sopenharmony_ci			}
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci			if (test_bit(PPC440SPE_DESC_WXOR, &sw_desc->flags)) {
293762306a36Sopenharmony_ci				/* Setup destination(s) in remaining WXOR
293862306a36Sopenharmony_ci				 * slots
293962306a36Sopenharmony_ci				 */
294062306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc,
294162306a36Sopenharmony_ci								 index);
294262306a36Sopenharmony_ci				if (addr) {
294362306a36Sopenharmony_ci					/* one destination */
294462306a36Sopenharmony_ci					list_for_each_entry_from(iter,
294562306a36Sopenharmony_ci					    &sw_desc->group_list,
294662306a36Sopenharmony_ci					    chain_node)
294762306a36Sopenharmony_ci						ppc440spe_desc_set_dest_addr(
294862306a36Sopenharmony_ci							iter, chan,
294962306a36Sopenharmony_ci							DMA_CUED_XOR_BASE,
295062306a36Sopenharmony_ci							addr, 0);
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci				} else {
295362306a36Sopenharmony_ci					/* two destinations */
295462306a36Sopenharmony_ci					list_for_each_entry_from(iter,
295562306a36Sopenharmony_ci					    &sw_desc->group_list,
295662306a36Sopenharmony_ci					    chain_node) {
295762306a36Sopenharmony_ci						ppc440spe_desc_set_dest_addr(
295862306a36Sopenharmony_ci							iter, chan,
295962306a36Sopenharmony_ci							DMA_CUED_XOR_BASE,
296062306a36Sopenharmony_ci							paddr, 0);
296162306a36Sopenharmony_ci						ppc440spe_desc_set_dest_addr(
296262306a36Sopenharmony_ci							iter, chan,
296362306a36Sopenharmony_ci							DMA_CUED_XOR_BASE,
296462306a36Sopenharmony_ci							qaddr, 1);
296562306a36Sopenharmony_ci					}
296662306a36Sopenharmony_ci				}
296762306a36Sopenharmony_ci			}
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_ci		}
297062306a36Sopenharmony_ci		break;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
297362306a36Sopenharmony_ci		/* DMA2 descriptors have only 1 destination, so there are
297462306a36Sopenharmony_ci		 * two chains - one for each dest.
297562306a36Sopenharmony_ci		 * If we want to include destination into calculations,
297662306a36Sopenharmony_ci		 * then make dest addresses cued with mult=1 (XOR).
297762306a36Sopenharmony_ci		 */
297862306a36Sopenharmony_ci		ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
297962306a36Sopenharmony_ci				DMA_CUED_XOR_HB :
298062306a36Sopenharmony_ci				DMA_CUED_XOR_BASE |
298162306a36Sopenharmony_ci					(1 << DMA_CUED_MULT1_OFF);
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci		qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
298462306a36Sopenharmony_ci				DMA_CUED_XOR_HB :
298562306a36Sopenharmony_ci				DMA_CUED_XOR_BASE |
298662306a36Sopenharmony_ci					(1 << DMA_CUED_MULT1_OFF);
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci		iter = ppc440spe_get_group_entry(sw_desc, 0);
298962306a36Sopenharmony_ci		for (i = 0; i < sw_desc->descs_per_op; i++) {
299062306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan,
299162306a36Sopenharmony_ci				paddr ? ppath : qpath,
299262306a36Sopenharmony_ci				paddr ? paddr : qaddr, 0);
299362306a36Sopenharmony_ci			iter = list_entry(iter->chain_node.next,
299462306a36Sopenharmony_ci					  struct ppc440spe_adma_desc_slot,
299562306a36Sopenharmony_ci					  chain_node);
299662306a36Sopenharmony_ci		}
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci		if (!addr) {
299962306a36Sopenharmony_ci			/* Two destinations; setup Q here */
300062306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc,
300162306a36Sopenharmony_ci				sw_desc->descs_per_op);
300262306a36Sopenharmony_ci			for (i = 0; i < sw_desc->descs_per_op; i++) {
300362306a36Sopenharmony_ci				ppc440spe_desc_set_dest_addr(iter,
300462306a36Sopenharmony_ci					chan, qpath, qaddr, 0);
300562306a36Sopenharmony_ci				iter = list_entry(iter->chain_node.next,
300662306a36Sopenharmony_ci						struct ppc440spe_adma_desc_slot,
300762306a36Sopenharmony_ci						chain_node);
300862306a36Sopenharmony_ci			}
300962306a36Sopenharmony_ci		}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci		break;
301262306a36Sopenharmony_ci	}
301362306a36Sopenharmony_ci}
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci/**
301662306a36Sopenharmony_ci * ppc440spe_adma_pq_zero_sum_set_dest - set destination address into descriptor
301762306a36Sopenharmony_ci * for the PQ_ZERO_SUM operation
301862306a36Sopenharmony_ci */
301962306a36Sopenharmony_cistatic void ppc440spe_adma_pqzero_sum_set_dest(
302062306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *sw_desc,
302162306a36Sopenharmony_ci		dma_addr_t paddr, dma_addr_t qaddr)
302262306a36Sopenharmony_ci{
302362306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter, *end;
302462306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
302562306a36Sopenharmony_ci	dma_addr_t addr = 0;
302662306a36Sopenharmony_ci	int idx;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	/* walk through the WXOR source list and set P/Q-destinations
303162306a36Sopenharmony_ci	 * for each slot
303262306a36Sopenharmony_ci	 */
303362306a36Sopenharmony_ci	idx = (paddr && qaddr) ? 2 : 1;
303462306a36Sopenharmony_ci	/* set end */
303562306a36Sopenharmony_ci	list_for_each_entry_reverse(end, &sw_desc->group_list,
303662306a36Sopenharmony_ci				    chain_node) {
303762306a36Sopenharmony_ci		if (!(--idx))
303862306a36Sopenharmony_ci			break;
303962306a36Sopenharmony_ci	}
304062306a36Sopenharmony_ci	/* set start */
304162306a36Sopenharmony_ci	idx = (paddr && qaddr) ? 2 : 1;
304262306a36Sopenharmony_ci	iter = ppc440spe_get_group_entry(sw_desc, idx);
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	if (paddr && qaddr) {
304562306a36Sopenharmony_ci		/* two destinations */
304662306a36Sopenharmony_ci		list_for_each_entry_from(iter, &sw_desc->group_list,
304762306a36Sopenharmony_ci					 chain_node) {
304862306a36Sopenharmony_ci			if (unlikely(iter == end))
304962306a36Sopenharmony_ci				break;
305062306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan,
305162306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, paddr, 0);
305262306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan,
305362306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, qaddr, 1);
305462306a36Sopenharmony_ci		}
305562306a36Sopenharmony_ci	} else {
305662306a36Sopenharmony_ci		/* one destination */
305762306a36Sopenharmony_ci		addr = paddr ? paddr : qaddr;
305862306a36Sopenharmony_ci		list_for_each_entry_from(iter, &sw_desc->group_list,
305962306a36Sopenharmony_ci					 chain_node) {
306062306a36Sopenharmony_ci			if (unlikely(iter == end))
306162306a36Sopenharmony_ci				break;
306262306a36Sopenharmony_ci			ppc440spe_desc_set_dest_addr(iter, chan,
306362306a36Sopenharmony_ci						DMA_CUED_XOR_BASE, addr, 0);
306462306a36Sopenharmony_ci		}
306562306a36Sopenharmony_ci	}
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	/*  The remaining descriptors are DATACHECK. These have no need in
306862306a36Sopenharmony_ci	 * destination. Actually, these destinations are used there
306962306a36Sopenharmony_ci	 * as sources for check operation. So, set addr as source.
307062306a36Sopenharmony_ci	 */
307162306a36Sopenharmony_ci	ppc440spe_desc_set_src_addr(end, chan, 0, 0, addr ? addr : paddr);
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	if (!addr) {
307462306a36Sopenharmony_ci		end = list_entry(end->chain_node.next,
307562306a36Sopenharmony_ci				 struct ppc440spe_adma_desc_slot, chain_node);
307662306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(end, chan, 0, 0, qaddr);
307762306a36Sopenharmony_ci	}
307862306a36Sopenharmony_ci}
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci/**
308162306a36Sopenharmony_ci * ppc440spe_desc_set_xor_src_cnt - set source count into descriptor
308262306a36Sopenharmony_ci */
308362306a36Sopenharmony_cistatic inline void ppc440spe_desc_set_xor_src_cnt(
308462306a36Sopenharmony_ci			struct ppc440spe_adma_desc_slot *desc,
308562306a36Sopenharmony_ci			int src_cnt)
308662306a36Sopenharmony_ci{
308762306a36Sopenharmony_ci	struct xor_cb *hw_desc = desc->hw_desc;
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci	hw_desc->cbc &= ~XOR_CDCR_OAC_MSK;
309062306a36Sopenharmony_ci	hw_desc->cbc |= src_cnt;
309162306a36Sopenharmony_ci}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci/**
309462306a36Sopenharmony_ci * ppc440spe_adma_pq_set_src - set source address into descriptor
309562306a36Sopenharmony_ci */
309662306a36Sopenharmony_cistatic void ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *sw_desc,
309762306a36Sopenharmony_ci		dma_addr_t addr, int index)
309862306a36Sopenharmony_ci{
309962306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
310062306a36Sopenharmony_ci	dma_addr_t haddr = 0;
310162306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter = NULL;
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	switch (chan->device->id) {
310662306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
310762306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
310862306a36Sopenharmony_ci		/* DMA0,1 may do: WXOR, RXOR, RXOR+WXORs chain
310962306a36Sopenharmony_ci		 */
311062306a36Sopenharmony_ci		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
311162306a36Sopenharmony_ci			/* RXOR-only or RXOR/WXOR operation */
311262306a36Sopenharmony_ci			int iskip = test_bit(PPC440SPE_DESC_RXOR12,
311362306a36Sopenharmony_ci				&sw_desc->flags) ?  2 : 3;
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci			if (index == 0) {
311662306a36Sopenharmony_ci				/* 1st slot (RXOR) */
311762306a36Sopenharmony_ci				/* setup sources region (R1-2-3, R1-2-4,
311862306a36Sopenharmony_ci				 * or R1-2-5)
311962306a36Sopenharmony_ci				 */
312062306a36Sopenharmony_ci				if (test_bit(PPC440SPE_DESC_RXOR12,
312162306a36Sopenharmony_ci						&sw_desc->flags))
312262306a36Sopenharmony_ci					haddr = DMA_RXOR12 <<
312362306a36Sopenharmony_ci						DMA_CUED_REGION_OFF;
312462306a36Sopenharmony_ci				else if (test_bit(PPC440SPE_DESC_RXOR123,
312562306a36Sopenharmony_ci				    &sw_desc->flags))
312662306a36Sopenharmony_ci					haddr = DMA_RXOR123 <<
312762306a36Sopenharmony_ci						DMA_CUED_REGION_OFF;
312862306a36Sopenharmony_ci				else if (test_bit(PPC440SPE_DESC_RXOR124,
312962306a36Sopenharmony_ci				    &sw_desc->flags))
313062306a36Sopenharmony_ci					haddr = DMA_RXOR124 <<
313162306a36Sopenharmony_ci						DMA_CUED_REGION_OFF;
313262306a36Sopenharmony_ci				else if (test_bit(PPC440SPE_DESC_RXOR125,
313362306a36Sopenharmony_ci				    &sw_desc->flags))
313462306a36Sopenharmony_ci					haddr = DMA_RXOR125 <<
313562306a36Sopenharmony_ci						DMA_CUED_REGION_OFF;
313662306a36Sopenharmony_ci				else
313762306a36Sopenharmony_ci					BUG();
313862306a36Sopenharmony_ci				haddr |= DMA_CUED_XOR_BASE;
313962306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc, 0);
314062306a36Sopenharmony_ci			} else if (index < iskip) {
314162306a36Sopenharmony_ci				/* 1st slot (RXOR)
314262306a36Sopenharmony_ci				 * shall actually set source address only once
314362306a36Sopenharmony_ci				 * instead of first <iskip>
314462306a36Sopenharmony_ci				 */
314562306a36Sopenharmony_ci				iter = NULL;
314662306a36Sopenharmony_ci			} else {
314762306a36Sopenharmony_ci				/* 2nd/3d and next slots (WXOR);
314862306a36Sopenharmony_ci				 * skip first slot with RXOR
314962306a36Sopenharmony_ci				 */
315062306a36Sopenharmony_ci				haddr = DMA_CUED_XOR_HB;
315162306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc,
315262306a36Sopenharmony_ci				    index - iskip + sw_desc->dst_cnt);
315362306a36Sopenharmony_ci			}
315462306a36Sopenharmony_ci		} else {
315562306a36Sopenharmony_ci			int znum = 0;
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci			/* WXOR-only operation; skip first slots with
315862306a36Sopenharmony_ci			 * zeroing destinations
315962306a36Sopenharmony_ci			 */
316062306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
316162306a36Sopenharmony_ci				znum++;
316262306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
316362306a36Sopenharmony_ci				znum++;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci			haddr = DMA_CUED_XOR_HB;
316662306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc,
316762306a36Sopenharmony_ci					index + znum);
316862306a36Sopenharmony_ci		}
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci		if (likely(iter)) {
317162306a36Sopenharmony_ci			ppc440spe_desc_set_src_addr(iter, chan, 0, haddr, addr);
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci			if (!index &&
317462306a36Sopenharmony_ci			    test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags) &&
317562306a36Sopenharmony_ci			    sw_desc->dst_cnt == 2) {
317662306a36Sopenharmony_ci				/* if we have two destinations for RXOR, then
317762306a36Sopenharmony_ci				 * setup source in the second descr too
317862306a36Sopenharmony_ci				 */
317962306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc, 1);
318062306a36Sopenharmony_ci				ppc440spe_desc_set_src_addr(iter, chan, 0,
318162306a36Sopenharmony_ci					haddr, addr);
318262306a36Sopenharmony_ci			}
318362306a36Sopenharmony_ci		}
318462306a36Sopenharmony_ci		break;
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
318762306a36Sopenharmony_ci		/* DMA2 may do Biskup */
318862306a36Sopenharmony_ci		iter = sw_desc->group_head;
318962306a36Sopenharmony_ci		if (iter->dst_cnt == 2) {
319062306a36Sopenharmony_ci			/* both P & Q calculations required; set P src here */
319162306a36Sopenharmony_ci			ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci			/* this is for Q */
319462306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc,
319562306a36Sopenharmony_ci				sw_desc->descs_per_op);
319662306a36Sopenharmony_ci		}
319762306a36Sopenharmony_ci		ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
319862306a36Sopenharmony_ci		break;
319962306a36Sopenharmony_ci	}
320062306a36Sopenharmony_ci}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci/**
320362306a36Sopenharmony_ci * ppc440spe_adma_memcpy_xor_set_src - set source address into descriptor
320462306a36Sopenharmony_ci */
320562306a36Sopenharmony_cistatic void ppc440spe_adma_memcpy_xor_set_src(
320662306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *sw_desc,
320762306a36Sopenharmony_ci		dma_addr_t addr, int index)
320862306a36Sopenharmony_ci{
320962306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
321262306a36Sopenharmony_ci	sw_desc = sw_desc->group_head;
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	if (likely(sw_desc))
321562306a36Sopenharmony_ci		ppc440spe_desc_set_src_addr(sw_desc, chan, index, 0, addr);
321662306a36Sopenharmony_ci}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci/**
321962306a36Sopenharmony_ci * ppc440spe_adma_dma2rxor_inc_addr  -
322062306a36Sopenharmony_ci */
322162306a36Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_inc_addr(
322262306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
322362306a36Sopenharmony_ci		struct ppc440spe_rxor *cursor, int index, int src_cnt)
322462306a36Sopenharmony_ci{
322562306a36Sopenharmony_ci	cursor->addr_count++;
322662306a36Sopenharmony_ci	if (index == src_cnt - 1) {
322762306a36Sopenharmony_ci		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
322862306a36Sopenharmony_ci	} else if (cursor->addr_count == XOR_MAX_OPS) {
322962306a36Sopenharmony_ci		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
323062306a36Sopenharmony_ci		cursor->addr_count = 0;
323162306a36Sopenharmony_ci		cursor->desc_count++;
323262306a36Sopenharmony_ci	}
323362306a36Sopenharmony_ci}
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci/**
323662306a36Sopenharmony_ci * ppc440spe_adma_dma2rxor_prep_src - setup RXOR types in DMA2 CDB
323762306a36Sopenharmony_ci */
323862306a36Sopenharmony_cistatic int ppc440spe_adma_dma2rxor_prep_src(
323962306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *hdesc,
324062306a36Sopenharmony_ci		struct ppc440spe_rxor *cursor, int index,
324162306a36Sopenharmony_ci		int src_cnt, u32 addr)
324262306a36Sopenharmony_ci{
324362306a36Sopenharmony_ci	u32 sign;
324462306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *desc = hdesc;
324562306a36Sopenharmony_ci	int i;
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	for (i = 0; i < cursor->desc_count; i++) {
324862306a36Sopenharmony_ci		desc = list_entry(hdesc->chain_node.next,
324962306a36Sopenharmony_ci				  struct ppc440spe_adma_desc_slot,
325062306a36Sopenharmony_ci				  chain_node);
325162306a36Sopenharmony_ci	}
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	switch (cursor->state) {
325462306a36Sopenharmony_ci	case 0:
325562306a36Sopenharmony_ci		if (addr == cursor->addrl + cursor->len) {
325662306a36Sopenharmony_ci			/* direct RXOR */
325762306a36Sopenharmony_ci			cursor->state = 1;
325862306a36Sopenharmony_ci			cursor->xor_count++;
325962306a36Sopenharmony_ci			if (index == src_cnt-1) {
326062306a36Sopenharmony_ci				ppc440spe_rxor_set_region(desc,
326162306a36Sopenharmony_ci					cursor->addr_count,
326262306a36Sopenharmony_ci					DMA_RXOR12 << DMA_CUED_REGION_OFF);
326362306a36Sopenharmony_ci				ppc440spe_adma_dma2rxor_inc_addr(
326462306a36Sopenharmony_ci					desc, cursor, index, src_cnt);
326562306a36Sopenharmony_ci			}
326662306a36Sopenharmony_ci		} else if (cursor->addrl == addr + cursor->len) {
326762306a36Sopenharmony_ci			/* reverse RXOR */
326862306a36Sopenharmony_ci			cursor->state = 1;
326962306a36Sopenharmony_ci			cursor->xor_count++;
327062306a36Sopenharmony_ci			set_bit(cursor->addr_count, &desc->reverse_flags[0]);
327162306a36Sopenharmony_ci			if (index == src_cnt-1) {
327262306a36Sopenharmony_ci				ppc440spe_rxor_set_region(desc,
327362306a36Sopenharmony_ci					cursor->addr_count,
327462306a36Sopenharmony_ci					DMA_RXOR12 << DMA_CUED_REGION_OFF);
327562306a36Sopenharmony_ci				ppc440spe_adma_dma2rxor_inc_addr(
327662306a36Sopenharmony_ci					desc, cursor, index, src_cnt);
327762306a36Sopenharmony_ci			}
327862306a36Sopenharmony_ci		} else {
327962306a36Sopenharmony_ci			printk(KERN_ERR "Cannot build "
328062306a36Sopenharmony_ci				"DMA2 RXOR command block.\n");
328162306a36Sopenharmony_ci			BUG();
328262306a36Sopenharmony_ci		}
328362306a36Sopenharmony_ci		break;
328462306a36Sopenharmony_ci	case 1:
328562306a36Sopenharmony_ci		sign = test_bit(cursor->addr_count,
328662306a36Sopenharmony_ci				desc->reverse_flags)
328762306a36Sopenharmony_ci			? -1 : 1;
328862306a36Sopenharmony_ci		if (index == src_cnt-2 || (sign == -1
328962306a36Sopenharmony_ci			&& addr != cursor->addrl - 2*cursor->len)) {
329062306a36Sopenharmony_ci			cursor->state = 0;
329162306a36Sopenharmony_ci			cursor->xor_count = 1;
329262306a36Sopenharmony_ci			cursor->addrl = addr;
329362306a36Sopenharmony_ci			ppc440spe_rxor_set_region(desc,
329462306a36Sopenharmony_ci				cursor->addr_count,
329562306a36Sopenharmony_ci				DMA_RXOR12 << DMA_CUED_REGION_OFF);
329662306a36Sopenharmony_ci			ppc440spe_adma_dma2rxor_inc_addr(
329762306a36Sopenharmony_ci				desc, cursor, index, src_cnt);
329862306a36Sopenharmony_ci		} else if (addr == cursor->addrl + 2*sign*cursor->len) {
329962306a36Sopenharmony_ci			cursor->state = 2;
330062306a36Sopenharmony_ci			cursor->xor_count = 0;
330162306a36Sopenharmony_ci			ppc440spe_rxor_set_region(desc,
330262306a36Sopenharmony_ci				cursor->addr_count,
330362306a36Sopenharmony_ci				DMA_RXOR123 << DMA_CUED_REGION_OFF);
330462306a36Sopenharmony_ci			if (index == src_cnt-1) {
330562306a36Sopenharmony_ci				ppc440spe_adma_dma2rxor_inc_addr(
330662306a36Sopenharmony_ci					desc, cursor, index, src_cnt);
330762306a36Sopenharmony_ci			}
330862306a36Sopenharmony_ci		} else if (addr == cursor->addrl + 3*cursor->len) {
330962306a36Sopenharmony_ci			cursor->state = 2;
331062306a36Sopenharmony_ci			cursor->xor_count = 0;
331162306a36Sopenharmony_ci			ppc440spe_rxor_set_region(desc,
331262306a36Sopenharmony_ci				cursor->addr_count,
331362306a36Sopenharmony_ci				DMA_RXOR124 << DMA_CUED_REGION_OFF);
331462306a36Sopenharmony_ci			if (index == src_cnt-1) {
331562306a36Sopenharmony_ci				ppc440spe_adma_dma2rxor_inc_addr(
331662306a36Sopenharmony_ci					desc, cursor, index, src_cnt);
331762306a36Sopenharmony_ci			}
331862306a36Sopenharmony_ci		} else if (addr == cursor->addrl + 4*cursor->len) {
331962306a36Sopenharmony_ci			cursor->state = 2;
332062306a36Sopenharmony_ci			cursor->xor_count = 0;
332162306a36Sopenharmony_ci			ppc440spe_rxor_set_region(desc,
332262306a36Sopenharmony_ci				cursor->addr_count,
332362306a36Sopenharmony_ci				DMA_RXOR125 << DMA_CUED_REGION_OFF);
332462306a36Sopenharmony_ci			if (index == src_cnt-1) {
332562306a36Sopenharmony_ci				ppc440spe_adma_dma2rxor_inc_addr(
332662306a36Sopenharmony_ci					desc, cursor, index, src_cnt);
332762306a36Sopenharmony_ci			}
332862306a36Sopenharmony_ci		} else {
332962306a36Sopenharmony_ci			cursor->state = 0;
333062306a36Sopenharmony_ci			cursor->xor_count = 1;
333162306a36Sopenharmony_ci			cursor->addrl = addr;
333262306a36Sopenharmony_ci			ppc440spe_rxor_set_region(desc,
333362306a36Sopenharmony_ci				cursor->addr_count,
333462306a36Sopenharmony_ci				DMA_RXOR12 << DMA_CUED_REGION_OFF);
333562306a36Sopenharmony_ci			ppc440spe_adma_dma2rxor_inc_addr(
333662306a36Sopenharmony_ci				desc, cursor, index, src_cnt);
333762306a36Sopenharmony_ci		}
333862306a36Sopenharmony_ci		break;
333962306a36Sopenharmony_ci	case 2:
334062306a36Sopenharmony_ci		cursor->state = 0;
334162306a36Sopenharmony_ci		cursor->addrl = addr;
334262306a36Sopenharmony_ci		cursor->xor_count++;
334362306a36Sopenharmony_ci		if (index) {
334462306a36Sopenharmony_ci			ppc440spe_adma_dma2rxor_inc_addr(
334562306a36Sopenharmony_ci				desc, cursor, index, src_cnt);
334662306a36Sopenharmony_ci		}
334762306a36Sopenharmony_ci		break;
334862306a36Sopenharmony_ci	}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	return 0;
335162306a36Sopenharmony_ci}
335262306a36Sopenharmony_ci
335362306a36Sopenharmony_ci/**
335462306a36Sopenharmony_ci * ppc440spe_adma_dma2rxor_set_src - set RXOR source address; it's assumed that
335562306a36Sopenharmony_ci *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
335662306a36Sopenharmony_ci */
335762306a36Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_src(
335862306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
335962306a36Sopenharmony_ci		int index, dma_addr_t addr)
336062306a36Sopenharmony_ci{
336162306a36Sopenharmony_ci	struct xor_cb *xcb = desc->hw_desc;
336262306a36Sopenharmony_ci	int k = 0, op = 0, lop = 0;
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	/* get the RXOR operand which corresponds to index addr */
336562306a36Sopenharmony_ci	while (op <= index) {
336662306a36Sopenharmony_ci		lop = op;
336762306a36Sopenharmony_ci		if (k == XOR_MAX_OPS) {
336862306a36Sopenharmony_ci			k = 0;
336962306a36Sopenharmony_ci			desc = list_entry(desc->chain_node.next,
337062306a36Sopenharmony_ci				struct ppc440spe_adma_desc_slot, chain_node);
337162306a36Sopenharmony_ci			xcb = desc->hw_desc;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci		}
337462306a36Sopenharmony_ci		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
337562306a36Sopenharmony_ci		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
337662306a36Sopenharmony_ci			op += 2;
337762306a36Sopenharmony_ci		else
337862306a36Sopenharmony_ci			op += 3;
337962306a36Sopenharmony_ci	}
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	BUG_ON(k < 1);
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci	if (test_bit(k-1, desc->reverse_flags)) {
338462306a36Sopenharmony_ci		/* reverse operand order; put last op in RXOR group */
338562306a36Sopenharmony_ci		if (index == op - 1)
338662306a36Sopenharmony_ci			ppc440spe_rxor_set_src(desc, k - 1, addr);
338762306a36Sopenharmony_ci	} else {
338862306a36Sopenharmony_ci		/* direct operand order; put first op in RXOR group */
338962306a36Sopenharmony_ci		if (index == lop)
339062306a36Sopenharmony_ci			ppc440spe_rxor_set_src(desc, k - 1, addr);
339162306a36Sopenharmony_ci	}
339262306a36Sopenharmony_ci}
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci/**
339562306a36Sopenharmony_ci * ppc440spe_adma_dma2rxor_set_mult - set RXOR multipliers; it's assumed that
339662306a36Sopenharmony_ci *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
339762306a36Sopenharmony_ci */
339862306a36Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_mult(
339962306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *desc,
340062306a36Sopenharmony_ci		int index, u8 mult)
340162306a36Sopenharmony_ci{
340262306a36Sopenharmony_ci	struct xor_cb *xcb = desc->hw_desc;
340362306a36Sopenharmony_ci	int k = 0, op = 0, lop = 0;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	/* get the RXOR operand which corresponds to index mult */
340662306a36Sopenharmony_ci	while (op <= index) {
340762306a36Sopenharmony_ci		lop = op;
340862306a36Sopenharmony_ci		if (k == XOR_MAX_OPS) {
340962306a36Sopenharmony_ci			k = 0;
341062306a36Sopenharmony_ci			desc = list_entry(desc->chain_node.next,
341162306a36Sopenharmony_ci					  struct ppc440spe_adma_desc_slot,
341262306a36Sopenharmony_ci					  chain_node);
341362306a36Sopenharmony_ci			xcb = desc->hw_desc;
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci		}
341662306a36Sopenharmony_ci		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
341762306a36Sopenharmony_ci		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
341862306a36Sopenharmony_ci			op += 2;
341962306a36Sopenharmony_ci		else
342062306a36Sopenharmony_ci			op += 3;
342162306a36Sopenharmony_ci	}
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	BUG_ON(k < 1);
342462306a36Sopenharmony_ci	if (test_bit(k-1, desc->reverse_flags)) {
342562306a36Sopenharmony_ci		/* reverse order */
342662306a36Sopenharmony_ci		ppc440spe_rxor_set_mult(desc, k - 1, op - index - 1, mult);
342762306a36Sopenharmony_ci	} else {
342862306a36Sopenharmony_ci		/* direct order */
342962306a36Sopenharmony_ci		ppc440spe_rxor_set_mult(desc, k - 1, index - lop, mult);
343062306a36Sopenharmony_ci	}
343162306a36Sopenharmony_ci}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci/**
343462306a36Sopenharmony_ci * ppc440spe_init_rxor_cursor -
343562306a36Sopenharmony_ci */
343662306a36Sopenharmony_cistatic void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor)
343762306a36Sopenharmony_ci{
343862306a36Sopenharmony_ci	memset(cursor, 0, sizeof(struct ppc440spe_rxor));
343962306a36Sopenharmony_ci	cursor->state = 2;
344062306a36Sopenharmony_ci}
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci/**
344362306a36Sopenharmony_ci * ppc440spe_adma_pq_set_src_mult - set multiplication coefficient into
344462306a36Sopenharmony_ci * descriptor for the PQXOR operation
344562306a36Sopenharmony_ci */
344662306a36Sopenharmony_cistatic void ppc440spe_adma_pq_set_src_mult(
344762306a36Sopenharmony_ci		struct ppc440spe_adma_desc_slot *sw_desc,
344862306a36Sopenharmony_ci		unsigned char mult, int index, int dst_pos)
344962306a36Sopenharmony_ci{
345062306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
345162306a36Sopenharmony_ci	u32 mult_idx, mult_dst;
345262306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter = NULL, *iter1 = NULL;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
345562306a36Sopenharmony_ci
345662306a36Sopenharmony_ci	switch (chan->device->id) {
345762306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
345862306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
345962306a36Sopenharmony_ci		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
346062306a36Sopenharmony_ci			int region = test_bit(PPC440SPE_DESC_RXOR12,
346162306a36Sopenharmony_ci					&sw_desc->flags) ? 2 : 3;
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_ci			if (index < region) {
346462306a36Sopenharmony_ci				/* RXOR multipliers */
346562306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc,
346662306a36Sopenharmony_ci					sw_desc->dst_cnt - 1);
346762306a36Sopenharmony_ci				if (sw_desc->dst_cnt == 2)
346862306a36Sopenharmony_ci					iter1 = ppc440spe_get_group_entry(
346962306a36Sopenharmony_ci							sw_desc, 0);
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci				mult_idx = DMA_CUED_MULT1_OFF + (index << 3);
347262306a36Sopenharmony_ci				mult_dst = DMA_CDB_SG_SRC;
347362306a36Sopenharmony_ci			} else {
347462306a36Sopenharmony_ci				/* WXOR multiplier */
347562306a36Sopenharmony_ci				iter = ppc440spe_get_group_entry(sw_desc,
347662306a36Sopenharmony_ci							index - region +
347762306a36Sopenharmony_ci							sw_desc->dst_cnt);
347862306a36Sopenharmony_ci				mult_idx = DMA_CUED_MULT1_OFF;
347962306a36Sopenharmony_ci				mult_dst = dst_pos ? DMA_CDB_SG_DST2 :
348062306a36Sopenharmony_ci						     DMA_CDB_SG_DST1;
348162306a36Sopenharmony_ci			}
348262306a36Sopenharmony_ci		} else {
348362306a36Sopenharmony_ci			int znum = 0;
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci			/* WXOR-only;
348662306a36Sopenharmony_ci			 * skip first slots with destinations (if ZERO_DST has
348762306a36Sopenharmony_ci			 * place)
348862306a36Sopenharmony_ci			 */
348962306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
349062306a36Sopenharmony_ci				znum++;
349162306a36Sopenharmony_ci			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
349262306a36Sopenharmony_ci				znum++;
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc, index + znum);
349562306a36Sopenharmony_ci			mult_idx = DMA_CUED_MULT1_OFF;
349662306a36Sopenharmony_ci			mult_dst = dst_pos ? DMA_CDB_SG_DST2 : DMA_CDB_SG_DST1;
349762306a36Sopenharmony_ci		}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci		if (likely(iter)) {
350062306a36Sopenharmony_ci			ppc440spe_desc_set_src_mult(iter, chan,
350162306a36Sopenharmony_ci				mult_idx, mult_dst, mult);
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci			if (unlikely(iter1)) {
350462306a36Sopenharmony_ci				/* if we have two destinations for RXOR, then
350562306a36Sopenharmony_ci				 * we've just set Q mult. Set-up P now.
350662306a36Sopenharmony_ci				 */
350762306a36Sopenharmony_ci				ppc440spe_desc_set_src_mult(iter1, chan,
350862306a36Sopenharmony_ci					mult_idx, mult_dst, 1);
350962306a36Sopenharmony_ci			}
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci		}
351262306a36Sopenharmony_ci		break;
351362306a36Sopenharmony_ci
351462306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
351562306a36Sopenharmony_ci		iter = sw_desc->group_head;
351662306a36Sopenharmony_ci		if (sw_desc->dst_cnt == 2) {
351762306a36Sopenharmony_ci			/* both P & Q calculations required; set P mult here */
351862306a36Sopenharmony_ci			ppc440spe_adma_dma2rxor_set_mult(iter, index, 1);
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci			/* and then set Q mult */
352162306a36Sopenharmony_ci			iter = ppc440spe_get_group_entry(sw_desc,
352262306a36Sopenharmony_ci			       sw_desc->descs_per_op);
352362306a36Sopenharmony_ci		}
352462306a36Sopenharmony_ci		ppc440spe_adma_dma2rxor_set_mult(iter, index, mult);
352562306a36Sopenharmony_ci		break;
352662306a36Sopenharmony_ci	}
352762306a36Sopenharmony_ci}
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci/**
353062306a36Sopenharmony_ci * ppc440spe_adma_free_chan_resources - free the resources allocated
353162306a36Sopenharmony_ci */
353262306a36Sopenharmony_cistatic void ppc440spe_adma_free_chan_resources(struct dma_chan *chan)
353362306a36Sopenharmony_ci{
353462306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
353562306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *iter, *_iter;
353662306a36Sopenharmony_ci	int in_use_descs = 0;
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
353962306a36Sopenharmony_ci	ppc440spe_adma_slot_cleanup(ppc440spe_chan);
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci	spin_lock_bh(&ppc440spe_chan->lock);
354262306a36Sopenharmony_ci	list_for_each_entry_safe(iter, _iter, &ppc440spe_chan->chain,
354362306a36Sopenharmony_ci					chain_node) {
354462306a36Sopenharmony_ci		in_use_descs++;
354562306a36Sopenharmony_ci		list_del(&iter->chain_node);
354662306a36Sopenharmony_ci	}
354762306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(iter, _iter,
354862306a36Sopenharmony_ci			&ppc440spe_chan->all_slots, slot_node) {
354962306a36Sopenharmony_ci		list_del(&iter->slot_node);
355062306a36Sopenharmony_ci		kfree(iter);
355162306a36Sopenharmony_ci		ppc440spe_chan->slots_allocated--;
355262306a36Sopenharmony_ci	}
355362306a36Sopenharmony_ci	ppc440spe_chan->last_used = NULL;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
355662306a36Sopenharmony_ci		"ppc440spe adma%d %s slots_allocated %d\n",
355762306a36Sopenharmony_ci		ppc440spe_chan->device->id,
355862306a36Sopenharmony_ci		__func__, ppc440spe_chan->slots_allocated);
355962306a36Sopenharmony_ci	spin_unlock_bh(&ppc440spe_chan->lock);
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	/* one is ok since we left it on there on purpose */
356262306a36Sopenharmony_ci	if (in_use_descs > 1)
356362306a36Sopenharmony_ci		printk(KERN_ERR "SPE: Freeing %d in use descriptors!\n",
356462306a36Sopenharmony_ci			in_use_descs - 1);
356562306a36Sopenharmony_ci}
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci/**
356862306a36Sopenharmony_ci * ppc440spe_adma_tx_status - poll the status of an ADMA transaction
356962306a36Sopenharmony_ci * @chan: ADMA channel handle
357062306a36Sopenharmony_ci * @cookie: ADMA transaction identifier
357162306a36Sopenharmony_ci * @txstate: a holder for the current state of the channel
357262306a36Sopenharmony_ci */
357362306a36Sopenharmony_cistatic enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan,
357462306a36Sopenharmony_ci			dma_cookie_t cookie, struct dma_tx_state *txstate)
357562306a36Sopenharmony_ci{
357662306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
357762306a36Sopenharmony_ci	enum dma_status ret;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
358062306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
358162306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
358262306a36Sopenharmony_ci		return ret;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci	ppc440spe_adma_slot_cleanup(ppc440spe_chan);
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci	return dma_cookie_status(chan, cookie, txstate);
358762306a36Sopenharmony_ci}
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci/**
359062306a36Sopenharmony_ci * ppc440spe_adma_eot_handler - end of transfer interrupt handler
359162306a36Sopenharmony_ci */
359262306a36Sopenharmony_cistatic irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
359362306a36Sopenharmony_ci{
359462306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan = data;
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev,
359762306a36Sopenharmony_ci		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	tasklet_schedule(&chan->irq_tasklet);
360062306a36Sopenharmony_ci	ppc440spe_adma_device_clear_eot_status(chan);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci	return IRQ_HANDLED;
360362306a36Sopenharmony_ci}
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci/**
360662306a36Sopenharmony_ci * ppc440spe_adma_err_handler - DMA error interrupt handler;
360762306a36Sopenharmony_ci *	do the same things as a eot handler
360862306a36Sopenharmony_ci */
360962306a36Sopenharmony_cistatic irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
361062306a36Sopenharmony_ci{
361162306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan = data;
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev,
361462306a36Sopenharmony_ci		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_ci	tasklet_schedule(&chan->irq_tasklet);
361762306a36Sopenharmony_ci	ppc440spe_adma_device_clear_eot_status(chan);
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	return IRQ_HANDLED;
362062306a36Sopenharmony_ci}
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci/**
362362306a36Sopenharmony_ci * ppc440spe_test_callback - called when test operation has been done
362462306a36Sopenharmony_ci */
362562306a36Sopenharmony_cistatic void ppc440spe_test_callback(void *unused)
362662306a36Sopenharmony_ci{
362762306a36Sopenharmony_ci	complete(&ppc440spe_r6_test_comp);
362862306a36Sopenharmony_ci}
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci/**
363162306a36Sopenharmony_ci * ppc440spe_adma_issue_pending - flush all pending descriptors to h/w
363262306a36Sopenharmony_ci */
363362306a36Sopenharmony_cistatic void ppc440spe_adma_issue_pending(struct dma_chan *chan)
363462306a36Sopenharmony_ci{
363562306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
363862306a36Sopenharmony_ci	dev_dbg(ppc440spe_chan->device->common.dev,
363962306a36Sopenharmony_ci		"ppc440spe adma%d: %s %d \n", ppc440spe_chan->device->id,
364062306a36Sopenharmony_ci		__func__, ppc440spe_chan->pending);
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	if (ppc440spe_chan->pending) {
364362306a36Sopenharmony_ci		ppc440spe_chan->pending = 0;
364462306a36Sopenharmony_ci		ppc440spe_chan_append(ppc440spe_chan);
364562306a36Sopenharmony_ci	}
364662306a36Sopenharmony_ci}
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci/**
364962306a36Sopenharmony_ci * ppc440spe_chan_start_null_xor - initiate the first XOR operation (DMA engines
365062306a36Sopenharmony_ci *	use FIFOs (as opposite to chains used in XOR) so this is a XOR
365162306a36Sopenharmony_ci *	specific operation)
365262306a36Sopenharmony_ci */
365362306a36Sopenharmony_cistatic void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
365462306a36Sopenharmony_ci{
365562306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
365662306a36Sopenharmony_ci	dma_cookie_t cookie;
365762306a36Sopenharmony_ci	int slot_cnt, slots_per_op;
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	dev_dbg(chan->device->common.dev,
366062306a36Sopenharmony_ci		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci	spin_lock_bh(&chan->lock);
366362306a36Sopenharmony_ci	slot_cnt = ppc440spe_chan_xor_slot_count(0, 2, &slots_per_op);
366462306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(chan, slot_cnt, slots_per_op);
366562306a36Sopenharmony_ci	if (sw_desc) {
366662306a36Sopenharmony_ci		group_start = sw_desc->group_head;
366762306a36Sopenharmony_ci		list_splice_init(&sw_desc->group_list, &chan->chain);
366862306a36Sopenharmony_ci		async_tx_ack(&sw_desc->async_tx);
366962306a36Sopenharmony_ci		ppc440spe_desc_init_null_xor(group_start);
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci		cookie = dma_cookie_assign(&sw_desc->async_tx);
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci		/* initialize the completed cookie to be less than
367462306a36Sopenharmony_ci		 * the most recently used cookie
367562306a36Sopenharmony_ci		 */
367662306a36Sopenharmony_ci		chan->common.completed_cookie = cookie - 1;
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_ci		/* channel should not be busy */
367962306a36Sopenharmony_ci		BUG_ON(ppc440spe_chan_is_busy(chan));
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ci		/* set the descriptor address */
368262306a36Sopenharmony_ci		ppc440spe_chan_set_first_xor_descriptor(chan, sw_desc);
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci		/* run the descriptor */
368562306a36Sopenharmony_ci		ppc440spe_chan_run(chan);
368662306a36Sopenharmony_ci	} else
368762306a36Sopenharmony_ci		printk(KERN_ERR "ppc440spe adma%d"
368862306a36Sopenharmony_ci			" failed to allocate null descriptor\n",
368962306a36Sopenharmony_ci			chan->device->id);
369062306a36Sopenharmony_ci	spin_unlock_bh(&chan->lock);
369162306a36Sopenharmony_ci}
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_ci/**
369462306a36Sopenharmony_ci * ppc440spe_test_raid6 - test are RAID-6 capabilities enabled successfully.
369562306a36Sopenharmony_ci *	For this we just perform one WXOR operation with the same source
369662306a36Sopenharmony_ci *	and destination addresses, the GF-multiplier is 1; so if RAID-6
369762306a36Sopenharmony_ci *	capabilities are enabled then we'll get src/dst filled with zero.
369862306a36Sopenharmony_ci */
369962306a36Sopenharmony_cistatic int ppc440spe_test_raid6(struct ppc440spe_adma_chan *chan)
370062306a36Sopenharmony_ci{
370162306a36Sopenharmony_ci	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
370262306a36Sopenharmony_ci	struct page *pg;
370362306a36Sopenharmony_ci	char *a;
370462306a36Sopenharmony_ci	dma_addr_t dma_addr, addrs[2];
370562306a36Sopenharmony_ci	unsigned long op = 0;
370662306a36Sopenharmony_ci	int rval = 0;
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci	set_bit(PPC440SPE_DESC_WXOR, &op);
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_ci	pg = alloc_page(GFP_KERNEL);
371162306a36Sopenharmony_ci	if (!pg)
371262306a36Sopenharmony_ci		return -ENOMEM;
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	spin_lock_bh(&chan->lock);
371562306a36Sopenharmony_ci	sw_desc = ppc440spe_adma_alloc_slots(chan, 1, 1);
371662306a36Sopenharmony_ci	if (sw_desc) {
371762306a36Sopenharmony_ci		/* 1 src, 1 dsr, int_ena, WXOR */
371862306a36Sopenharmony_ci		ppc440spe_desc_init_dma01pq(sw_desc, 1, 1, 1, op);
371962306a36Sopenharmony_ci		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
372062306a36Sopenharmony_ci			ppc440spe_desc_set_byte_count(iter, chan, PAGE_SIZE);
372162306a36Sopenharmony_ci			iter->unmap_len = PAGE_SIZE;
372262306a36Sopenharmony_ci		}
372362306a36Sopenharmony_ci	} else {
372462306a36Sopenharmony_ci		rval = -EFAULT;
372562306a36Sopenharmony_ci		spin_unlock_bh(&chan->lock);
372662306a36Sopenharmony_ci		goto exit;
372762306a36Sopenharmony_ci	}
372862306a36Sopenharmony_ci	spin_unlock_bh(&chan->lock);
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	/* Fill the test page with ones */
373162306a36Sopenharmony_ci	memset(page_address(pg), 0xFF, PAGE_SIZE);
373262306a36Sopenharmony_ci	dma_addr = dma_map_page(chan->device->dev, pg, 0,
373362306a36Sopenharmony_ci				PAGE_SIZE, DMA_BIDIRECTIONAL);
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	/* Setup addresses */
373662306a36Sopenharmony_ci	ppc440spe_adma_pq_set_src(sw_desc, dma_addr, 0);
373762306a36Sopenharmony_ci	ppc440spe_adma_pq_set_src_mult(sw_desc, 1, 0, 0);
373862306a36Sopenharmony_ci	addrs[0] = dma_addr;
373962306a36Sopenharmony_ci	addrs[1] = 0;
374062306a36Sopenharmony_ci	ppc440spe_adma_pq_set_dest(sw_desc, addrs, DMA_PREP_PQ_DISABLE_Q);
374162306a36Sopenharmony_ci
374262306a36Sopenharmony_ci	async_tx_ack(&sw_desc->async_tx);
374362306a36Sopenharmony_ci	sw_desc->async_tx.callback = ppc440spe_test_callback;
374462306a36Sopenharmony_ci	sw_desc->async_tx.callback_param = NULL;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	init_completion(&ppc440spe_r6_test_comp);
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	ppc440spe_adma_tx_submit(&sw_desc->async_tx);
374962306a36Sopenharmony_ci	ppc440spe_adma_issue_pending(&chan->common);
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci	wait_for_completion(&ppc440spe_r6_test_comp);
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci	/* Now check if the test page is zeroed */
375462306a36Sopenharmony_ci	a = page_address(pg);
375562306a36Sopenharmony_ci	if ((*(u32 *)a) == 0 && memcmp(a, a+4, PAGE_SIZE-4) == 0) {
375662306a36Sopenharmony_ci		/* page is zero - RAID-6 enabled */
375762306a36Sopenharmony_ci		rval = 0;
375862306a36Sopenharmony_ci	} else {
375962306a36Sopenharmony_ci		/* RAID-6 was not enabled */
376062306a36Sopenharmony_ci		rval = -EINVAL;
376162306a36Sopenharmony_ci	}
376262306a36Sopenharmony_ciexit:
376362306a36Sopenharmony_ci	__free_page(pg);
376462306a36Sopenharmony_ci	return rval;
376562306a36Sopenharmony_ci}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_cistatic void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev)
376862306a36Sopenharmony_ci{
376962306a36Sopenharmony_ci	switch (adev->id) {
377062306a36Sopenharmony_ci	case PPC440SPE_DMA0_ID:
377162306a36Sopenharmony_ci	case PPC440SPE_DMA1_ID:
377262306a36Sopenharmony_ci		dma_cap_set(DMA_MEMCPY, adev->common.cap_mask);
377362306a36Sopenharmony_ci		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
377462306a36Sopenharmony_ci		dma_cap_set(DMA_PQ, adev->common.cap_mask);
377562306a36Sopenharmony_ci		dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask);
377662306a36Sopenharmony_ci		dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask);
377762306a36Sopenharmony_ci		break;
377862306a36Sopenharmony_ci	case PPC440SPE_XOR_ID:
377962306a36Sopenharmony_ci		dma_cap_set(DMA_XOR, adev->common.cap_mask);
378062306a36Sopenharmony_ci		dma_cap_set(DMA_PQ, adev->common.cap_mask);
378162306a36Sopenharmony_ci		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
378262306a36Sopenharmony_ci		adev->common.cap_mask = adev->common.cap_mask;
378362306a36Sopenharmony_ci		break;
378462306a36Sopenharmony_ci	}
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	/* Set base routines */
378762306a36Sopenharmony_ci	adev->common.device_alloc_chan_resources =
378862306a36Sopenharmony_ci				ppc440spe_adma_alloc_chan_resources;
378962306a36Sopenharmony_ci	adev->common.device_free_chan_resources =
379062306a36Sopenharmony_ci				ppc440spe_adma_free_chan_resources;
379162306a36Sopenharmony_ci	adev->common.device_tx_status = ppc440spe_adma_tx_status;
379262306a36Sopenharmony_ci	adev->common.device_issue_pending = ppc440spe_adma_issue_pending;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	/* Set prep routines based on capability */
379562306a36Sopenharmony_ci	if (dma_has_cap(DMA_MEMCPY, adev->common.cap_mask)) {
379662306a36Sopenharmony_ci		adev->common.device_prep_dma_memcpy =
379762306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_memcpy;
379862306a36Sopenharmony_ci	}
379962306a36Sopenharmony_ci	if (dma_has_cap(DMA_XOR, adev->common.cap_mask)) {
380062306a36Sopenharmony_ci		adev->common.max_xor = XOR_MAX_OPS;
380162306a36Sopenharmony_ci		adev->common.device_prep_dma_xor =
380262306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_xor;
380362306a36Sopenharmony_ci	}
380462306a36Sopenharmony_ci	if (dma_has_cap(DMA_PQ, adev->common.cap_mask)) {
380562306a36Sopenharmony_ci		switch (adev->id) {
380662306a36Sopenharmony_ci		case PPC440SPE_DMA0_ID:
380762306a36Sopenharmony_ci			dma_set_maxpq(&adev->common,
380862306a36Sopenharmony_ci				DMA0_FIFO_SIZE / sizeof(struct dma_cdb), 0);
380962306a36Sopenharmony_ci			break;
381062306a36Sopenharmony_ci		case PPC440SPE_DMA1_ID:
381162306a36Sopenharmony_ci			dma_set_maxpq(&adev->common,
381262306a36Sopenharmony_ci				DMA1_FIFO_SIZE / sizeof(struct dma_cdb), 0);
381362306a36Sopenharmony_ci			break;
381462306a36Sopenharmony_ci		case PPC440SPE_XOR_ID:
381562306a36Sopenharmony_ci			adev->common.max_pq = XOR_MAX_OPS * 3;
381662306a36Sopenharmony_ci			break;
381762306a36Sopenharmony_ci		}
381862306a36Sopenharmony_ci		adev->common.device_prep_dma_pq =
381962306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_pq;
382062306a36Sopenharmony_ci	}
382162306a36Sopenharmony_ci	if (dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask)) {
382262306a36Sopenharmony_ci		switch (adev->id) {
382362306a36Sopenharmony_ci		case PPC440SPE_DMA0_ID:
382462306a36Sopenharmony_ci			adev->common.max_pq = DMA0_FIFO_SIZE /
382562306a36Sopenharmony_ci						sizeof(struct dma_cdb);
382662306a36Sopenharmony_ci			break;
382762306a36Sopenharmony_ci		case PPC440SPE_DMA1_ID:
382862306a36Sopenharmony_ci			adev->common.max_pq = DMA1_FIFO_SIZE /
382962306a36Sopenharmony_ci						sizeof(struct dma_cdb);
383062306a36Sopenharmony_ci			break;
383162306a36Sopenharmony_ci		}
383262306a36Sopenharmony_ci		adev->common.device_prep_dma_pq_val =
383362306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_pqzero_sum;
383462306a36Sopenharmony_ci	}
383562306a36Sopenharmony_ci	if (dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask)) {
383662306a36Sopenharmony_ci		switch (adev->id) {
383762306a36Sopenharmony_ci		case PPC440SPE_DMA0_ID:
383862306a36Sopenharmony_ci			adev->common.max_xor = DMA0_FIFO_SIZE /
383962306a36Sopenharmony_ci						sizeof(struct dma_cdb);
384062306a36Sopenharmony_ci			break;
384162306a36Sopenharmony_ci		case PPC440SPE_DMA1_ID:
384262306a36Sopenharmony_ci			adev->common.max_xor = DMA1_FIFO_SIZE /
384362306a36Sopenharmony_ci						sizeof(struct dma_cdb);
384462306a36Sopenharmony_ci			break;
384562306a36Sopenharmony_ci		}
384662306a36Sopenharmony_ci		adev->common.device_prep_dma_xor_val =
384762306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_xor_zero_sum;
384862306a36Sopenharmony_ci	}
384962306a36Sopenharmony_ci	if (dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask)) {
385062306a36Sopenharmony_ci		adev->common.device_prep_dma_interrupt =
385162306a36Sopenharmony_ci			ppc440spe_adma_prep_dma_interrupt;
385262306a36Sopenharmony_ci	}
385362306a36Sopenharmony_ci	pr_info("%s: AMCC(R) PPC440SP(E) ADMA Engine: "
385462306a36Sopenharmony_ci	  "( %s%s%s%s%s%s)\n",
385562306a36Sopenharmony_ci	  dev_name(adev->dev),
385662306a36Sopenharmony_ci	  dma_has_cap(DMA_PQ, adev->common.cap_mask) ? "pq " : "",
385762306a36Sopenharmony_ci	  dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask) ? "pq_val " : "",
385862306a36Sopenharmony_ci	  dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "",
385962306a36Sopenharmony_ci	  dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask) ? "xor_val " : "",
386062306a36Sopenharmony_ci	  dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "",
386162306a36Sopenharmony_ci	  dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask) ? "intr " : "");
386262306a36Sopenharmony_ci}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_cistatic int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
386562306a36Sopenharmony_ci				     struct ppc440spe_adma_chan *chan,
386662306a36Sopenharmony_ci				     int *initcode)
386762306a36Sopenharmony_ci{
386862306a36Sopenharmony_ci	struct platform_device *ofdev;
386962306a36Sopenharmony_ci	struct device_node *np;
387062306a36Sopenharmony_ci	int ret;
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	ofdev = container_of(adev->dev, struct platform_device, dev);
387362306a36Sopenharmony_ci	np = ofdev->dev.of_node;
387462306a36Sopenharmony_ci	if (adev->id != PPC440SPE_XOR_ID) {
387562306a36Sopenharmony_ci		adev->err_irq = irq_of_parse_and_map(np, 1);
387662306a36Sopenharmony_ci		if (!adev->err_irq) {
387762306a36Sopenharmony_ci			dev_warn(adev->dev, "no err irq resource?\n");
387862306a36Sopenharmony_ci			*initcode = PPC_ADMA_INIT_IRQ2;
387962306a36Sopenharmony_ci			adev->err_irq = -ENXIO;
388062306a36Sopenharmony_ci		} else
388162306a36Sopenharmony_ci			atomic_inc(&ppc440spe_adma_err_irq_ref);
388262306a36Sopenharmony_ci	} else {
388362306a36Sopenharmony_ci		adev->err_irq = -ENXIO;
388462306a36Sopenharmony_ci	}
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	adev->irq = irq_of_parse_and_map(np, 0);
388762306a36Sopenharmony_ci	if (!adev->irq) {
388862306a36Sopenharmony_ci		dev_err(adev->dev, "no irq resource\n");
388962306a36Sopenharmony_ci		*initcode = PPC_ADMA_INIT_IRQ1;
389062306a36Sopenharmony_ci		ret = -ENXIO;
389162306a36Sopenharmony_ci		goto err_irq_map;
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci	dev_dbg(adev->dev, "irq %d, err irq %d\n",
389462306a36Sopenharmony_ci		adev->irq, adev->err_irq);
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	ret = request_irq(adev->irq, ppc440spe_adma_eot_handler,
389762306a36Sopenharmony_ci			  0, dev_driver_string(adev->dev), chan);
389862306a36Sopenharmony_ci	if (ret) {
389962306a36Sopenharmony_ci		dev_err(adev->dev, "can't request irq %d\n",
390062306a36Sopenharmony_ci			adev->irq);
390162306a36Sopenharmony_ci		*initcode = PPC_ADMA_INIT_IRQ1;
390262306a36Sopenharmony_ci		ret = -EIO;
390362306a36Sopenharmony_ci		goto err_req1;
390462306a36Sopenharmony_ci	}
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	/* only DMA engines have a separate error IRQ
390762306a36Sopenharmony_ci	 * so it's Ok if err_irq < 0 in XOR engine case.
390862306a36Sopenharmony_ci	 */
390962306a36Sopenharmony_ci	if (adev->err_irq > 0) {
391062306a36Sopenharmony_ci		/* both DMA engines share common error IRQ */
391162306a36Sopenharmony_ci		ret = request_irq(adev->err_irq,
391262306a36Sopenharmony_ci				  ppc440spe_adma_err_handler,
391362306a36Sopenharmony_ci				  IRQF_SHARED,
391462306a36Sopenharmony_ci				  dev_driver_string(adev->dev),
391562306a36Sopenharmony_ci				  chan);
391662306a36Sopenharmony_ci		if (ret) {
391762306a36Sopenharmony_ci			dev_err(adev->dev, "can't request irq %d\n",
391862306a36Sopenharmony_ci				adev->err_irq);
391962306a36Sopenharmony_ci			*initcode = PPC_ADMA_INIT_IRQ2;
392062306a36Sopenharmony_ci			ret = -EIO;
392162306a36Sopenharmony_ci			goto err_req2;
392262306a36Sopenharmony_ci		}
392362306a36Sopenharmony_ci	}
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci	if (adev->id == PPC440SPE_XOR_ID) {
392662306a36Sopenharmony_ci		/* enable XOR engine interrupts */
392762306a36Sopenharmony_ci		iowrite32be(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
392862306a36Sopenharmony_ci			    XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT,
392962306a36Sopenharmony_ci			    &adev->xor_reg->ier);
393062306a36Sopenharmony_ci	} else {
393162306a36Sopenharmony_ci		u32 mask, enable;
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci		np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
393462306a36Sopenharmony_ci		if (!np) {
393562306a36Sopenharmony_ci			pr_err("%s: can't find I2O device tree node\n",
393662306a36Sopenharmony_ci				__func__);
393762306a36Sopenharmony_ci			ret = -ENODEV;
393862306a36Sopenharmony_ci			goto err_req2;
393962306a36Sopenharmony_ci		}
394062306a36Sopenharmony_ci		adev->i2o_reg = of_iomap(np, 0);
394162306a36Sopenharmony_ci		if (!adev->i2o_reg) {
394262306a36Sopenharmony_ci			pr_err("%s: failed to map I2O registers\n", __func__);
394362306a36Sopenharmony_ci			of_node_put(np);
394462306a36Sopenharmony_ci			ret = -EINVAL;
394562306a36Sopenharmony_ci			goto err_req2;
394662306a36Sopenharmony_ci		}
394762306a36Sopenharmony_ci		of_node_put(np);
394862306a36Sopenharmony_ci		/* Unmask 'CS FIFO Attention' interrupts and
394962306a36Sopenharmony_ci		 * enable generating interrupts on errors
395062306a36Sopenharmony_ci		 */
395162306a36Sopenharmony_ci		enable = (adev->id == PPC440SPE_DMA0_ID) ?
395262306a36Sopenharmony_ci			 ~(I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
395362306a36Sopenharmony_ci			 ~(I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
395462306a36Sopenharmony_ci		mask = ioread32(&adev->i2o_reg->iopim) & enable;
395562306a36Sopenharmony_ci		iowrite32(mask, &adev->i2o_reg->iopim);
395662306a36Sopenharmony_ci	}
395762306a36Sopenharmony_ci	return 0;
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_cierr_req2:
396062306a36Sopenharmony_ci	free_irq(adev->irq, chan);
396162306a36Sopenharmony_cierr_req1:
396262306a36Sopenharmony_ci	irq_dispose_mapping(adev->irq);
396362306a36Sopenharmony_cierr_irq_map:
396462306a36Sopenharmony_ci	if (adev->err_irq > 0) {
396562306a36Sopenharmony_ci		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref))
396662306a36Sopenharmony_ci			irq_dispose_mapping(adev->err_irq);
396762306a36Sopenharmony_ci	}
396862306a36Sopenharmony_ci	return ret;
396962306a36Sopenharmony_ci}
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_cistatic void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
397262306a36Sopenharmony_ci					struct ppc440spe_adma_chan *chan)
397362306a36Sopenharmony_ci{
397462306a36Sopenharmony_ci	u32 mask, disable;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	if (adev->id == PPC440SPE_XOR_ID) {
397762306a36Sopenharmony_ci		/* disable XOR engine interrupts */
397862306a36Sopenharmony_ci		mask = ioread32be(&adev->xor_reg->ier);
397962306a36Sopenharmony_ci		mask &= ~(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
398062306a36Sopenharmony_ci			  XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT);
398162306a36Sopenharmony_ci		iowrite32be(mask, &adev->xor_reg->ier);
398262306a36Sopenharmony_ci	} else {
398362306a36Sopenharmony_ci		/* disable DMAx engine interrupts */
398462306a36Sopenharmony_ci		disable = (adev->id == PPC440SPE_DMA0_ID) ?
398562306a36Sopenharmony_ci			  (I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
398662306a36Sopenharmony_ci			  (I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
398762306a36Sopenharmony_ci		mask = ioread32(&adev->i2o_reg->iopim) | disable;
398862306a36Sopenharmony_ci		iowrite32(mask, &adev->i2o_reg->iopim);
398962306a36Sopenharmony_ci	}
399062306a36Sopenharmony_ci	free_irq(adev->irq, chan);
399162306a36Sopenharmony_ci	irq_dispose_mapping(adev->irq);
399262306a36Sopenharmony_ci	if (adev->err_irq > 0) {
399362306a36Sopenharmony_ci		free_irq(adev->err_irq, chan);
399462306a36Sopenharmony_ci		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) {
399562306a36Sopenharmony_ci			irq_dispose_mapping(adev->err_irq);
399662306a36Sopenharmony_ci			iounmap(adev->i2o_reg);
399762306a36Sopenharmony_ci		}
399862306a36Sopenharmony_ci	}
399962306a36Sopenharmony_ci}
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci/**
400262306a36Sopenharmony_ci * ppc440spe_adma_probe - probe the asynch device
400362306a36Sopenharmony_ci */
400462306a36Sopenharmony_cistatic int ppc440spe_adma_probe(struct platform_device *ofdev)
400562306a36Sopenharmony_ci{
400662306a36Sopenharmony_ci	struct device_node *np = ofdev->dev.of_node;
400762306a36Sopenharmony_ci	struct resource res;
400862306a36Sopenharmony_ci	struct ppc440spe_adma_device *adev;
400962306a36Sopenharmony_ci	struct ppc440spe_adma_chan *chan;
401062306a36Sopenharmony_ci	struct ppc_dma_chan_ref *ref, *_ref;
401162306a36Sopenharmony_ci	int ret = 0, initcode = PPC_ADMA_INIT_OK;
401262306a36Sopenharmony_ci	const u32 *idx;
401362306a36Sopenharmony_ci	int len;
401462306a36Sopenharmony_ci	void *regs;
401562306a36Sopenharmony_ci	u32 id, pool_size;
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
401862306a36Sopenharmony_ci		id = PPC440SPE_XOR_ID;
401962306a36Sopenharmony_ci		/* As far as the XOR engine is concerned, it does not
402062306a36Sopenharmony_ci		 * use FIFOs but uses linked list. So there is no dependency
402162306a36Sopenharmony_ci		 * between pool size to allocate and the engine configuration.
402262306a36Sopenharmony_ci		 */
402362306a36Sopenharmony_ci		pool_size = PAGE_SIZE << 1;
402462306a36Sopenharmony_ci	} else {
402562306a36Sopenharmony_ci		/* it is DMA0 or DMA1 */
402662306a36Sopenharmony_ci		idx = of_get_property(np, "cell-index", &len);
402762306a36Sopenharmony_ci		if (!idx || (len != sizeof(u32))) {
402862306a36Sopenharmony_ci			dev_err(&ofdev->dev, "Device node %pOF has missing "
402962306a36Sopenharmony_ci				"or invalid cell-index property\n",
403062306a36Sopenharmony_ci				np);
403162306a36Sopenharmony_ci			return -EINVAL;
403262306a36Sopenharmony_ci		}
403362306a36Sopenharmony_ci		id = *idx;
403462306a36Sopenharmony_ci		/* DMA0,1 engines use FIFO to maintain CDBs, so we
403562306a36Sopenharmony_ci		 * should allocate the pool accordingly to size of this
403662306a36Sopenharmony_ci		 * FIFO. Thus, the pool size depends on the FIFO depth:
403762306a36Sopenharmony_ci		 * how much CDBs pointers the FIFO may contain then so
403862306a36Sopenharmony_ci		 * much CDBs we should provide in the pool.
403962306a36Sopenharmony_ci		 * That is
404062306a36Sopenharmony_ci		 *   CDB size = 32B;
404162306a36Sopenharmony_ci		 *   CDBs number = (DMA0_FIFO_SIZE >> 3);
404262306a36Sopenharmony_ci		 *   Pool size = CDBs number * CDB size =
404362306a36Sopenharmony_ci		 *      = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2.
404462306a36Sopenharmony_ci		 */
404562306a36Sopenharmony_ci		pool_size = (id == PPC440SPE_DMA0_ID) ?
404662306a36Sopenharmony_ci			    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
404762306a36Sopenharmony_ci		pool_size <<= 2;
404862306a36Sopenharmony_ci	}
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci	if (of_address_to_resource(np, 0, &res)) {
405162306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to get memory resource\n");
405262306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_MEMRES;
405362306a36Sopenharmony_ci		ret = -ENODEV;
405462306a36Sopenharmony_ci		goto out;
405562306a36Sopenharmony_ci	}
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	if (!request_mem_region(res.start, resource_size(&res),
405862306a36Sopenharmony_ci				dev_driver_string(&ofdev->dev))) {
405962306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to request memory region %pR\n",
406062306a36Sopenharmony_ci			&res);
406162306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_MEMREG;
406262306a36Sopenharmony_ci		ret = -EBUSY;
406362306a36Sopenharmony_ci		goto out;
406462306a36Sopenharmony_ci	}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	/* create a device */
406762306a36Sopenharmony_ci	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
406862306a36Sopenharmony_ci	if (!adev) {
406962306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_ALLOC;
407062306a36Sopenharmony_ci		ret = -ENOMEM;
407162306a36Sopenharmony_ci		goto err_adev_alloc;
407262306a36Sopenharmony_ci	}
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	adev->id = id;
407562306a36Sopenharmony_ci	adev->pool_size = pool_size;
407662306a36Sopenharmony_ci	/* allocate coherent memory for hardware descriptors */
407762306a36Sopenharmony_ci	adev->dma_desc_pool_virt = dma_alloc_coherent(&ofdev->dev,
407862306a36Sopenharmony_ci					adev->pool_size, &adev->dma_desc_pool,
407962306a36Sopenharmony_ci					GFP_KERNEL);
408062306a36Sopenharmony_ci	if (adev->dma_desc_pool_virt == NULL) {
408162306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to allocate %d bytes of coherent "
408262306a36Sopenharmony_ci			"memory for hardware descriptors\n",
408362306a36Sopenharmony_ci			adev->pool_size);
408462306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_COHERENT;
408562306a36Sopenharmony_ci		ret = -ENOMEM;
408662306a36Sopenharmony_ci		goto err_dma_alloc;
408762306a36Sopenharmony_ci	}
408862306a36Sopenharmony_ci	dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
408962306a36Sopenharmony_ci		adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci	regs = ioremap(res.start, resource_size(&res));
409262306a36Sopenharmony_ci	if (!regs) {
409362306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to ioremap regs!\n");
409462306a36Sopenharmony_ci		ret = -ENOMEM;
409562306a36Sopenharmony_ci		goto err_regs_alloc;
409662306a36Sopenharmony_ci	}
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci	if (adev->id == PPC440SPE_XOR_ID) {
409962306a36Sopenharmony_ci		adev->xor_reg = regs;
410062306a36Sopenharmony_ci		/* Reset XOR */
410162306a36Sopenharmony_ci		iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr);
410262306a36Sopenharmony_ci		iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr);
410362306a36Sopenharmony_ci	} else {
410462306a36Sopenharmony_ci		size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ?
410562306a36Sopenharmony_ci				   DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
410662306a36Sopenharmony_ci		adev->dma_reg = regs;
410762306a36Sopenharmony_ci		/* DMAx_FIFO_SIZE is defined in bytes,
410862306a36Sopenharmony_ci		 * <fsiz> - is defined in number of CDB pointers (8byte).
410962306a36Sopenharmony_ci		 * DMA FIFO Length = CSlength + CPlength, where
411062306a36Sopenharmony_ci		 * CSlength = CPlength = (fsiz + 1) * 8.
411162306a36Sopenharmony_ci		 */
411262306a36Sopenharmony_ci		iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2),
411362306a36Sopenharmony_ci			  &adev->dma_reg->fsiz);
411462306a36Sopenharmony_ci		/* Configure DMA engine */
411562306a36Sopenharmony_ci		iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN,
411662306a36Sopenharmony_ci			  &adev->dma_reg->cfg);
411762306a36Sopenharmony_ci		/* Clear Status */
411862306a36Sopenharmony_ci		iowrite32(~0, &adev->dma_reg->dsts);
411962306a36Sopenharmony_ci	}
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	adev->dev = &ofdev->dev;
412262306a36Sopenharmony_ci	adev->common.dev = &ofdev->dev;
412362306a36Sopenharmony_ci	INIT_LIST_HEAD(&adev->common.channels);
412462306a36Sopenharmony_ci	platform_set_drvdata(ofdev, adev);
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_ci	/* create a channel */
412762306a36Sopenharmony_ci	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
412862306a36Sopenharmony_ci	if (!chan) {
412962306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_CHANNEL;
413062306a36Sopenharmony_ci		ret = -ENOMEM;
413162306a36Sopenharmony_ci		goto err_chan_alloc;
413262306a36Sopenharmony_ci	}
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	spin_lock_init(&chan->lock);
413562306a36Sopenharmony_ci	INIT_LIST_HEAD(&chan->chain);
413662306a36Sopenharmony_ci	INIT_LIST_HEAD(&chan->all_slots);
413762306a36Sopenharmony_ci	chan->device = adev;
413862306a36Sopenharmony_ci	chan->common.device = &adev->common;
413962306a36Sopenharmony_ci	dma_cookie_init(&chan->common);
414062306a36Sopenharmony_ci	list_add_tail(&chan->common.device_node, &adev->common.channels);
414162306a36Sopenharmony_ci	tasklet_setup(&chan->irq_tasklet, ppc440spe_adma_tasklet);
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	/* allocate and map helper pages for async validation or
414462306a36Sopenharmony_ci	 * async_mult/async_sum_product operations on DMA0/1.
414562306a36Sopenharmony_ci	 */
414662306a36Sopenharmony_ci	if (adev->id != PPC440SPE_XOR_ID) {
414762306a36Sopenharmony_ci		chan->pdest_page = alloc_page(GFP_KERNEL);
414862306a36Sopenharmony_ci		chan->qdest_page = alloc_page(GFP_KERNEL);
414962306a36Sopenharmony_ci		if (!chan->pdest_page ||
415062306a36Sopenharmony_ci		    !chan->qdest_page) {
415162306a36Sopenharmony_ci			if (chan->pdest_page)
415262306a36Sopenharmony_ci				__free_page(chan->pdest_page);
415362306a36Sopenharmony_ci			if (chan->qdest_page)
415462306a36Sopenharmony_ci				__free_page(chan->qdest_page);
415562306a36Sopenharmony_ci			ret = -ENOMEM;
415662306a36Sopenharmony_ci			goto err_page_alloc;
415762306a36Sopenharmony_ci		}
415862306a36Sopenharmony_ci		chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0,
415962306a36Sopenharmony_ci					   PAGE_SIZE, DMA_BIDIRECTIONAL);
416062306a36Sopenharmony_ci		chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0,
416162306a36Sopenharmony_ci					   PAGE_SIZE, DMA_BIDIRECTIONAL);
416262306a36Sopenharmony_ci	}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	ref = kmalloc(sizeof(*ref), GFP_KERNEL);
416562306a36Sopenharmony_ci	if (ref) {
416662306a36Sopenharmony_ci		ref->chan = &chan->common;
416762306a36Sopenharmony_ci		INIT_LIST_HEAD(&ref->node);
416862306a36Sopenharmony_ci		list_add_tail(&ref->node, &ppc440spe_adma_chan_list);
416962306a36Sopenharmony_ci	} else {
417062306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to allocate channel reference!\n");
417162306a36Sopenharmony_ci		ret = -ENOMEM;
417262306a36Sopenharmony_ci		goto err_ref_alloc;
417362306a36Sopenharmony_ci	}
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	ret = ppc440spe_adma_setup_irqs(adev, chan, &initcode);
417662306a36Sopenharmony_ci	if (ret)
417762306a36Sopenharmony_ci		goto err_irq;
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci	ppc440spe_adma_init_capabilities(adev);
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	ret = dma_async_device_register(&adev->common);
418262306a36Sopenharmony_ci	if (ret) {
418362306a36Sopenharmony_ci		initcode = PPC_ADMA_INIT_REGISTER;
418462306a36Sopenharmony_ci		dev_err(&ofdev->dev, "failed to register dma device\n");
418562306a36Sopenharmony_ci		goto err_dev_reg;
418662306a36Sopenharmony_ci	}
418762306a36Sopenharmony_ci
418862306a36Sopenharmony_ci	goto out;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_cierr_dev_reg:
419162306a36Sopenharmony_ci	ppc440spe_adma_release_irqs(adev, chan);
419262306a36Sopenharmony_cierr_irq:
419362306a36Sopenharmony_ci	list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list, node) {
419462306a36Sopenharmony_ci		if (chan == to_ppc440spe_adma_chan(ref->chan)) {
419562306a36Sopenharmony_ci			list_del(&ref->node);
419662306a36Sopenharmony_ci			kfree(ref);
419762306a36Sopenharmony_ci		}
419862306a36Sopenharmony_ci	}
419962306a36Sopenharmony_cierr_ref_alloc:
420062306a36Sopenharmony_ci	if (adev->id != PPC440SPE_XOR_ID) {
420162306a36Sopenharmony_ci		dma_unmap_page(&ofdev->dev, chan->pdest,
420262306a36Sopenharmony_ci			       PAGE_SIZE, DMA_BIDIRECTIONAL);
420362306a36Sopenharmony_ci		dma_unmap_page(&ofdev->dev, chan->qdest,
420462306a36Sopenharmony_ci			       PAGE_SIZE, DMA_BIDIRECTIONAL);
420562306a36Sopenharmony_ci		__free_page(chan->pdest_page);
420662306a36Sopenharmony_ci		__free_page(chan->qdest_page);
420762306a36Sopenharmony_ci	}
420862306a36Sopenharmony_cierr_page_alloc:
420962306a36Sopenharmony_ci	kfree(chan);
421062306a36Sopenharmony_cierr_chan_alloc:
421162306a36Sopenharmony_ci	if (adev->id == PPC440SPE_XOR_ID)
421262306a36Sopenharmony_ci		iounmap(adev->xor_reg);
421362306a36Sopenharmony_ci	else
421462306a36Sopenharmony_ci		iounmap(adev->dma_reg);
421562306a36Sopenharmony_cierr_regs_alloc:
421662306a36Sopenharmony_ci	dma_free_coherent(adev->dev, adev->pool_size,
421762306a36Sopenharmony_ci			  adev->dma_desc_pool_virt,
421862306a36Sopenharmony_ci			  adev->dma_desc_pool);
421962306a36Sopenharmony_cierr_dma_alloc:
422062306a36Sopenharmony_ci	kfree(adev);
422162306a36Sopenharmony_cierr_adev_alloc:
422262306a36Sopenharmony_ci	release_mem_region(res.start, resource_size(&res));
422362306a36Sopenharmony_ciout:
422462306a36Sopenharmony_ci	if (id < PPC440SPE_ADMA_ENGINES_NUM)
422562306a36Sopenharmony_ci		ppc440spe_adma_devices[id] = initcode;
422662306a36Sopenharmony_ci
422762306a36Sopenharmony_ci	return ret;
422862306a36Sopenharmony_ci}
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_ci/**
423162306a36Sopenharmony_ci * ppc440spe_adma_remove - remove the asynch device
423262306a36Sopenharmony_ci */
423362306a36Sopenharmony_cistatic int ppc440spe_adma_remove(struct platform_device *ofdev)
423462306a36Sopenharmony_ci{
423562306a36Sopenharmony_ci	struct ppc440spe_adma_device *adev = platform_get_drvdata(ofdev);
423662306a36Sopenharmony_ci	struct device_node *np = ofdev->dev.of_node;
423762306a36Sopenharmony_ci	struct resource res;
423862306a36Sopenharmony_ci	struct dma_chan *chan, *_chan;
423962306a36Sopenharmony_ci	struct ppc_dma_chan_ref *ref, *_ref;
424062306a36Sopenharmony_ci	struct ppc440spe_adma_chan *ppc440spe_chan;
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
424362306a36Sopenharmony_ci		ppc440spe_adma_devices[adev->id] = -1;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	dma_async_device_unregister(&adev->common);
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &adev->common.channels,
424862306a36Sopenharmony_ci				 device_node) {
424962306a36Sopenharmony_ci		ppc440spe_chan = to_ppc440spe_adma_chan(chan);
425062306a36Sopenharmony_ci		ppc440spe_adma_release_irqs(adev, ppc440spe_chan);
425162306a36Sopenharmony_ci		tasklet_kill(&ppc440spe_chan->irq_tasklet);
425262306a36Sopenharmony_ci		if (adev->id != PPC440SPE_XOR_ID) {
425362306a36Sopenharmony_ci			dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest,
425462306a36Sopenharmony_ci					PAGE_SIZE, DMA_BIDIRECTIONAL);
425562306a36Sopenharmony_ci			dma_unmap_page(&ofdev->dev, ppc440spe_chan->qdest,
425662306a36Sopenharmony_ci					PAGE_SIZE, DMA_BIDIRECTIONAL);
425762306a36Sopenharmony_ci			__free_page(ppc440spe_chan->pdest_page);
425862306a36Sopenharmony_ci			__free_page(ppc440spe_chan->qdest_page);
425962306a36Sopenharmony_ci		}
426062306a36Sopenharmony_ci		list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list,
426162306a36Sopenharmony_ci					 node) {
426262306a36Sopenharmony_ci			if (ppc440spe_chan ==
426362306a36Sopenharmony_ci			    to_ppc440spe_adma_chan(ref->chan)) {
426462306a36Sopenharmony_ci				list_del(&ref->node);
426562306a36Sopenharmony_ci				kfree(ref);
426662306a36Sopenharmony_ci			}
426762306a36Sopenharmony_ci		}
426862306a36Sopenharmony_ci		list_del(&chan->device_node);
426962306a36Sopenharmony_ci		kfree(ppc440spe_chan);
427062306a36Sopenharmony_ci	}
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ci	dma_free_coherent(adev->dev, adev->pool_size,
427362306a36Sopenharmony_ci			  adev->dma_desc_pool_virt, adev->dma_desc_pool);
427462306a36Sopenharmony_ci	if (adev->id == PPC440SPE_XOR_ID)
427562306a36Sopenharmony_ci		iounmap(adev->xor_reg);
427662306a36Sopenharmony_ci	else
427762306a36Sopenharmony_ci		iounmap(adev->dma_reg);
427862306a36Sopenharmony_ci	of_address_to_resource(np, 0, &res);
427962306a36Sopenharmony_ci	release_mem_region(res.start, resource_size(&res));
428062306a36Sopenharmony_ci	kfree(adev);
428162306a36Sopenharmony_ci	return 0;
428262306a36Sopenharmony_ci}
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ci/*
428562306a36Sopenharmony_ci * /sys driver interface to enable h/w RAID-6 capabilities
428662306a36Sopenharmony_ci * Files created in e.g. /sys/devices/plb.0/400100100.dma0/driver/
428762306a36Sopenharmony_ci * directory are "devices", "enable" and "poly".
428862306a36Sopenharmony_ci * "devices" shows available engines.
428962306a36Sopenharmony_ci * "enable" is used to enable RAID-6 capabilities or to check
429062306a36Sopenharmony_ci * whether these has been activated.
429162306a36Sopenharmony_ci * "poly" allows setting/checking used polynomial (for PPC440SPe only).
429262306a36Sopenharmony_ci */
429362306a36Sopenharmony_ci
429462306a36Sopenharmony_cistatic ssize_t devices_show(struct device_driver *dev, char *buf)
429562306a36Sopenharmony_ci{
429662306a36Sopenharmony_ci	ssize_t size = 0;
429762306a36Sopenharmony_ci	int i;
429862306a36Sopenharmony_ci
429962306a36Sopenharmony_ci	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) {
430062306a36Sopenharmony_ci		if (ppc440spe_adma_devices[i] == -1)
430162306a36Sopenharmony_ci			continue;
430262306a36Sopenharmony_ci		size += sysfs_emit_at(buf, size, "PPC440SP(E)-ADMA.%d: %s\n",
430362306a36Sopenharmony_ci				     i, ppc_adma_errors[ppc440spe_adma_devices[i]]);
430462306a36Sopenharmony_ci	}
430562306a36Sopenharmony_ci	return size;
430662306a36Sopenharmony_ci}
430762306a36Sopenharmony_cistatic DRIVER_ATTR_RO(devices);
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_cistatic ssize_t enable_show(struct device_driver *dev, char *buf)
431062306a36Sopenharmony_ci{
431162306a36Sopenharmony_ci	return sysfs_emit(buf, "PPC440SP(e) RAID-6 capabilities are %sABLED.\n",
431262306a36Sopenharmony_ci			  ppc440spe_r6_enabled ? "EN" : "DIS");
431362306a36Sopenharmony_ci}
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_cistatic ssize_t enable_store(struct device_driver *dev, const char *buf,
431662306a36Sopenharmony_ci			    size_t count)
431762306a36Sopenharmony_ci{
431862306a36Sopenharmony_ci	unsigned long val;
431962306a36Sopenharmony_ci	int err;
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci	if (!count || count > 11)
432262306a36Sopenharmony_ci		return -EINVAL;
432362306a36Sopenharmony_ci
432462306a36Sopenharmony_ci	if (!ppc440spe_r6_tchan)
432562306a36Sopenharmony_ci		return -EFAULT;
432662306a36Sopenharmony_ci
432762306a36Sopenharmony_ci	/* Write a key */
432862306a36Sopenharmony_ci	err = kstrtoul(buf, 16, &val);
432962306a36Sopenharmony_ci	if (err)
433062306a36Sopenharmony_ci		return err;
433162306a36Sopenharmony_ci
433262306a36Sopenharmony_ci	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_XORBA, val);
433362306a36Sopenharmony_ci	isync();
433462306a36Sopenharmony_ci
433562306a36Sopenharmony_ci	/* Verify whether it really works now */
433662306a36Sopenharmony_ci	if (ppc440spe_test_raid6(ppc440spe_r6_tchan) == 0) {
433762306a36Sopenharmony_ci		pr_info("PPC440SP(e) RAID-6 has been activated "
433862306a36Sopenharmony_ci			"successfully\n");
433962306a36Sopenharmony_ci		ppc440spe_r6_enabled = 1;
434062306a36Sopenharmony_ci	} else {
434162306a36Sopenharmony_ci		pr_info("PPC440SP(e) RAID-6 hasn't been activated!"
434262306a36Sopenharmony_ci			" Error key ?\n");
434362306a36Sopenharmony_ci		ppc440spe_r6_enabled = 0;
434462306a36Sopenharmony_ci	}
434562306a36Sopenharmony_ci	return count;
434662306a36Sopenharmony_ci}
434762306a36Sopenharmony_cistatic DRIVER_ATTR_RW(enable);
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_cistatic ssize_t poly_show(struct device_driver *dev, char *buf)
435062306a36Sopenharmony_ci{
435162306a36Sopenharmony_ci	ssize_t size = 0;
435262306a36Sopenharmony_ci	u32 reg;
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci#ifdef CONFIG_440SP
435562306a36Sopenharmony_ci	/* 440SP has fixed polynomial */
435662306a36Sopenharmony_ci	reg = 0x4d;
435762306a36Sopenharmony_ci#else
435862306a36Sopenharmony_ci	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
435962306a36Sopenharmony_ci	reg >>= MQ0_CFBHL_POLY;
436062306a36Sopenharmony_ci	reg &= 0xFF;
436162306a36Sopenharmony_ci#endif
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci	size = sysfs_emit(buf, "PPC440SP(e) RAID-6 driver "
436462306a36Sopenharmony_ci			"uses 0x1%02x polynomial.\n", reg);
436562306a36Sopenharmony_ci	return size;
436662306a36Sopenharmony_ci}
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_cistatic ssize_t poly_store(struct device_driver *dev, const char *buf,
436962306a36Sopenharmony_ci			  size_t count)
437062306a36Sopenharmony_ci{
437162306a36Sopenharmony_ci	unsigned long reg, val;
437262306a36Sopenharmony_ci	int err;
437362306a36Sopenharmony_ci#ifdef CONFIG_440SP
437462306a36Sopenharmony_ci	/* 440SP uses default 0x14D polynomial only */
437562306a36Sopenharmony_ci	return -EINVAL;
437662306a36Sopenharmony_ci#endif
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci	if (!count || count > 6)
437962306a36Sopenharmony_ci		return -EINVAL;
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ci	/* e.g., 0x14D or 0x11D */
438262306a36Sopenharmony_ci	err = kstrtoul(buf, 16, &val);
438362306a36Sopenharmony_ci	if (err)
438462306a36Sopenharmony_ci		return err;
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	if (val & ~0x1FF)
438762306a36Sopenharmony_ci		return -EINVAL;
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci	val &= 0xFF;
439062306a36Sopenharmony_ci	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
439162306a36Sopenharmony_ci	reg &= ~(0xFF << MQ0_CFBHL_POLY);
439262306a36Sopenharmony_ci	reg |= val << MQ0_CFBHL_POLY;
439362306a36Sopenharmony_ci	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, reg);
439462306a36Sopenharmony_ci
439562306a36Sopenharmony_ci	return count;
439662306a36Sopenharmony_ci}
439762306a36Sopenharmony_cistatic DRIVER_ATTR_RW(poly);
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci/*
440062306a36Sopenharmony_ci * Common initialisation for RAID engines; allocate memory for
440162306a36Sopenharmony_ci * DMAx FIFOs, perform configuration common for all DMA engines.
440262306a36Sopenharmony_ci * Further DMA engine specific configuration is done at probe time.
440362306a36Sopenharmony_ci */
440462306a36Sopenharmony_cistatic int ppc440spe_configure_raid_devices(void)
440562306a36Sopenharmony_ci{
440662306a36Sopenharmony_ci	struct device_node *np;
440762306a36Sopenharmony_ci	struct resource i2o_res;
440862306a36Sopenharmony_ci	struct i2o_regs __iomem *i2o_reg;
440962306a36Sopenharmony_ci	dcr_host_t i2o_dcr_host;
441062306a36Sopenharmony_ci	unsigned int dcr_base, dcr_len;
441162306a36Sopenharmony_ci	int i, ret;
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
441462306a36Sopenharmony_ci	if (!np) {
441562306a36Sopenharmony_ci		pr_err("%s: can't find I2O device tree node\n",
441662306a36Sopenharmony_ci			__func__);
441762306a36Sopenharmony_ci		return -ENODEV;
441862306a36Sopenharmony_ci	}
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci	if (of_address_to_resource(np, 0, &i2o_res)) {
442162306a36Sopenharmony_ci		of_node_put(np);
442262306a36Sopenharmony_ci		return -EINVAL;
442362306a36Sopenharmony_ci	}
442462306a36Sopenharmony_ci
442562306a36Sopenharmony_ci	i2o_reg = of_iomap(np, 0);
442662306a36Sopenharmony_ci	if (!i2o_reg) {
442762306a36Sopenharmony_ci		pr_err("%s: failed to map I2O registers\n", __func__);
442862306a36Sopenharmony_ci		of_node_put(np);
442962306a36Sopenharmony_ci		return -EINVAL;
443062306a36Sopenharmony_ci	}
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci	/* Get I2O DCRs base */
443362306a36Sopenharmony_ci	dcr_base = dcr_resource_start(np, 0);
443462306a36Sopenharmony_ci	dcr_len = dcr_resource_len(np, 0);
443562306a36Sopenharmony_ci	if (!dcr_base && !dcr_len) {
443662306a36Sopenharmony_ci		pr_err("%pOF: can't get DCR registers base/len!\n", np);
443762306a36Sopenharmony_ci		of_node_put(np);
443862306a36Sopenharmony_ci		iounmap(i2o_reg);
443962306a36Sopenharmony_ci		return -ENODEV;
444062306a36Sopenharmony_ci	}
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci	i2o_dcr_host = dcr_map(np, dcr_base, dcr_len);
444362306a36Sopenharmony_ci	if (!DCR_MAP_OK(i2o_dcr_host)) {
444462306a36Sopenharmony_ci		pr_err("%pOF: failed to map DCRs!\n", np);
444562306a36Sopenharmony_ci		of_node_put(np);
444662306a36Sopenharmony_ci		iounmap(i2o_reg);
444762306a36Sopenharmony_ci		return -ENODEV;
444862306a36Sopenharmony_ci	}
444962306a36Sopenharmony_ci	of_node_put(np);
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_ci	/* Provide memory regions for DMA's FIFOs: I2O, DMA0 and DMA1 share
445262306a36Sopenharmony_ci	 * the base address of FIFO memory space.
445362306a36Sopenharmony_ci	 * Actually we need twice more physical memory than programmed in the
445462306a36Sopenharmony_ci	 * <fsiz> register (because there are two FIFOs for each DMA: CP and CS)
445562306a36Sopenharmony_ci	 */
445662306a36Sopenharmony_ci	ppc440spe_dma_fifo_buf = kmalloc((DMA0_FIFO_SIZE + DMA1_FIFO_SIZE) << 1,
445762306a36Sopenharmony_ci					 GFP_KERNEL);
445862306a36Sopenharmony_ci	if (!ppc440spe_dma_fifo_buf) {
445962306a36Sopenharmony_ci		pr_err("%s: DMA FIFO buffer allocation failed.\n", __func__);
446062306a36Sopenharmony_ci		iounmap(i2o_reg);
446162306a36Sopenharmony_ci		dcr_unmap(i2o_dcr_host, dcr_len);
446262306a36Sopenharmony_ci		return -ENOMEM;
446362306a36Sopenharmony_ci	}
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci	/*
446662306a36Sopenharmony_ci	 * Configure h/w
446762306a36Sopenharmony_ci	 */
446862306a36Sopenharmony_ci	/* Reset I2O/DMA */
446962306a36Sopenharmony_ci	mtdcri(SDR0, DCRN_SDR0_SRST, DCRN_SDR0_SRST_I2ODMA);
447062306a36Sopenharmony_ci	mtdcri(SDR0, DCRN_SDR0_SRST, 0);
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci	/* Setup the base address of mmaped registers */
447362306a36Sopenharmony_ci	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAH, (u32)(i2o_res.start >> 32));
447462306a36Sopenharmony_ci	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAL, (u32)(i2o_res.start) |
447562306a36Sopenharmony_ci						I2O_REG_ENABLE);
447662306a36Sopenharmony_ci	dcr_unmap(i2o_dcr_host, dcr_len);
447762306a36Sopenharmony_ci
447862306a36Sopenharmony_ci	/* Setup FIFO memory space base address */
447962306a36Sopenharmony_ci	iowrite32(0, &i2o_reg->ifbah);
448062306a36Sopenharmony_ci	iowrite32(((u32)__pa(ppc440spe_dma_fifo_buf)), &i2o_reg->ifbal);
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci	/* set zero FIFO size for I2O, so the whole
448362306a36Sopenharmony_ci	 * ppc440spe_dma_fifo_buf is used by DMAs.
448462306a36Sopenharmony_ci	 * DMAx_FIFOs will be configured while probe.
448562306a36Sopenharmony_ci	 */
448662306a36Sopenharmony_ci	iowrite32(0, &i2o_reg->ifsiz);
448762306a36Sopenharmony_ci	iounmap(i2o_reg);
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_ci	/* To prepare WXOR/RXOR functionality we need access to
449062306a36Sopenharmony_ci	 * Memory Queue Module DCRs (finally it will be enabled
449162306a36Sopenharmony_ci	 * via /sys interface of the ppc440spe ADMA driver).
449262306a36Sopenharmony_ci	 */
449362306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ibm,mq-440spe");
449462306a36Sopenharmony_ci	if (!np) {
449562306a36Sopenharmony_ci		pr_err("%s: can't find MQ device tree node\n",
449662306a36Sopenharmony_ci			__func__);
449762306a36Sopenharmony_ci		ret = -ENODEV;
449862306a36Sopenharmony_ci		goto out_free;
449962306a36Sopenharmony_ci	}
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci	/* Get MQ DCRs base */
450262306a36Sopenharmony_ci	dcr_base = dcr_resource_start(np, 0);
450362306a36Sopenharmony_ci	dcr_len = dcr_resource_len(np, 0);
450462306a36Sopenharmony_ci	if (!dcr_base && !dcr_len) {
450562306a36Sopenharmony_ci		pr_err("%pOF: can't get DCR registers base/len!\n", np);
450662306a36Sopenharmony_ci		ret = -ENODEV;
450762306a36Sopenharmony_ci		goto out_mq;
450862306a36Sopenharmony_ci	}
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ci	ppc440spe_mq_dcr_host = dcr_map(np, dcr_base, dcr_len);
451162306a36Sopenharmony_ci	if (!DCR_MAP_OK(ppc440spe_mq_dcr_host)) {
451262306a36Sopenharmony_ci		pr_err("%pOF: failed to map DCRs!\n", np);
451362306a36Sopenharmony_ci		ret = -ENODEV;
451462306a36Sopenharmony_ci		goto out_mq;
451562306a36Sopenharmony_ci	}
451662306a36Sopenharmony_ci	of_node_put(np);
451762306a36Sopenharmony_ci	ppc440spe_mq_dcr_len = dcr_len;
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	/* Set HB alias */
452062306a36Sopenharmony_ci	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_BAUH, DMA_CUED_XOR_HB);
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	/* Set:
452362306a36Sopenharmony_ci	 * - LL transaction passing limit to 1;
452462306a36Sopenharmony_ci	 * - Memory controller cycle limit to 1;
452562306a36Sopenharmony_ci	 * - Galois Polynomial to 0x14d (default)
452662306a36Sopenharmony_ci	 */
452762306a36Sopenharmony_ci	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL,
452862306a36Sopenharmony_ci		  (1 << MQ0_CFBHL_TPLM) | (1 << MQ0_CFBHL_HBCL) |
452962306a36Sopenharmony_ci		  (PPC440SPE_DEFAULT_POLY << MQ0_CFBHL_POLY));
453062306a36Sopenharmony_ci
453162306a36Sopenharmony_ci	atomic_set(&ppc440spe_adma_err_irq_ref, 0);
453262306a36Sopenharmony_ci	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++)
453362306a36Sopenharmony_ci		ppc440spe_adma_devices[i] = -1;
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci	return 0;
453662306a36Sopenharmony_ci
453762306a36Sopenharmony_ciout_mq:
453862306a36Sopenharmony_ci	of_node_put(np);
453962306a36Sopenharmony_ciout_free:
454062306a36Sopenharmony_ci	kfree(ppc440spe_dma_fifo_buf);
454162306a36Sopenharmony_ci	return ret;
454262306a36Sopenharmony_ci}
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_cistatic const struct of_device_id ppc440spe_adma_of_match[] = {
454562306a36Sopenharmony_ci	{ .compatible	= "ibm,dma-440spe", },
454662306a36Sopenharmony_ci	{ .compatible	= "amcc,xor-accelerator", },
454762306a36Sopenharmony_ci	{},
454862306a36Sopenharmony_ci};
454962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match);
455062306a36Sopenharmony_ci
455162306a36Sopenharmony_cistatic struct platform_driver ppc440spe_adma_driver = {
455262306a36Sopenharmony_ci	.probe = ppc440spe_adma_probe,
455362306a36Sopenharmony_ci	.remove = ppc440spe_adma_remove,
455462306a36Sopenharmony_ci	.driver = {
455562306a36Sopenharmony_ci		.name = "PPC440SP(E)-ADMA",
455662306a36Sopenharmony_ci		.of_match_table = ppc440spe_adma_of_match,
455762306a36Sopenharmony_ci	},
455862306a36Sopenharmony_ci};
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_cistatic __init int ppc440spe_adma_init(void)
456162306a36Sopenharmony_ci{
456262306a36Sopenharmony_ci	int ret;
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ci	ret = ppc440spe_configure_raid_devices();
456562306a36Sopenharmony_ci	if (ret)
456662306a36Sopenharmony_ci		return ret;
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_ci	ret = platform_driver_register(&ppc440spe_adma_driver);
456962306a36Sopenharmony_ci	if (ret) {
457062306a36Sopenharmony_ci		pr_err("%s: failed to register platform driver\n",
457162306a36Sopenharmony_ci			__func__);
457262306a36Sopenharmony_ci		goto out_reg;
457362306a36Sopenharmony_ci	}
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_ci	/* Initialization status */
457662306a36Sopenharmony_ci	ret = driver_create_file(&ppc440spe_adma_driver.driver,
457762306a36Sopenharmony_ci				 &driver_attr_devices);
457862306a36Sopenharmony_ci	if (ret)
457962306a36Sopenharmony_ci		goto out_dev;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci	/* RAID-6 h/w enable entry */
458262306a36Sopenharmony_ci	ret = driver_create_file(&ppc440spe_adma_driver.driver,
458362306a36Sopenharmony_ci				 &driver_attr_enable);
458462306a36Sopenharmony_ci	if (ret)
458562306a36Sopenharmony_ci		goto out_en;
458662306a36Sopenharmony_ci
458762306a36Sopenharmony_ci	/* GF polynomial to use */
458862306a36Sopenharmony_ci	ret = driver_create_file(&ppc440spe_adma_driver.driver,
458962306a36Sopenharmony_ci				 &driver_attr_poly);
459062306a36Sopenharmony_ci	if (!ret)
459162306a36Sopenharmony_ci		return ret;
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	driver_remove_file(&ppc440spe_adma_driver.driver,
459462306a36Sopenharmony_ci			   &driver_attr_enable);
459562306a36Sopenharmony_ciout_en:
459662306a36Sopenharmony_ci	driver_remove_file(&ppc440spe_adma_driver.driver,
459762306a36Sopenharmony_ci			   &driver_attr_devices);
459862306a36Sopenharmony_ciout_dev:
459962306a36Sopenharmony_ci	/* User will not be able to enable h/w RAID-6 */
460062306a36Sopenharmony_ci	pr_err("%s: failed to create RAID-6 driver interface\n",
460162306a36Sopenharmony_ci		__func__);
460262306a36Sopenharmony_ci	platform_driver_unregister(&ppc440spe_adma_driver);
460362306a36Sopenharmony_ciout_reg:
460462306a36Sopenharmony_ci	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
460562306a36Sopenharmony_ci	kfree(ppc440spe_dma_fifo_buf);
460662306a36Sopenharmony_ci	return ret;
460762306a36Sopenharmony_ci}
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_cistatic void __exit ppc440spe_adma_exit(void)
461062306a36Sopenharmony_ci{
461162306a36Sopenharmony_ci	driver_remove_file(&ppc440spe_adma_driver.driver,
461262306a36Sopenharmony_ci			   &driver_attr_poly);
461362306a36Sopenharmony_ci	driver_remove_file(&ppc440spe_adma_driver.driver,
461462306a36Sopenharmony_ci			   &driver_attr_enable);
461562306a36Sopenharmony_ci	driver_remove_file(&ppc440spe_adma_driver.driver,
461662306a36Sopenharmony_ci			   &driver_attr_devices);
461762306a36Sopenharmony_ci	platform_driver_unregister(&ppc440spe_adma_driver);
461862306a36Sopenharmony_ci	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
461962306a36Sopenharmony_ci	kfree(ppc440spe_dma_fifo_buf);
462062306a36Sopenharmony_ci}
462162306a36Sopenharmony_ci
462262306a36Sopenharmony_ciarch_initcall(ppc440spe_adma_init);
462362306a36Sopenharmony_cimodule_exit(ppc440spe_adma_exit);
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_ciMODULE_AUTHOR("Yuri Tikhonov <yur@emcraft.com>");
462662306a36Sopenharmony_ciMODULE_DESCRIPTION("PPC440SPE ADMA Engine Driver");
462762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4628