xref: /openbmc/linux/drivers/dma/ppc4xx/adma.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
19ab65affSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
212458ea0SAnatolij Gustschin /*
312458ea0SAnatolij Gustschin  * Copyright (C) 2006-2009 DENX Software Engineering.
412458ea0SAnatolij Gustschin  *
512458ea0SAnatolij Gustschin  * Author: Yuri Tikhonov <yur@emcraft.com>
612458ea0SAnatolij Gustschin  *
712458ea0SAnatolij Gustschin  * Further porting to arch/powerpc by
812458ea0SAnatolij Gustschin  * 	Anatolij Gustschin <agust@denx.de>
912458ea0SAnatolij Gustschin  */
1012458ea0SAnatolij Gustschin 
1112458ea0SAnatolij Gustschin /*
1212458ea0SAnatolij Gustschin  * This driver supports the asynchrounous DMA copy and RAID engines available
1312458ea0SAnatolij Gustschin  * on the AMCC PPC440SPe Processors.
1412458ea0SAnatolij Gustschin  * Based on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x)
1512458ea0SAnatolij Gustschin  * ADMA driver written by D.Williams.
1612458ea0SAnatolij Gustschin  */
1712458ea0SAnatolij Gustschin 
1812458ea0SAnatolij Gustschin #include <linux/init.h>
1912458ea0SAnatolij Gustschin #include <linux/module.h>
2012458ea0SAnatolij Gustschin #include <linux/async_tx.h>
2112458ea0SAnatolij Gustschin #include <linux/delay.h>
2212458ea0SAnatolij Gustschin #include <linux/dma-mapping.h>
2312458ea0SAnatolij Gustschin #include <linux/spinlock.h>
2412458ea0SAnatolij Gustschin #include <linux/interrupt.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
2612458ea0SAnatolij Gustschin #include <linux/uaccess.h>
2712458ea0SAnatolij Gustschin #include <linux/proc_fs.h>
2812458ea0SAnatolij Gustschin #include <linux/of.h>
29c11eede6SRob Herring #include <linux/of_address.h>
30c11eede6SRob Herring #include <linux/of_irq.h>
31*897500c7SRob Herring #include <linux/platform_device.h>
3212458ea0SAnatolij Gustschin #include <asm/dcr.h>
3312458ea0SAnatolij Gustschin #include <asm/dcr-regs.h>
3412458ea0SAnatolij Gustschin #include "adma.h"
35d2ebfb33SRussell King - ARM Linux #include "../dmaengine.h"
3612458ea0SAnatolij Gustschin 
3712458ea0SAnatolij Gustschin enum ppc_adma_init_code {
3812458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_OK = 0,
3912458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_MEMRES,
4012458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_MEMREG,
4112458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_ALLOC,
4212458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_COHERENT,
4312458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_CHANNEL,
4412458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_IRQ1,
4512458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_IRQ2,
4612458ea0SAnatolij Gustschin 	PPC_ADMA_INIT_REGISTER
4712458ea0SAnatolij Gustschin };
4812458ea0SAnatolij Gustschin 
4912458ea0SAnatolij Gustschin static char *ppc_adma_errors[] = {
5012458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_OK] = "ok",
5112458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_MEMRES] = "failed to get memory resource",
5212458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_MEMREG] = "failed to request memory region",
5312458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev "
5412458ea0SAnatolij Gustschin 				"structure",
5512458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for "
5612458ea0SAnatolij Gustschin 				   "hardware descriptors",
5712458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel",
5812458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_IRQ1] = "failed to request first irq",
5912458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_IRQ2] = "failed to request second irq",
6012458ea0SAnatolij Gustschin 	[PPC_ADMA_INIT_REGISTER] = "failed to register dma async device",
6112458ea0SAnatolij Gustschin };
6212458ea0SAnatolij Gustschin 
6312458ea0SAnatolij Gustschin static enum ppc_adma_init_code
6412458ea0SAnatolij Gustschin ppc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM];
6512458ea0SAnatolij Gustschin 
6612458ea0SAnatolij Gustschin struct ppc_dma_chan_ref {
6712458ea0SAnatolij Gustschin 	struct dma_chan *chan;
6812458ea0SAnatolij Gustschin 	struct list_head node;
6912458ea0SAnatolij Gustschin };
7012458ea0SAnatolij Gustschin 
7112458ea0SAnatolij Gustschin /* The list of channels exported by ppc440spe ADMA */
7268f35addSKrzysztof Kozlowski static struct list_head
7312458ea0SAnatolij Gustschin ppc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list);
7412458ea0SAnatolij Gustschin 
7512458ea0SAnatolij Gustschin /* This flag is set when want to refetch the xor chain in the interrupt
7612458ea0SAnatolij Gustschin  * handler
7712458ea0SAnatolij Gustschin  */
7812458ea0SAnatolij Gustschin static u32 do_xor_refetch;
7912458ea0SAnatolij Gustschin 
8012458ea0SAnatolij Gustschin /* Pointer to DMA0, DMA1 CP/CS FIFO */
8112458ea0SAnatolij Gustschin static void *ppc440spe_dma_fifo_buf;
8212458ea0SAnatolij Gustschin 
8312458ea0SAnatolij Gustschin /* Pointers to last submitted to DMA0, DMA1 CDBs */
8412458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *chan_last_sub[3];
8512458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *chan_first_cdb[3];
8612458ea0SAnatolij Gustschin 
8712458ea0SAnatolij Gustschin /* Pointer to last linked and submitted xor CB */
8812458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *xor_last_linked;
8912458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *xor_last_submit;
9012458ea0SAnatolij Gustschin 
9112458ea0SAnatolij Gustschin /* This array is used in data-check operations for storing a pattern */
9212458ea0SAnatolij Gustschin static char ppc440spe_qword[16];
9312458ea0SAnatolij Gustschin 
9412458ea0SAnatolij Gustschin static atomic_t ppc440spe_adma_err_irq_ref;
9512458ea0SAnatolij Gustschin static dcr_host_t ppc440spe_mq_dcr_host;
9612458ea0SAnatolij Gustschin static unsigned int ppc440spe_mq_dcr_len;
9712458ea0SAnatolij Gustschin 
9812458ea0SAnatolij Gustschin /* Since RXOR operations use the common register (MQ0_CF2H) for setting-up
9912458ea0SAnatolij Gustschin  * the block size in transactions, then we do not allow to activate more than
10012458ea0SAnatolij Gustschin  * only one RXOR transactions simultaneously. So use this var to store
10112458ea0SAnatolij Gustschin  * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is
10212458ea0SAnatolij Gustschin  * set) or not (PPC440SPE_RXOR_RUN is clear).
10312458ea0SAnatolij Gustschin  */
10412458ea0SAnatolij Gustschin static unsigned long ppc440spe_rxor_state;
10512458ea0SAnatolij Gustschin 
10612458ea0SAnatolij Gustschin /* These are used in enable & check routines
10712458ea0SAnatolij Gustschin  */
10812458ea0SAnatolij Gustschin static u32 ppc440spe_r6_enabled;
10912458ea0SAnatolij Gustschin static struct ppc440spe_adma_chan *ppc440spe_r6_tchan;
11012458ea0SAnatolij Gustschin static struct completion ppc440spe_r6_test_comp;
11112458ea0SAnatolij Gustschin 
11212458ea0SAnatolij Gustschin static int ppc440spe_adma_dma2rxor_prep_src(
11312458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
11412458ea0SAnatolij Gustschin 		struct ppc440spe_rxor *cursor, int index,
11512458ea0SAnatolij Gustschin 		int src_cnt, u32 addr);
11612458ea0SAnatolij Gustschin static void ppc440spe_adma_dma2rxor_set_src(
11712458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
11812458ea0SAnatolij Gustschin 		int index, dma_addr_t addr);
11912458ea0SAnatolij Gustschin static void ppc440spe_adma_dma2rxor_set_mult(
12012458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
12112458ea0SAnatolij Gustschin 		int index, u8 mult);
12212458ea0SAnatolij Gustschin 
12312458ea0SAnatolij Gustschin #ifdef ADMA_LL_DEBUG
12412458ea0SAnatolij Gustschin #define ADMA_LL_DBG(x) ({ if (1) x; 0; })
12512458ea0SAnatolij Gustschin #else
12612458ea0SAnatolij Gustschin #define ADMA_LL_DBG(x) ({ if (0) x; 0; })
12712458ea0SAnatolij Gustschin #endif
12812458ea0SAnatolij Gustschin 
print_cb(struct ppc440spe_adma_chan * chan,void * block)12912458ea0SAnatolij Gustschin static void print_cb(struct ppc440spe_adma_chan *chan, void *block)
13012458ea0SAnatolij Gustschin {
13112458ea0SAnatolij Gustschin 	struct dma_cdb *cdb;
13212458ea0SAnatolij Gustschin 	struct xor_cb *cb;
13312458ea0SAnatolij Gustschin 	int i;
13412458ea0SAnatolij Gustschin 
13512458ea0SAnatolij Gustschin 	switch (chan->device->id) {
13612458ea0SAnatolij Gustschin 	case 0:
13712458ea0SAnatolij Gustschin 	case 1:
13812458ea0SAnatolij Gustschin 		cdb = block;
13912458ea0SAnatolij Gustschin 
14012458ea0SAnatolij Gustschin 		pr_debug("CDB at %p [%d]:\n"
14112458ea0SAnatolij Gustschin 			"\t attr 0x%02x opc 0x%02x cnt 0x%08x\n"
14212458ea0SAnatolij Gustschin 			"\t sg1u 0x%08x sg1l 0x%08x\n"
14312458ea0SAnatolij Gustschin 			"\t sg2u 0x%08x sg2l 0x%08x\n"
14412458ea0SAnatolij Gustschin 			"\t sg3u 0x%08x sg3l 0x%08x\n",
14512458ea0SAnatolij Gustschin 			cdb, chan->device->id,
14612458ea0SAnatolij Gustschin 			cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt),
14712458ea0SAnatolij Gustschin 			le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l),
14812458ea0SAnatolij Gustschin 			le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l),
14912458ea0SAnatolij Gustschin 			le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l)
15012458ea0SAnatolij Gustschin 		);
15112458ea0SAnatolij Gustschin 		break;
15212458ea0SAnatolij Gustschin 	case 2:
15312458ea0SAnatolij Gustschin 		cb = block;
15412458ea0SAnatolij Gustschin 
15512458ea0SAnatolij Gustschin 		pr_debug("CB at %p [%d]:\n"
15612458ea0SAnatolij Gustschin 			"\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n"
15712458ea0SAnatolij Gustschin 			"\t cbtah 0x%08x cbtal 0x%08x\n"
15812458ea0SAnatolij Gustschin 			"\t cblah 0x%08x cblal 0x%08x\n",
15912458ea0SAnatolij Gustschin 			cb, chan->device->id,
16012458ea0SAnatolij Gustschin 			cb->cbc, cb->cbbc, cb->cbs,
16112458ea0SAnatolij Gustschin 			cb->cbtah, cb->cbtal,
16212458ea0SAnatolij Gustschin 			cb->cblah, cb->cblal);
16312458ea0SAnatolij Gustschin 		for (i = 0; i < 16; i++) {
16412458ea0SAnatolij Gustschin 			if (i && !cb->ops[i].h && !cb->ops[i].l)
16512458ea0SAnatolij Gustschin 				continue;
16612458ea0SAnatolij Gustschin 			pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n",
16712458ea0SAnatolij Gustschin 				i, cb->ops[i].h, cb->ops[i].l);
16812458ea0SAnatolij Gustschin 		}
16912458ea0SAnatolij Gustschin 		break;
17012458ea0SAnatolij Gustschin 	}
17112458ea0SAnatolij Gustschin }
17212458ea0SAnatolij Gustschin 
print_cb_list(struct ppc440spe_adma_chan * chan,struct ppc440spe_adma_desc_slot * iter)17312458ea0SAnatolij Gustschin static void print_cb_list(struct ppc440spe_adma_chan *chan,
17412458ea0SAnatolij Gustschin 			  struct ppc440spe_adma_desc_slot *iter)
17512458ea0SAnatolij Gustschin {
17612458ea0SAnatolij Gustschin 	for (; iter; iter = iter->hw_next)
17712458ea0SAnatolij Gustschin 		print_cb(chan, iter->hw_desc);
17812458ea0SAnatolij Gustschin }
17912458ea0SAnatolij Gustschin 
prep_dma_xor_dbg(int id,dma_addr_t dst,dma_addr_t * src,unsigned int src_cnt)18012458ea0SAnatolij Gustschin static void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src,
18112458ea0SAnatolij Gustschin 			     unsigned int src_cnt)
18212458ea0SAnatolij Gustschin {
18312458ea0SAnatolij Gustschin 	int i;
18412458ea0SAnatolij Gustschin 
18512458ea0SAnatolij Gustschin 	pr_debug("\n%s(%d):\nsrc: ", __func__, id);
18612458ea0SAnatolij Gustschin 	for (i = 0; i < src_cnt; i++)
18712458ea0SAnatolij Gustschin 		pr_debug("\t0x%016llx ", src[i]);
18812458ea0SAnatolij Gustschin 	pr_debug("dst:\n\t0x%016llx\n", dst);
18912458ea0SAnatolij Gustschin }
19012458ea0SAnatolij Gustschin 
prep_dma_pq_dbg(int id,dma_addr_t * dst,dma_addr_t * src,unsigned int src_cnt)19112458ea0SAnatolij Gustschin static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
19212458ea0SAnatolij Gustschin 			    unsigned int src_cnt)
19312458ea0SAnatolij Gustschin {
19412458ea0SAnatolij Gustschin 	int i;
19512458ea0SAnatolij Gustschin 
19612458ea0SAnatolij Gustschin 	pr_debug("\n%s(%d):\nsrc: ", __func__, id);
19712458ea0SAnatolij Gustschin 	for (i = 0; i < src_cnt; i++)
19812458ea0SAnatolij Gustschin 		pr_debug("\t0x%016llx ", src[i]);
19912458ea0SAnatolij Gustschin 	pr_debug("dst: ");
20012458ea0SAnatolij Gustschin 	for (i = 0; i < 2; i++)
20112458ea0SAnatolij Gustschin 		pr_debug("\t0x%016llx ", dst[i]);
20212458ea0SAnatolij Gustschin }
20312458ea0SAnatolij Gustschin 
prep_dma_pqzero_sum_dbg(int id,dma_addr_t * src,unsigned int src_cnt,const unsigned char * scf)20412458ea0SAnatolij Gustschin static void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
20512458ea0SAnatolij Gustschin 				    unsigned int src_cnt,
20612458ea0SAnatolij Gustschin 				    const unsigned char *scf)
20712458ea0SAnatolij Gustschin {
20812458ea0SAnatolij Gustschin 	int i;
20912458ea0SAnatolij Gustschin 
21012458ea0SAnatolij Gustschin 	pr_debug("\n%s(%d):\nsrc(coef): ", __func__, id);
21112458ea0SAnatolij Gustschin 	if (scf) {
21212458ea0SAnatolij Gustschin 		for (i = 0; i < src_cnt; i++)
21312458ea0SAnatolij Gustschin 			pr_debug("\t0x%016llx(0x%02x) ", src[i], scf[i]);
21412458ea0SAnatolij Gustschin 	} else {
21512458ea0SAnatolij Gustschin 		for (i = 0; i < src_cnt; i++)
21612458ea0SAnatolij Gustschin 			pr_debug("\t0x%016llx(no) ", src[i]);
21712458ea0SAnatolij Gustschin 	}
21812458ea0SAnatolij Gustschin 
21912458ea0SAnatolij Gustschin 	pr_debug("dst: ");
22012458ea0SAnatolij Gustschin 	for (i = 0; i < 2; i++)
22112458ea0SAnatolij Gustschin 		pr_debug("\t0x%016llx ", src[src_cnt + i]);
22212458ea0SAnatolij Gustschin }
22312458ea0SAnatolij Gustschin 
22412458ea0SAnatolij Gustschin /******************************************************************************
22512458ea0SAnatolij Gustschin  * Command (Descriptor) Blocks low-level routines
22612458ea0SAnatolij Gustschin  ******************************************************************************/
22712458ea0SAnatolij Gustschin /**
22812458ea0SAnatolij Gustschin  * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT
22912458ea0SAnatolij Gustschin  * pseudo operation
23012458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan)23112458ea0SAnatolij Gustschin static void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot *desc,
23212458ea0SAnatolij Gustschin 					  struct ppc440spe_adma_chan *chan)
23312458ea0SAnatolij Gustschin {
23412458ea0SAnatolij Gustschin 	struct xor_cb *p;
23512458ea0SAnatolij Gustschin 
23612458ea0SAnatolij Gustschin 	switch (chan->device->id) {
23712458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
23812458ea0SAnatolij Gustschin 		p = desc->hw_desc;
23912458ea0SAnatolij Gustschin 		memset(desc->hw_desc, 0, sizeof(struct xor_cb));
24012458ea0SAnatolij Gustschin 		/* NOP with Command Block Complete Enable */
24112458ea0SAnatolij Gustschin 		p->cbc = XOR_CBCR_CBCE_BIT;
24212458ea0SAnatolij Gustschin 		break;
24312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
24412458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
24512458ea0SAnatolij Gustschin 		memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
24612458ea0SAnatolij Gustschin 		/* NOP with interrupt */
24712458ea0SAnatolij Gustschin 		set_bit(PPC440SPE_DESC_INT, &desc->flags);
24812458ea0SAnatolij Gustschin 		break;
24912458ea0SAnatolij Gustschin 	default:
25012458ea0SAnatolij Gustschin 		printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id,
25112458ea0SAnatolij Gustschin 				__func__);
25212458ea0SAnatolij Gustschin 		break;
25312458ea0SAnatolij Gustschin 	}
25412458ea0SAnatolij Gustschin }
25512458ea0SAnatolij Gustschin 
25612458ea0SAnatolij Gustschin /**
25712458ea0SAnatolij Gustschin  * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR
25812458ea0SAnatolij Gustschin  * pseudo operation
25912458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot * desc)26012458ea0SAnatolij Gustschin static void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc)
26112458ea0SAnatolij Gustschin {
26212458ea0SAnatolij Gustschin 	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
26312458ea0SAnatolij Gustschin 	desc->hw_next = NULL;
26412458ea0SAnatolij Gustschin 	desc->src_cnt = 0;
26512458ea0SAnatolij Gustschin 	desc->dst_cnt = 1;
26612458ea0SAnatolij Gustschin }
26712458ea0SAnatolij Gustschin 
26812458ea0SAnatolij Gustschin /**
26912458ea0SAnatolij Gustschin  * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation
27012458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot * desc,int src_cnt,unsigned long flags)27112458ea0SAnatolij Gustschin static void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc,
27212458ea0SAnatolij Gustschin 					 int src_cnt, unsigned long flags)
27312458ea0SAnatolij Gustschin {
27412458ea0SAnatolij Gustschin 	struct xor_cb *hw_desc = desc->hw_desc;
27512458ea0SAnatolij Gustschin 
27612458ea0SAnatolij Gustschin 	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
27712458ea0SAnatolij Gustschin 	desc->hw_next = NULL;
27812458ea0SAnatolij Gustschin 	desc->src_cnt = src_cnt;
27912458ea0SAnatolij Gustschin 	desc->dst_cnt = 1;
28012458ea0SAnatolij Gustschin 
28112458ea0SAnatolij Gustschin 	hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt;
28212458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_INTERRUPT)
28312458ea0SAnatolij Gustschin 		/* Enable interrupt on completion */
28412458ea0SAnatolij Gustschin 		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
28512458ea0SAnatolij Gustschin }
28612458ea0SAnatolij Gustschin 
28712458ea0SAnatolij Gustschin /**
28812458ea0SAnatolij Gustschin  * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ
28912458ea0SAnatolij Gustschin  * operation in DMA2 controller
29012458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot * desc,int dst_cnt,int src_cnt,unsigned long flags)29112458ea0SAnatolij Gustschin static void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc,
29212458ea0SAnatolij Gustschin 		int dst_cnt, int src_cnt, unsigned long flags)
29312458ea0SAnatolij Gustschin {
29412458ea0SAnatolij Gustschin 	struct xor_cb *hw_desc = desc->hw_desc;
29512458ea0SAnatolij Gustschin 
29612458ea0SAnatolij Gustschin 	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
29712458ea0SAnatolij Gustschin 	desc->hw_next = NULL;
29812458ea0SAnatolij Gustschin 	desc->src_cnt = src_cnt;
29912458ea0SAnatolij Gustschin 	desc->dst_cnt = dst_cnt;
30012458ea0SAnatolij Gustschin 	memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags));
30112458ea0SAnatolij Gustschin 	desc->descs_per_op = 0;
30212458ea0SAnatolij Gustschin 
30312458ea0SAnatolij Gustschin 	hw_desc->cbc = XOR_CBCR_TGT_BIT;
30412458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_INTERRUPT)
30512458ea0SAnatolij Gustschin 		/* Enable interrupt on completion */
30612458ea0SAnatolij Gustschin 		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
30712458ea0SAnatolij Gustschin }
30812458ea0SAnatolij Gustschin 
30912458ea0SAnatolij Gustschin #define DMA_CTRL_FLAGS_LAST	DMA_PREP_FENCE
31012458ea0SAnatolij Gustschin #define DMA_PREP_ZERO_P		(DMA_CTRL_FLAGS_LAST << 1)
31112458ea0SAnatolij Gustschin #define DMA_PREP_ZERO_Q		(DMA_PREP_ZERO_P << 1)
31212458ea0SAnatolij Gustschin 
31312458ea0SAnatolij Gustschin /**
31412458ea0SAnatolij Gustschin  * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation
31512458ea0SAnatolij Gustschin  * with DMA0/1
31612458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot * desc,int dst_cnt,int src_cnt,unsigned long flags,unsigned long op)31712458ea0SAnatolij Gustschin static void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc,
31812458ea0SAnatolij Gustschin 				int dst_cnt, int src_cnt, unsigned long flags,
31912458ea0SAnatolij Gustschin 				unsigned long op)
32012458ea0SAnatolij Gustschin {
32112458ea0SAnatolij Gustschin 	struct dma_cdb *hw_desc;
32212458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter;
32312458ea0SAnatolij Gustschin 	u8 dopc;
32412458ea0SAnatolij Gustschin 
32512458ea0SAnatolij Gustschin 	/* Common initialization of a PQ descriptors chain */
32612458ea0SAnatolij Gustschin 	set_bits(op, &desc->flags);
32712458ea0SAnatolij Gustschin 	desc->src_cnt = src_cnt;
32812458ea0SAnatolij Gustschin 	desc->dst_cnt = dst_cnt;
32912458ea0SAnatolij Gustschin 
33012458ea0SAnatolij Gustschin 	/* WXOR MULTICAST if both P and Q are being computed
33112458ea0SAnatolij Gustschin 	 * MV_SG1_SG2 if Q only
33212458ea0SAnatolij Gustschin 	 */
33312458ea0SAnatolij Gustschin 	dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ?
33412458ea0SAnatolij Gustschin 		DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2;
33512458ea0SAnatolij Gustschin 
33612458ea0SAnatolij Gustschin 	list_for_each_entry(iter, &desc->group_list, chain_node) {
33712458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
33812458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
33912458ea0SAnatolij Gustschin 
34012458ea0SAnatolij Gustschin 		if (likely(!list_is_last(&iter->chain_node,
34112458ea0SAnatolij Gustschin 				&desc->group_list))) {
34212458ea0SAnatolij Gustschin 			/* set 'next' pointer */
34312458ea0SAnatolij Gustschin 			iter->hw_next = list_entry(iter->chain_node.next,
34412458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot, chain_node);
34512458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
34612458ea0SAnatolij Gustschin 		} else {
34712458ea0SAnatolij Gustschin 			/* this is the last descriptor.
34812458ea0SAnatolij Gustschin 			 * this slot will be pasted from ADMA level
34912458ea0SAnatolij Gustschin 			 * each time it wants to configure parameters
35012458ea0SAnatolij Gustschin 			 * of the transaction (src, dst, ...)
35112458ea0SAnatolij Gustschin 			 */
35212458ea0SAnatolij Gustschin 			iter->hw_next = NULL;
35312458ea0SAnatolij Gustschin 			if (flags & DMA_PREP_INTERRUPT)
35412458ea0SAnatolij Gustschin 				set_bit(PPC440SPE_DESC_INT, &iter->flags);
35512458ea0SAnatolij Gustschin 			else
35612458ea0SAnatolij Gustschin 				clear_bit(PPC440SPE_DESC_INT, &iter->flags);
35712458ea0SAnatolij Gustschin 		}
35812458ea0SAnatolij Gustschin 	}
35912458ea0SAnatolij Gustschin 
36012458ea0SAnatolij Gustschin 	/* Set OPS depending on WXOR/RXOR type of operation */
36112458ea0SAnatolij Gustschin 	if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) {
36212458ea0SAnatolij Gustschin 		/* This is a WXOR only chain:
36312458ea0SAnatolij Gustschin 		 * - first descriptors are for zeroing destinations
36412458ea0SAnatolij Gustschin 		 *   if PPC440SPE_ZERO_P/Q set;
36512458ea0SAnatolij Gustschin 		 * - descriptors remained are for GF-XOR operations.
36612458ea0SAnatolij Gustschin 		 */
36712458ea0SAnatolij Gustschin 		iter = list_first_entry(&desc->group_list,
36812458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
36912458ea0SAnatolij Gustschin 					chain_node);
37012458ea0SAnatolij Gustschin 
37112458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) {
37212458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
37312458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
37412458ea0SAnatolij Gustschin 			iter = list_first_entry(&iter->chain_node,
37512458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
37612458ea0SAnatolij Gustschin 					chain_node);
37712458ea0SAnatolij Gustschin 		}
37812458ea0SAnatolij Gustschin 
37912458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) {
38012458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
38112458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
38212458ea0SAnatolij Gustschin 			iter = list_first_entry(&iter->chain_node,
38312458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
38412458ea0SAnatolij Gustschin 					chain_node);
38512458ea0SAnatolij Gustschin 		}
38612458ea0SAnatolij Gustschin 
38712458ea0SAnatolij Gustschin 		list_for_each_entry_from(iter, &desc->group_list, chain_node) {
38812458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
38912458ea0SAnatolij Gustschin 			hw_desc->opc = dopc;
39012458ea0SAnatolij Gustschin 		}
39112458ea0SAnatolij Gustschin 	} else {
39212458ea0SAnatolij Gustschin 		/* This is either RXOR-only or mixed RXOR/WXOR */
39312458ea0SAnatolij Gustschin 
39412458ea0SAnatolij Gustschin 		/* The first 1 or 2 slots in chain are always RXOR,
39512458ea0SAnatolij Gustschin 		 * if need to calculate P & Q, then there are two
39612458ea0SAnatolij Gustschin 		 * RXOR slots; if only P or only Q, then there is one
39712458ea0SAnatolij Gustschin 		 */
39812458ea0SAnatolij Gustschin 		iter = list_first_entry(&desc->group_list,
39912458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
40012458ea0SAnatolij Gustschin 					chain_node);
40112458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
40212458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
40312458ea0SAnatolij Gustschin 
40412458ea0SAnatolij Gustschin 		if (desc->dst_cnt == DMA_DEST_MAX_NUM) {
40512458ea0SAnatolij Gustschin 			iter = list_first_entry(&iter->chain_node,
40612458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
40712458ea0SAnatolij Gustschin 						chain_node);
40812458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
40912458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
41012458ea0SAnatolij Gustschin 		}
41112458ea0SAnatolij Gustschin 
41212458ea0SAnatolij Gustschin 		/* The remaining descs (if any) are WXORs */
41312458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) {
41412458ea0SAnatolij Gustschin 			iter = list_first_entry(&iter->chain_node,
41512458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
41612458ea0SAnatolij Gustschin 						chain_node);
41712458ea0SAnatolij Gustschin 			list_for_each_entry_from(iter, &desc->group_list,
41812458ea0SAnatolij Gustschin 						chain_node) {
41912458ea0SAnatolij Gustschin 				hw_desc = iter->hw_desc;
42012458ea0SAnatolij Gustschin 				hw_desc->opc = dopc;
42112458ea0SAnatolij Gustschin 			}
42212458ea0SAnatolij Gustschin 		}
42312458ea0SAnatolij Gustschin 	}
42412458ea0SAnatolij Gustschin }
42512458ea0SAnatolij Gustschin 
42612458ea0SAnatolij Gustschin /**
42712458ea0SAnatolij Gustschin  * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor
42812458ea0SAnatolij Gustschin  * for PQ_ZERO_SUM operation
42912458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_dma01pqzero_sum(struct ppc440spe_adma_desc_slot * desc,int dst_cnt,int src_cnt)43012458ea0SAnatolij Gustschin static void ppc440spe_desc_init_dma01pqzero_sum(
43112458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *desc,
43212458ea0SAnatolij Gustschin 				int dst_cnt, int src_cnt)
43312458ea0SAnatolij Gustschin {
43412458ea0SAnatolij Gustschin 	struct dma_cdb *hw_desc;
43512458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter;
43612458ea0SAnatolij Gustschin 	int i = 0;
43712458ea0SAnatolij Gustschin 	u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST :
43812458ea0SAnatolij Gustschin 				   DMA_CDB_OPC_MV_SG1_SG2;
43912458ea0SAnatolij Gustschin 	/*
44012458ea0SAnatolij Gustschin 	 * Initialize starting from 2nd or 3rd descriptor dependent
44112458ea0SAnatolij Gustschin 	 * on dst_cnt. First one or two slots are for cloning P
44212458ea0SAnatolij Gustschin 	 * and/or Q to chan->pdest and/or chan->qdest as we have
44312458ea0SAnatolij Gustschin 	 * to preserve original P/Q.
44412458ea0SAnatolij Gustschin 	 */
44512458ea0SAnatolij Gustschin 	iter = list_first_entry(&desc->group_list,
44612458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot, chain_node);
44712458ea0SAnatolij Gustschin 	iter = list_entry(iter->chain_node.next,
44812458ea0SAnatolij Gustschin 			  struct ppc440spe_adma_desc_slot, chain_node);
44912458ea0SAnatolij Gustschin 
45012458ea0SAnatolij Gustschin 	if (dst_cnt > 1) {
45112458ea0SAnatolij Gustschin 		iter = list_entry(iter->chain_node.next,
45212458ea0SAnatolij Gustschin 				  struct ppc440spe_adma_desc_slot, chain_node);
45312458ea0SAnatolij Gustschin 	}
45412458ea0SAnatolij Gustschin 	/* initialize each source descriptor in chain */
45512458ea0SAnatolij Gustschin 	list_for_each_entry_from(iter, &desc->group_list, chain_node) {
45612458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
45712458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
45812458ea0SAnatolij Gustschin 		iter->src_cnt = 0;
45912458ea0SAnatolij Gustschin 		iter->dst_cnt = 0;
46012458ea0SAnatolij Gustschin 
46112458ea0SAnatolij Gustschin 		/* This is a ZERO_SUM operation:
46212458ea0SAnatolij Gustschin 		 * - <src_cnt> descriptors starting from 2nd or 3rd
46312458ea0SAnatolij Gustschin 		 *   descriptor are for GF-XOR operations;
46412458ea0SAnatolij Gustschin 		 * - remaining <dst_cnt> descriptors are for checking the result
46512458ea0SAnatolij Gustschin 		 */
46612458ea0SAnatolij Gustschin 		if (i++ < src_cnt)
46712458ea0SAnatolij Gustschin 			/* MV_SG1_SG2 if only Q is being verified
46812458ea0SAnatolij Gustschin 			 * MULTICAST if both P and Q are being verified
46912458ea0SAnatolij Gustschin 			 */
47012458ea0SAnatolij Gustschin 			hw_desc->opc = dopc;
47112458ea0SAnatolij Gustschin 		else
47212458ea0SAnatolij Gustschin 			/* DMA_CDB_OPC_DCHECK128 operation */
47312458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_DCHECK128;
47412458ea0SAnatolij Gustschin 
47512458ea0SAnatolij Gustschin 		if (likely(!list_is_last(&iter->chain_node,
47612458ea0SAnatolij Gustschin 					 &desc->group_list))) {
47712458ea0SAnatolij Gustschin 			/* set 'next' pointer */
47812458ea0SAnatolij Gustschin 			iter->hw_next = list_entry(iter->chain_node.next,
47912458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
48012458ea0SAnatolij Gustschin 						chain_node);
48112458ea0SAnatolij Gustschin 		} else {
48212458ea0SAnatolij Gustschin 			/* this is the last descriptor.
48312458ea0SAnatolij Gustschin 			 * this slot will be pasted from ADMA level
48412458ea0SAnatolij Gustschin 			 * each time it wants to configure parameters
48512458ea0SAnatolij Gustschin 			 * of the transaction (src, dst, ...)
48612458ea0SAnatolij Gustschin 			 */
48712458ea0SAnatolij Gustschin 			iter->hw_next = NULL;
48812458ea0SAnatolij Gustschin 			/* always enable interrupt generation since we get
48912458ea0SAnatolij Gustschin 			 * the status of pqzero from the handler
49012458ea0SAnatolij Gustschin 			 */
49112458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_DESC_INT, &iter->flags);
49212458ea0SAnatolij Gustschin 		}
49312458ea0SAnatolij Gustschin 	}
49412458ea0SAnatolij Gustschin 	desc->src_cnt = src_cnt;
49512458ea0SAnatolij Gustschin 	desc->dst_cnt = dst_cnt;
49612458ea0SAnatolij Gustschin }
49712458ea0SAnatolij Gustschin 
49812458ea0SAnatolij Gustschin /**
49912458ea0SAnatolij Gustschin  * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation
50012458ea0SAnatolij Gustschin  */
ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot * desc,unsigned long flags)50112458ea0SAnatolij Gustschin static void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc,
50212458ea0SAnatolij Gustschin 					unsigned long flags)
50312458ea0SAnatolij Gustschin {
50412458ea0SAnatolij Gustschin 	struct dma_cdb *hw_desc = desc->hw_desc;
50512458ea0SAnatolij Gustschin 
50612458ea0SAnatolij Gustschin 	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
50712458ea0SAnatolij Gustschin 	desc->hw_next = NULL;
50812458ea0SAnatolij Gustschin 	desc->src_cnt = 1;
50912458ea0SAnatolij Gustschin 	desc->dst_cnt = 1;
51012458ea0SAnatolij Gustschin 
51112458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_INTERRUPT)
51212458ea0SAnatolij Gustschin 		set_bit(PPC440SPE_DESC_INT, &desc->flags);
51312458ea0SAnatolij Gustschin 	else
51412458ea0SAnatolij Gustschin 		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
51512458ea0SAnatolij Gustschin 
51612458ea0SAnatolij Gustschin 	hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
51712458ea0SAnatolij Gustschin }
51812458ea0SAnatolij Gustschin 
51912458ea0SAnatolij Gustschin /**
52012458ea0SAnatolij Gustschin  * ppc440spe_desc_set_src_addr - set source address into the descriptor
52112458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,int src_idx,dma_addr_t addrh,dma_addr_t addrl)52212458ea0SAnatolij Gustschin static void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc,
52312458ea0SAnatolij Gustschin 					struct ppc440spe_adma_chan *chan,
52412458ea0SAnatolij Gustschin 					int src_idx, dma_addr_t addrh,
52512458ea0SAnatolij Gustschin 					dma_addr_t addrl)
52612458ea0SAnatolij Gustschin {
52712458ea0SAnatolij Gustschin 	struct dma_cdb *dma_hw_desc;
52812458ea0SAnatolij Gustschin 	struct xor_cb *xor_hw_desc;
52912458ea0SAnatolij Gustschin 	phys_addr_t addr64, tmplow, tmphi;
53012458ea0SAnatolij Gustschin 
53112458ea0SAnatolij Gustschin 	switch (chan->device->id) {
53212458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
53312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
53412458ea0SAnatolij Gustschin 		if (!addrh) {
53512458ea0SAnatolij Gustschin 			addr64 = addrl;
53612458ea0SAnatolij Gustschin 			tmphi = (addr64 >> 32);
53712458ea0SAnatolij Gustschin 			tmplow = (addr64 & 0xFFFFFFFF);
53812458ea0SAnatolij Gustschin 		} else {
53912458ea0SAnatolij Gustschin 			tmphi = addrh;
54012458ea0SAnatolij Gustschin 			tmplow = addrl;
54112458ea0SAnatolij Gustschin 		}
54212458ea0SAnatolij Gustschin 		dma_hw_desc = desc->hw_desc;
54312458ea0SAnatolij Gustschin 		dma_hw_desc->sg1l = cpu_to_le32((u32)tmplow);
54412458ea0SAnatolij Gustschin 		dma_hw_desc->sg1u |= cpu_to_le32((u32)tmphi);
54512458ea0SAnatolij Gustschin 		break;
54612458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
54712458ea0SAnatolij Gustschin 		xor_hw_desc = desc->hw_desc;
54812458ea0SAnatolij Gustschin 		xor_hw_desc->ops[src_idx].l = addrl;
54912458ea0SAnatolij Gustschin 		xor_hw_desc->ops[src_idx].h |= addrh;
55012458ea0SAnatolij Gustschin 		break;
55112458ea0SAnatolij Gustschin 	}
55212458ea0SAnatolij Gustschin }
55312458ea0SAnatolij Gustschin 
55412458ea0SAnatolij Gustschin /**
55512458ea0SAnatolij Gustschin  * ppc440spe_desc_set_src_mult - set source address mult into the descriptor
55612458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,u32 mult_index,int sg_index,unsigned char mult_value)55712458ea0SAnatolij Gustschin static void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
55812458ea0SAnatolij Gustschin 			struct ppc440spe_adma_chan *chan, u32 mult_index,
55912458ea0SAnatolij Gustschin 			int sg_index, unsigned char mult_value)
56012458ea0SAnatolij Gustschin {
56112458ea0SAnatolij Gustschin 	struct dma_cdb *dma_hw_desc;
56212458ea0SAnatolij Gustschin 	u32 *psgu;
56312458ea0SAnatolij Gustschin 
56412458ea0SAnatolij Gustschin 	switch (chan->device->id) {
56512458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
56612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
56712458ea0SAnatolij Gustschin 		dma_hw_desc = desc->hw_desc;
56812458ea0SAnatolij Gustschin 
56912458ea0SAnatolij Gustschin 		switch (sg_index) {
57012458ea0SAnatolij Gustschin 		/* for RXOR operations set multiplier
57112458ea0SAnatolij Gustschin 		 * into source cued address
57212458ea0SAnatolij Gustschin 		 */
57312458ea0SAnatolij Gustschin 		case DMA_CDB_SG_SRC:
57412458ea0SAnatolij Gustschin 			psgu = &dma_hw_desc->sg1u;
57512458ea0SAnatolij Gustschin 			break;
57612458ea0SAnatolij Gustschin 		/* for WXOR operations set multiplier
57712458ea0SAnatolij Gustschin 		 * into destination cued address(es)
57812458ea0SAnatolij Gustschin 		 */
57912458ea0SAnatolij Gustschin 		case DMA_CDB_SG_DST1:
58012458ea0SAnatolij Gustschin 			psgu = &dma_hw_desc->sg2u;
58112458ea0SAnatolij Gustschin 			break;
58212458ea0SAnatolij Gustschin 		case DMA_CDB_SG_DST2:
58312458ea0SAnatolij Gustschin 			psgu = &dma_hw_desc->sg3u;
58412458ea0SAnatolij Gustschin 			break;
58512458ea0SAnatolij Gustschin 		default:
58612458ea0SAnatolij Gustschin 			BUG();
58712458ea0SAnatolij Gustschin 		}
58812458ea0SAnatolij Gustschin 
58912458ea0SAnatolij Gustschin 		*psgu |= cpu_to_le32(mult_value << mult_index);
59012458ea0SAnatolij Gustschin 		break;
59112458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
59212458ea0SAnatolij Gustschin 		break;
59312458ea0SAnatolij Gustschin 	default:
59412458ea0SAnatolij Gustschin 		BUG();
59512458ea0SAnatolij Gustschin 	}
59612458ea0SAnatolij Gustschin }
59712458ea0SAnatolij Gustschin 
59812458ea0SAnatolij Gustschin /**
59912458ea0SAnatolij Gustschin  * ppc440spe_desc_set_dest_addr - set destination address into the descriptor
60012458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,dma_addr_t addrh,dma_addr_t addrl,u32 dst_idx)60112458ea0SAnatolij Gustschin static void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc,
60212458ea0SAnatolij Gustschin 				struct ppc440spe_adma_chan *chan,
60312458ea0SAnatolij Gustschin 				dma_addr_t addrh, dma_addr_t addrl,
60412458ea0SAnatolij Gustschin 				u32 dst_idx)
60512458ea0SAnatolij Gustschin {
60612458ea0SAnatolij Gustschin 	struct dma_cdb *dma_hw_desc;
60712458ea0SAnatolij Gustschin 	struct xor_cb *xor_hw_desc;
60812458ea0SAnatolij Gustschin 	phys_addr_t addr64, tmphi, tmplow;
60912458ea0SAnatolij Gustschin 	u32 *psgu, *psgl;
61012458ea0SAnatolij Gustschin 
61112458ea0SAnatolij Gustschin 	switch (chan->device->id) {
61212458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
61312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
61412458ea0SAnatolij Gustschin 		if (!addrh) {
61512458ea0SAnatolij Gustschin 			addr64 = addrl;
61612458ea0SAnatolij Gustschin 			tmphi = (addr64 >> 32);
61712458ea0SAnatolij Gustschin 			tmplow = (addr64 & 0xFFFFFFFF);
61812458ea0SAnatolij Gustschin 		} else {
61912458ea0SAnatolij Gustschin 			tmphi = addrh;
62012458ea0SAnatolij Gustschin 			tmplow = addrl;
62112458ea0SAnatolij Gustschin 		}
62212458ea0SAnatolij Gustschin 		dma_hw_desc = desc->hw_desc;
62312458ea0SAnatolij Gustschin 
62412458ea0SAnatolij Gustschin 		psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u;
62512458ea0SAnatolij Gustschin 		psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l;
62612458ea0SAnatolij Gustschin 
62712458ea0SAnatolij Gustschin 		*psgl = cpu_to_le32((u32)tmplow);
62812458ea0SAnatolij Gustschin 		*psgu |= cpu_to_le32((u32)tmphi);
62912458ea0SAnatolij Gustschin 		break;
63012458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
63112458ea0SAnatolij Gustschin 		xor_hw_desc = desc->hw_desc;
63212458ea0SAnatolij Gustschin 		xor_hw_desc->cbtal = addrl;
63312458ea0SAnatolij Gustschin 		xor_hw_desc->cbtah |= addrh;
63412458ea0SAnatolij Gustschin 		break;
63512458ea0SAnatolij Gustschin 	}
63612458ea0SAnatolij Gustschin }
63712458ea0SAnatolij Gustschin 
63812458ea0SAnatolij Gustschin /**
63912458ea0SAnatolij Gustschin  * ppc440spe_desc_set_byte_count - set number of data bytes involved
64012458ea0SAnatolij Gustschin  * into the operation
64112458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,u32 byte_count)64212458ea0SAnatolij Gustschin static void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot *desc,
64312458ea0SAnatolij Gustschin 				struct ppc440spe_adma_chan *chan,
64412458ea0SAnatolij Gustschin 				u32 byte_count)
64512458ea0SAnatolij Gustschin {
64612458ea0SAnatolij Gustschin 	struct dma_cdb *dma_hw_desc;
64712458ea0SAnatolij Gustschin 	struct xor_cb *xor_hw_desc;
64812458ea0SAnatolij Gustschin 
64912458ea0SAnatolij Gustschin 	switch (chan->device->id) {
65012458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
65112458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
65212458ea0SAnatolij Gustschin 		dma_hw_desc = desc->hw_desc;
65312458ea0SAnatolij Gustschin 		dma_hw_desc->cnt = cpu_to_le32(byte_count);
65412458ea0SAnatolij Gustschin 		break;
65512458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
65612458ea0SAnatolij Gustschin 		xor_hw_desc = desc->hw_desc;
65712458ea0SAnatolij Gustschin 		xor_hw_desc->cbbc = byte_count;
65812458ea0SAnatolij Gustschin 		break;
65912458ea0SAnatolij Gustschin 	}
66012458ea0SAnatolij Gustschin }
66112458ea0SAnatolij Gustschin 
66212458ea0SAnatolij Gustschin /**
66312458ea0SAnatolij Gustschin  * ppc440spe_desc_set_rxor_block_size - set RXOR block size
66412458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_rxor_block_size(u32 byte_count)66512458ea0SAnatolij Gustschin static inline void ppc440spe_desc_set_rxor_block_size(u32 byte_count)
66612458ea0SAnatolij Gustschin {
66712458ea0SAnatolij Gustschin 	/* assume that byte_count is aligned on the 512-boundary;
66812458ea0SAnatolij Gustschin 	 * thus write it directly to the register (bits 23:31 are
66912458ea0SAnatolij Gustschin 	 * reserved there).
67012458ea0SAnatolij Gustschin 	 */
67112458ea0SAnatolij Gustschin 	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count);
67212458ea0SAnatolij Gustschin }
67312458ea0SAnatolij Gustschin 
67412458ea0SAnatolij Gustschin /**
67512458ea0SAnatolij Gustschin  * ppc440spe_desc_set_dcheck - set CHECK pattern
67612458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,u8 * qword)67712458ea0SAnatolij Gustschin static void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc,
67812458ea0SAnatolij Gustschin 				struct ppc440spe_adma_chan *chan, u8 *qword)
67912458ea0SAnatolij Gustschin {
68012458ea0SAnatolij Gustschin 	struct dma_cdb *dma_hw_desc;
68112458ea0SAnatolij Gustschin 
68212458ea0SAnatolij Gustschin 	switch (chan->device->id) {
68312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
68412458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
68512458ea0SAnatolij Gustschin 		dma_hw_desc = desc->hw_desc;
68612458ea0SAnatolij Gustschin 		iowrite32(qword[0], &dma_hw_desc->sg3l);
68712458ea0SAnatolij Gustschin 		iowrite32(qword[4], &dma_hw_desc->sg3u);
68812458ea0SAnatolij Gustschin 		iowrite32(qword[8], &dma_hw_desc->sg2l);
68912458ea0SAnatolij Gustschin 		iowrite32(qword[12], &dma_hw_desc->sg2u);
69012458ea0SAnatolij Gustschin 		break;
69112458ea0SAnatolij Gustschin 	default:
69212458ea0SAnatolij Gustschin 		BUG();
69312458ea0SAnatolij Gustschin 	}
69412458ea0SAnatolij Gustschin }
69512458ea0SAnatolij Gustschin 
69612458ea0SAnatolij Gustschin /**
69712458ea0SAnatolij Gustschin  * ppc440spe_xor_set_link - set link address in xor CB
69812458ea0SAnatolij Gustschin  */
ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot * prev_desc,struct ppc440spe_adma_desc_slot * next_desc)69912458ea0SAnatolij Gustschin static void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
70012458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *next_desc)
70112458ea0SAnatolij Gustschin {
70212458ea0SAnatolij Gustschin 	struct xor_cb *xor_hw_desc = prev_desc->hw_desc;
70312458ea0SAnatolij Gustschin 
70412458ea0SAnatolij Gustschin 	if (unlikely(!next_desc || !(next_desc->phys))) {
70512458ea0SAnatolij Gustschin 		printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n",
70612458ea0SAnatolij Gustschin 			__func__, next_desc,
70712458ea0SAnatolij Gustschin 			next_desc ? next_desc->phys : 0);
70812458ea0SAnatolij Gustschin 		BUG();
70912458ea0SAnatolij Gustschin 	}
71012458ea0SAnatolij Gustschin 
71112458ea0SAnatolij Gustschin 	xor_hw_desc->cbs = 0;
71212458ea0SAnatolij Gustschin 	xor_hw_desc->cblal = next_desc->phys;
71312458ea0SAnatolij Gustschin 	xor_hw_desc->cblah = 0;
71412458ea0SAnatolij Gustschin 	xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT;
71512458ea0SAnatolij Gustschin }
71612458ea0SAnatolij Gustschin 
71712458ea0SAnatolij Gustschin /**
71812458ea0SAnatolij Gustschin  * ppc440spe_desc_set_link - set the address of descriptor following this
71912458ea0SAnatolij Gustschin  * descriptor in chain
72012458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_link(struct ppc440spe_adma_chan * chan,struct ppc440spe_adma_desc_slot * prev_desc,struct ppc440spe_adma_desc_slot * next_desc)72112458ea0SAnatolij Gustschin static void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
72212458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *prev_desc,
72312458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *next_desc)
72412458ea0SAnatolij Gustschin {
72512458ea0SAnatolij Gustschin 	unsigned long flags;
72612458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *tail = next_desc;
72712458ea0SAnatolij Gustschin 
72812458ea0SAnatolij Gustschin 	if (unlikely(!prev_desc || !next_desc ||
72912458ea0SAnatolij Gustschin 		(prev_desc->hw_next && prev_desc->hw_next != next_desc))) {
73012458ea0SAnatolij Gustschin 		/* If previous next is overwritten something is wrong.
73112458ea0SAnatolij Gustschin 		 * though we may refetch from append to initiate list
73212458ea0SAnatolij Gustschin 		 * processing; in this case - it's ok.
73312458ea0SAnatolij Gustschin 		 */
73412458ea0SAnatolij Gustschin 		printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; "
73512458ea0SAnatolij Gustschin 			"prev->hw_next=0x%p\n", __func__, prev_desc,
73612458ea0SAnatolij Gustschin 			next_desc, prev_desc ? prev_desc->hw_next : 0);
73712458ea0SAnatolij Gustschin 		BUG();
73812458ea0SAnatolij Gustschin 	}
73912458ea0SAnatolij Gustschin 
74012458ea0SAnatolij Gustschin 	local_irq_save(flags);
74112458ea0SAnatolij Gustschin 
74212458ea0SAnatolij Gustschin 	/* do s/w chaining both for DMA and XOR descriptors */
74312458ea0SAnatolij Gustschin 	prev_desc->hw_next = next_desc;
74412458ea0SAnatolij Gustschin 
74512458ea0SAnatolij Gustschin 	switch (chan->device->id) {
74612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
74712458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
74812458ea0SAnatolij Gustschin 		break;
74912458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
75012458ea0SAnatolij Gustschin 		/* bind descriptor to the chain */
75112458ea0SAnatolij Gustschin 		while (tail->hw_next)
75212458ea0SAnatolij Gustschin 			tail = tail->hw_next;
75312458ea0SAnatolij Gustschin 		xor_last_linked = tail;
75412458ea0SAnatolij Gustschin 
75512458ea0SAnatolij Gustschin 		if (prev_desc == xor_last_submit)
75612458ea0SAnatolij Gustschin 			/* do not link to the last submitted CB */
75712458ea0SAnatolij Gustschin 			break;
75812458ea0SAnatolij Gustschin 		ppc440spe_xor_set_link(prev_desc, next_desc);
75912458ea0SAnatolij Gustschin 		break;
76012458ea0SAnatolij Gustschin 	}
76112458ea0SAnatolij Gustschin 
76212458ea0SAnatolij Gustschin 	local_irq_restore(flags);
76312458ea0SAnatolij Gustschin }
76412458ea0SAnatolij Gustschin 
76512458ea0SAnatolij Gustschin /**
76612458ea0SAnatolij Gustschin  * ppc440spe_desc_get_link - get the address of the descriptor that
76712458ea0SAnatolij Gustschin  * follows this one
76812458ea0SAnatolij Gustschin  */
ppc440spe_desc_get_link(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan)76912458ea0SAnatolij Gustschin static inline u32 ppc440spe_desc_get_link(struct ppc440spe_adma_desc_slot *desc,
77012458ea0SAnatolij Gustschin 					struct ppc440spe_adma_chan *chan)
77112458ea0SAnatolij Gustschin {
77212458ea0SAnatolij Gustschin 	if (!desc->hw_next)
77312458ea0SAnatolij Gustschin 		return 0;
77412458ea0SAnatolij Gustschin 
77512458ea0SAnatolij Gustschin 	return desc->hw_next->phys;
77612458ea0SAnatolij Gustschin }
77712458ea0SAnatolij Gustschin 
77812458ea0SAnatolij Gustschin /**
77912458ea0SAnatolij Gustschin  * ppc440spe_desc_is_aligned - check alignment
78012458ea0SAnatolij Gustschin  */
ppc440spe_desc_is_aligned(struct ppc440spe_adma_desc_slot * desc,int num_slots)78112458ea0SAnatolij Gustschin static inline int ppc440spe_desc_is_aligned(
78212458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *desc, int num_slots)
78312458ea0SAnatolij Gustschin {
78412458ea0SAnatolij Gustschin 	return (desc->idx & (num_slots - 1)) ? 0 : 1;
78512458ea0SAnatolij Gustschin }
78612458ea0SAnatolij Gustschin 
78712458ea0SAnatolij Gustschin /**
78812458ea0SAnatolij Gustschin  * ppc440spe_chan_xor_slot_count - get the number of slots necessary for
78912458ea0SAnatolij Gustschin  * XOR operation
79012458ea0SAnatolij Gustschin  */
ppc440spe_chan_xor_slot_count(size_t len,int src_cnt,int * slots_per_op)79112458ea0SAnatolij Gustschin static int ppc440spe_chan_xor_slot_count(size_t len, int src_cnt,
79212458ea0SAnatolij Gustschin 			int *slots_per_op)
79312458ea0SAnatolij Gustschin {
79412458ea0SAnatolij Gustschin 	int slot_cnt;
79512458ea0SAnatolij Gustschin 
79612458ea0SAnatolij Gustschin 	/* each XOR descriptor provides up to 16 source operands */
79712458ea0SAnatolij Gustschin 	slot_cnt = *slots_per_op = (src_cnt + XOR_MAX_OPS - 1)/XOR_MAX_OPS;
79812458ea0SAnatolij Gustschin 
79912458ea0SAnatolij Gustschin 	if (likely(len <= PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT))
80012458ea0SAnatolij Gustschin 		return slot_cnt;
80112458ea0SAnatolij Gustschin 
80212458ea0SAnatolij Gustschin 	printk(KERN_ERR "%s: len %d > max %d !!\n",
80312458ea0SAnatolij Gustschin 		__func__, len, PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
80412458ea0SAnatolij Gustschin 	BUG();
80512458ea0SAnatolij Gustschin 	return slot_cnt;
80612458ea0SAnatolij Gustschin }
80712458ea0SAnatolij Gustschin 
80812458ea0SAnatolij Gustschin /**
80912458ea0SAnatolij Gustschin  * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for
81012458ea0SAnatolij Gustschin  * DMA2 PQ operation
81112458ea0SAnatolij Gustschin  */
ppc440spe_dma2_pq_slot_count(dma_addr_t * srcs,int src_cnt,size_t len)81212458ea0SAnatolij Gustschin static int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs,
81312458ea0SAnatolij Gustschin 		int src_cnt, size_t len)
81412458ea0SAnatolij Gustschin {
81512458ea0SAnatolij Gustschin 	signed long long order = 0;
81612458ea0SAnatolij Gustschin 	int state = 0;
81712458ea0SAnatolij Gustschin 	int addr_count = 0;
81812458ea0SAnatolij Gustschin 	int i;
81912458ea0SAnatolij Gustschin 	for (i = 1; i < src_cnt; i++) {
82012458ea0SAnatolij Gustschin 		dma_addr_t cur_addr = srcs[i];
82112458ea0SAnatolij Gustschin 		dma_addr_t old_addr = srcs[i-1];
82212458ea0SAnatolij Gustschin 		switch (state) {
82312458ea0SAnatolij Gustschin 		case 0:
82412458ea0SAnatolij Gustschin 			if (cur_addr == old_addr + len) {
82512458ea0SAnatolij Gustschin 				/* direct RXOR */
82612458ea0SAnatolij Gustschin 				order = 1;
82712458ea0SAnatolij Gustschin 				state = 1;
82812458ea0SAnatolij Gustschin 				if (i == src_cnt-1)
82912458ea0SAnatolij Gustschin 					addr_count++;
83012458ea0SAnatolij Gustschin 			} else if (old_addr == cur_addr + len) {
83112458ea0SAnatolij Gustschin 				/* reverse RXOR */
83212458ea0SAnatolij Gustschin 				order = -1;
83312458ea0SAnatolij Gustschin 				state = 1;
83412458ea0SAnatolij Gustschin 				if (i == src_cnt-1)
83512458ea0SAnatolij Gustschin 					addr_count++;
83612458ea0SAnatolij Gustschin 			} else {
83712458ea0SAnatolij Gustschin 				state = 3;
83812458ea0SAnatolij Gustschin 			}
83912458ea0SAnatolij Gustschin 			break;
84012458ea0SAnatolij Gustschin 		case 1:
84112458ea0SAnatolij Gustschin 			if (i == src_cnt-2 || (order == -1
84212458ea0SAnatolij Gustschin 				&& cur_addr != old_addr - len)) {
84312458ea0SAnatolij Gustschin 				order = 0;
84412458ea0SAnatolij Gustschin 				state = 0;
84512458ea0SAnatolij Gustschin 				addr_count++;
84612458ea0SAnatolij Gustschin 			} else if (cur_addr == old_addr + len*order) {
84712458ea0SAnatolij Gustschin 				state = 2;
84812458ea0SAnatolij Gustschin 				if (i == src_cnt-1)
84912458ea0SAnatolij Gustschin 					addr_count++;
85012458ea0SAnatolij Gustschin 			} else if (cur_addr == old_addr + 2*len) {
85112458ea0SAnatolij Gustschin 				state = 2;
85212458ea0SAnatolij Gustschin 				if (i == src_cnt-1)
85312458ea0SAnatolij Gustschin 					addr_count++;
85412458ea0SAnatolij Gustschin 			} else if (cur_addr == old_addr + 3*len) {
85512458ea0SAnatolij Gustschin 				state = 2;
85612458ea0SAnatolij Gustschin 				if (i == src_cnt-1)
85712458ea0SAnatolij Gustschin 					addr_count++;
85812458ea0SAnatolij Gustschin 			} else {
85912458ea0SAnatolij Gustschin 				order = 0;
86012458ea0SAnatolij Gustschin 				state = 0;
86112458ea0SAnatolij Gustschin 				addr_count++;
86212458ea0SAnatolij Gustschin 			}
86312458ea0SAnatolij Gustschin 			break;
86412458ea0SAnatolij Gustschin 		case 2:
86512458ea0SAnatolij Gustschin 			order = 0;
86612458ea0SAnatolij Gustschin 			state = 0;
86712458ea0SAnatolij Gustschin 			addr_count++;
86812458ea0SAnatolij Gustschin 				break;
86912458ea0SAnatolij Gustschin 		}
87012458ea0SAnatolij Gustschin 		if (state == 3)
87112458ea0SAnatolij Gustschin 			break;
87212458ea0SAnatolij Gustschin 	}
87312458ea0SAnatolij Gustschin 	if (src_cnt <= 1 || (state != 1 && state != 2)) {
87412458ea0SAnatolij Gustschin 		pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n",
87512458ea0SAnatolij Gustschin 			__func__, src_cnt, state, addr_count, order);
87612458ea0SAnatolij Gustschin 		for (i = 0; i < src_cnt; i++)
87712458ea0SAnatolij Gustschin 			pr_err("\t[%d] 0x%llx \n", i, srcs[i]);
87812458ea0SAnatolij Gustschin 		BUG();
87912458ea0SAnatolij Gustschin 	}
88012458ea0SAnatolij Gustschin 
88112458ea0SAnatolij Gustschin 	return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS;
88212458ea0SAnatolij Gustschin }
88312458ea0SAnatolij Gustschin 
88412458ea0SAnatolij Gustschin 
88512458ea0SAnatolij Gustschin /******************************************************************************
88612458ea0SAnatolij Gustschin  * ADMA channel low-level routines
88712458ea0SAnatolij Gustschin  ******************************************************************************/
88812458ea0SAnatolij Gustschin 
88912458ea0SAnatolij Gustschin static u32
89012458ea0SAnatolij Gustschin ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan);
89112458ea0SAnatolij Gustschin static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan);
89212458ea0SAnatolij Gustschin 
89312458ea0SAnatolij Gustschin /**
89412458ea0SAnatolij Gustschin  * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine
89512458ea0SAnatolij Gustschin  */
ppc440spe_adma_device_clear_eot_status(struct ppc440spe_adma_chan * chan)89612458ea0SAnatolij Gustschin static void ppc440spe_adma_device_clear_eot_status(
89712458ea0SAnatolij Gustschin 					struct ppc440spe_adma_chan *chan)
89812458ea0SAnatolij Gustschin {
89912458ea0SAnatolij Gustschin 	struct dma_regs *dma_reg;
90012458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg;
90112458ea0SAnatolij Gustschin 	u8 *p = chan->device->dma_desc_pool_virt;
90212458ea0SAnatolij Gustschin 	struct dma_cdb *cdb;
90312458ea0SAnatolij Gustschin 	u32 rv, i;
90412458ea0SAnatolij Gustschin 
90512458ea0SAnatolij Gustschin 	switch (chan->device->id) {
90612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
90712458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
90812458ea0SAnatolij Gustschin 		/* read FIFO to ack */
90912458ea0SAnatolij Gustschin 		dma_reg = chan->device->dma_reg;
91012458ea0SAnatolij Gustschin 		while ((rv = ioread32(&dma_reg->csfpl))) {
91112458ea0SAnatolij Gustschin 			i = rv & DMA_CDB_ADDR_MSK;
91212458ea0SAnatolij Gustschin 			cdb = (struct dma_cdb *)&p[i -
91312458ea0SAnatolij Gustschin 			    (u32)chan->device->dma_desc_pool];
91412458ea0SAnatolij Gustschin 
91512458ea0SAnatolij Gustschin 			/* Clear opcode to ack. This is necessary for
91612458ea0SAnatolij Gustschin 			 * ZeroSum operations only
91712458ea0SAnatolij Gustschin 			 */
91812458ea0SAnatolij Gustschin 			cdb->opc = 0;
91912458ea0SAnatolij Gustschin 
92012458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_RXOR_RUN,
92112458ea0SAnatolij Gustschin 			    &ppc440spe_rxor_state)) {
92212458ea0SAnatolij Gustschin 				/* probably this is a completed RXOR op,
92312458ea0SAnatolij Gustschin 				 * get pointer to CDB using the fact that
92412458ea0SAnatolij Gustschin 				 * physical and virtual addresses of CDB
92512458ea0SAnatolij Gustschin 				 * in pools have the same offsets
92612458ea0SAnatolij Gustschin 				 */
92712458ea0SAnatolij Gustschin 				if (le32_to_cpu(cdb->sg1u) &
92812458ea0SAnatolij Gustschin 				    DMA_CUED_XOR_BASE) {
92912458ea0SAnatolij Gustschin 					/* this is a RXOR */
93012458ea0SAnatolij Gustschin 					clear_bit(PPC440SPE_RXOR_RUN,
93112458ea0SAnatolij Gustschin 						  &ppc440spe_rxor_state);
93212458ea0SAnatolij Gustschin 				}
93312458ea0SAnatolij Gustschin 			}
93412458ea0SAnatolij Gustschin 
93512458ea0SAnatolij Gustschin 			if (rv & DMA_CDB_STATUS_MSK) {
93612458ea0SAnatolij Gustschin 				/* ZeroSum check failed
93712458ea0SAnatolij Gustschin 				 */
93812458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *iter;
93912458ea0SAnatolij Gustschin 				dma_addr_t phys = rv & ~DMA_CDB_MSK;
94012458ea0SAnatolij Gustschin 
94112458ea0SAnatolij Gustschin 				/*
94212458ea0SAnatolij Gustschin 				 * Update the status of corresponding
94312458ea0SAnatolij Gustschin 				 * descriptor.
94412458ea0SAnatolij Gustschin 				 */
94512458ea0SAnatolij Gustschin 				list_for_each_entry(iter, &chan->chain,
94612458ea0SAnatolij Gustschin 				    chain_node) {
94712458ea0SAnatolij Gustschin 					if (iter->phys == phys)
94812458ea0SAnatolij Gustschin 						break;
94912458ea0SAnatolij Gustschin 				}
95012458ea0SAnatolij Gustschin 				/*
95112458ea0SAnatolij Gustschin 				 * if cannot find the corresponding
95212458ea0SAnatolij Gustschin 				 * slot it's a bug
95312458ea0SAnatolij Gustschin 				 */
95412458ea0SAnatolij Gustschin 				BUG_ON(&iter->chain_node == &chan->chain);
95512458ea0SAnatolij Gustschin 
95612458ea0SAnatolij Gustschin 				if (iter->xor_check_result) {
95712458ea0SAnatolij Gustschin 					if (test_bit(PPC440SPE_DESC_PCHECK,
95812458ea0SAnatolij Gustschin 						     &iter->flags)) {
95912458ea0SAnatolij Gustschin 						*iter->xor_check_result |=
96012458ea0SAnatolij Gustschin 							SUM_CHECK_P_RESULT;
96112458ea0SAnatolij Gustschin 					} else
96212458ea0SAnatolij Gustschin 					if (test_bit(PPC440SPE_DESC_QCHECK,
96312458ea0SAnatolij Gustschin 						     &iter->flags)) {
96412458ea0SAnatolij Gustschin 						*iter->xor_check_result |=
96512458ea0SAnatolij Gustschin 							SUM_CHECK_Q_RESULT;
96612458ea0SAnatolij Gustschin 					} else
96712458ea0SAnatolij Gustschin 						BUG();
96812458ea0SAnatolij Gustschin 				}
96912458ea0SAnatolij Gustschin 			}
97012458ea0SAnatolij Gustschin 		}
97112458ea0SAnatolij Gustschin 
97212458ea0SAnatolij Gustschin 		rv = ioread32(&dma_reg->dsts);
97312458ea0SAnatolij Gustschin 		if (rv) {
97412458ea0SAnatolij Gustschin 			pr_err("DMA%d err status: 0x%x\n",
97512458ea0SAnatolij Gustschin 			       chan->device->id, rv);
97612458ea0SAnatolij Gustschin 			/* write back to clear */
97712458ea0SAnatolij Gustschin 			iowrite32(rv, &dma_reg->dsts);
97812458ea0SAnatolij Gustschin 		}
97912458ea0SAnatolij Gustschin 		break;
98012458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
98112458ea0SAnatolij Gustschin 		/* reset status bits to ack */
98212458ea0SAnatolij Gustschin 		xor_reg = chan->device->xor_reg;
98312458ea0SAnatolij Gustschin 		rv = ioread32be(&xor_reg->sr);
98412458ea0SAnatolij Gustschin 		iowrite32be(rv, &xor_reg->sr);
98512458ea0SAnatolij Gustschin 
98612458ea0SAnatolij Gustschin 		if (rv & (XOR_IE_ICBIE_BIT|XOR_IE_ICIE_BIT|XOR_IE_RPTIE_BIT)) {
98712458ea0SAnatolij Gustschin 			if (rv & XOR_IE_RPTIE_BIT) {
98812458ea0SAnatolij Gustschin 				/* Read PLB Timeout Error.
98912458ea0SAnatolij Gustschin 				 * Try to resubmit the CB
99012458ea0SAnatolij Gustschin 				 */
99112458ea0SAnatolij Gustschin 				u32 val = ioread32be(&xor_reg->ccbalr);
99212458ea0SAnatolij Gustschin 
99312458ea0SAnatolij Gustschin 				iowrite32be(val, &xor_reg->cblalr);
99412458ea0SAnatolij Gustschin 
99512458ea0SAnatolij Gustschin 				val = ioread32be(&xor_reg->crsr);
99612458ea0SAnatolij Gustschin 				iowrite32be(val | XOR_CRSR_XAE_BIT,
99712458ea0SAnatolij Gustschin 					    &xor_reg->crsr);
99812458ea0SAnatolij Gustschin 			} else
99912458ea0SAnatolij Gustschin 				pr_err("XOR ERR 0x%x status\n", rv);
100012458ea0SAnatolij Gustschin 			break;
100112458ea0SAnatolij Gustschin 		}
100212458ea0SAnatolij Gustschin 
100312458ea0SAnatolij Gustschin 		/*  if the XORcore is idle, but there are unprocessed CBs
100412458ea0SAnatolij Gustschin 		 * then refetch the s/w chain here
100512458ea0SAnatolij Gustschin 		 */
100612458ea0SAnatolij Gustschin 		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) &&
100712458ea0SAnatolij Gustschin 		    do_xor_refetch)
100812458ea0SAnatolij Gustschin 			ppc440spe_chan_append(chan);
100912458ea0SAnatolij Gustschin 		break;
101012458ea0SAnatolij Gustschin 	}
101112458ea0SAnatolij Gustschin }
101212458ea0SAnatolij Gustschin 
101312458ea0SAnatolij Gustschin /**
101412458ea0SAnatolij Gustschin  * ppc440spe_chan_is_busy - get the channel status
101512458ea0SAnatolij Gustschin  */
ppc440spe_chan_is_busy(struct ppc440spe_adma_chan * chan)101612458ea0SAnatolij Gustschin static int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan)
101712458ea0SAnatolij Gustschin {
101812458ea0SAnatolij Gustschin 	struct dma_regs *dma_reg;
101912458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg;
102012458ea0SAnatolij Gustschin 	int busy = 0;
102112458ea0SAnatolij Gustschin 
102212458ea0SAnatolij Gustschin 	switch (chan->device->id) {
102312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
102412458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
102512458ea0SAnatolij Gustschin 		dma_reg = chan->device->dma_reg;
102612458ea0SAnatolij Gustschin 		/*  if command FIFO's head and tail pointers are equal and
102712458ea0SAnatolij Gustschin 		 * status tail is the same as command, then channel is free
102812458ea0SAnatolij Gustschin 		 */
102912458ea0SAnatolij Gustschin 		if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) ||
103012458ea0SAnatolij Gustschin 		    ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp))
103112458ea0SAnatolij Gustschin 			busy = 1;
103212458ea0SAnatolij Gustschin 		break;
103312458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
103412458ea0SAnatolij Gustschin 		/* use the special status bit for the XORcore
103512458ea0SAnatolij Gustschin 		 */
103612458ea0SAnatolij Gustschin 		xor_reg = chan->device->xor_reg;
103712458ea0SAnatolij Gustschin 		busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0;
103812458ea0SAnatolij Gustschin 		break;
103912458ea0SAnatolij Gustschin 	}
104012458ea0SAnatolij Gustschin 
104112458ea0SAnatolij Gustschin 	return busy;
104212458ea0SAnatolij Gustschin }
104312458ea0SAnatolij Gustschin 
104412458ea0SAnatolij Gustschin /**
104512458ea0SAnatolij Gustschin  * ppc440spe_chan_set_first_xor_descriptor -  init XORcore chain
104612458ea0SAnatolij Gustschin  */
ppc440spe_chan_set_first_xor_descriptor(struct ppc440spe_adma_chan * chan,struct ppc440spe_adma_desc_slot * next_desc)104712458ea0SAnatolij Gustschin static void ppc440spe_chan_set_first_xor_descriptor(
104812458ea0SAnatolij Gustschin 				struct ppc440spe_adma_chan *chan,
104912458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot *next_desc)
105012458ea0SAnatolij Gustschin {
105112458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg = chan->device->xor_reg;
105212458ea0SAnatolij Gustschin 
105312458ea0SAnatolij Gustschin 	if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)
105412458ea0SAnatolij Gustschin 		printk(KERN_INFO "%s: Warn: XORcore is running "
105512458ea0SAnatolij Gustschin 			"when try to set the first CDB!\n",
105612458ea0SAnatolij Gustschin 			__func__);
105712458ea0SAnatolij Gustschin 
105812458ea0SAnatolij Gustschin 	xor_last_submit = xor_last_linked = next_desc;
105912458ea0SAnatolij Gustschin 
106012458ea0SAnatolij Gustschin 	iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr);
106112458ea0SAnatolij Gustschin 
106212458ea0SAnatolij Gustschin 	iowrite32be(next_desc->phys, &xor_reg->cblalr);
106312458ea0SAnatolij Gustschin 	iowrite32be(0, &xor_reg->cblahr);
106412458ea0SAnatolij Gustschin 	iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT,
106512458ea0SAnatolij Gustschin 		    &xor_reg->cbcr);
106612458ea0SAnatolij Gustschin 
106712458ea0SAnatolij Gustschin 	chan->hw_chain_inited = 1;
106812458ea0SAnatolij Gustschin }
106912458ea0SAnatolij Gustschin 
107012458ea0SAnatolij Gustschin /**
107112458ea0SAnatolij Gustschin  * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO.
107212458ea0SAnatolij Gustschin  * called with irqs disabled
107312458ea0SAnatolij Gustschin  */
ppc440spe_dma_put_desc(struct ppc440spe_adma_chan * chan,struct ppc440spe_adma_desc_slot * desc)107412458ea0SAnatolij Gustschin static void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
107512458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc)
107612458ea0SAnatolij Gustschin {
107712458ea0SAnatolij Gustschin 	u32 pcdb;
107812458ea0SAnatolij Gustschin 	struct dma_regs *dma_reg = chan->device->dma_reg;
107912458ea0SAnatolij Gustschin 
108012458ea0SAnatolij Gustschin 	pcdb = desc->phys;
108112458ea0SAnatolij Gustschin 	if (!test_bit(PPC440SPE_DESC_INT, &desc->flags))
108212458ea0SAnatolij Gustschin 		pcdb |= DMA_CDB_NO_INT;
108312458ea0SAnatolij Gustschin 
108412458ea0SAnatolij Gustschin 	chan_last_sub[chan->device->id] = desc;
108512458ea0SAnatolij Gustschin 
108612458ea0SAnatolij Gustschin 	ADMA_LL_DBG(print_cb(chan, desc->hw_desc));
108712458ea0SAnatolij Gustschin 
108812458ea0SAnatolij Gustschin 	iowrite32(pcdb, &dma_reg->cpfpl);
108912458ea0SAnatolij Gustschin }
109012458ea0SAnatolij Gustschin 
109112458ea0SAnatolij Gustschin /**
109212458ea0SAnatolij Gustschin  * ppc440spe_chan_append - update the h/w chain in the channel
109312458ea0SAnatolij Gustschin  */
ppc440spe_chan_append(struct ppc440spe_adma_chan * chan)109412458ea0SAnatolij Gustschin static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan)
109512458ea0SAnatolij Gustschin {
109612458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg;
109712458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter;
109812458ea0SAnatolij Gustschin 	struct xor_cb *xcb;
109912458ea0SAnatolij Gustschin 	u32 cur_desc;
110012458ea0SAnatolij Gustschin 	unsigned long flags;
110112458ea0SAnatolij Gustschin 
110212458ea0SAnatolij Gustschin 	local_irq_save(flags);
110312458ea0SAnatolij Gustschin 
110412458ea0SAnatolij Gustschin 	switch (chan->device->id) {
110512458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
110612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
110712458ea0SAnatolij Gustschin 		cur_desc = ppc440spe_chan_get_current_descriptor(chan);
110812458ea0SAnatolij Gustschin 
110912458ea0SAnatolij Gustschin 		if (likely(cur_desc)) {
111012458ea0SAnatolij Gustschin 			iter = chan_last_sub[chan->device->id];
111112458ea0SAnatolij Gustschin 			BUG_ON(!iter);
111212458ea0SAnatolij Gustschin 		} else {
111312458ea0SAnatolij Gustschin 			/* first peer */
111412458ea0SAnatolij Gustschin 			iter = chan_first_cdb[chan->device->id];
111512458ea0SAnatolij Gustschin 			BUG_ON(!iter);
111612458ea0SAnatolij Gustschin 			ppc440spe_dma_put_desc(chan, iter);
111712458ea0SAnatolij Gustschin 			chan->hw_chain_inited = 1;
111812458ea0SAnatolij Gustschin 		}
111912458ea0SAnatolij Gustschin 
112012458ea0SAnatolij Gustschin 		/* is there something new to append */
112112458ea0SAnatolij Gustschin 		if (!iter->hw_next)
112212458ea0SAnatolij Gustschin 			break;
112312458ea0SAnatolij Gustschin 
112412458ea0SAnatolij Gustschin 		/* flush descriptors from the s/w queue to fifo */
112512458ea0SAnatolij Gustschin 		list_for_each_entry_continue(iter, &chan->chain, chain_node) {
112612458ea0SAnatolij Gustschin 			ppc440spe_dma_put_desc(chan, iter);
112712458ea0SAnatolij Gustschin 			if (!iter->hw_next)
112812458ea0SAnatolij Gustschin 				break;
112912458ea0SAnatolij Gustschin 		}
113012458ea0SAnatolij Gustschin 		break;
113112458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
113212458ea0SAnatolij Gustschin 		/* update h/w links and refetch */
113312458ea0SAnatolij Gustschin 		if (!xor_last_submit->hw_next)
113412458ea0SAnatolij Gustschin 			break;
113512458ea0SAnatolij Gustschin 
113612458ea0SAnatolij Gustschin 		xor_reg = chan->device->xor_reg;
113712458ea0SAnatolij Gustschin 		/* the last linked CDB has to generate an interrupt
113812458ea0SAnatolij Gustschin 		 * that we'd be able to append the next lists to h/w
113912458ea0SAnatolij Gustschin 		 * regardless of the XOR engine state at the moment of
114012458ea0SAnatolij Gustschin 		 * appending of these next lists
114112458ea0SAnatolij Gustschin 		 */
114212458ea0SAnatolij Gustschin 		xcb = xor_last_linked->hw_desc;
114312458ea0SAnatolij Gustschin 		xcb->cbc |= XOR_CBCR_CBCE_BIT;
114412458ea0SAnatolij Gustschin 
114512458ea0SAnatolij Gustschin 		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) {
114612458ea0SAnatolij Gustschin 			/* XORcore is idle. Refetch now */
114712458ea0SAnatolij Gustschin 			do_xor_refetch = 0;
114812458ea0SAnatolij Gustschin 			ppc440spe_xor_set_link(xor_last_submit,
114912458ea0SAnatolij Gustschin 				xor_last_submit->hw_next);
115012458ea0SAnatolij Gustschin 
115112458ea0SAnatolij Gustschin 			ADMA_LL_DBG(print_cb_list(chan,
115212458ea0SAnatolij Gustschin 				xor_last_submit->hw_next));
115312458ea0SAnatolij Gustschin 
115412458ea0SAnatolij Gustschin 			xor_last_submit = xor_last_linked;
115512458ea0SAnatolij Gustschin 			iowrite32be(ioread32be(&xor_reg->crsr) |
115612458ea0SAnatolij Gustschin 				    XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT,
115712458ea0SAnatolij Gustschin 				    &xor_reg->crsr);
115812458ea0SAnatolij Gustschin 		} else {
115912458ea0SAnatolij Gustschin 			/* XORcore is running. Refetch later in the handler */
116012458ea0SAnatolij Gustschin 			do_xor_refetch = 1;
116112458ea0SAnatolij Gustschin 		}
116212458ea0SAnatolij Gustschin 
116312458ea0SAnatolij Gustschin 		break;
116412458ea0SAnatolij Gustschin 	}
116512458ea0SAnatolij Gustschin 
116612458ea0SAnatolij Gustschin 	local_irq_restore(flags);
116712458ea0SAnatolij Gustschin }
116812458ea0SAnatolij Gustschin 
116912458ea0SAnatolij Gustschin /**
117012458ea0SAnatolij Gustschin  * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor
117112458ea0SAnatolij Gustschin  */
117212458ea0SAnatolij Gustschin static u32
ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan * chan)117312458ea0SAnatolij Gustschin ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan)
117412458ea0SAnatolij Gustschin {
117512458ea0SAnatolij Gustschin 	struct dma_regs *dma_reg;
117612458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg;
117712458ea0SAnatolij Gustschin 
117812458ea0SAnatolij Gustschin 	if (unlikely(!chan->hw_chain_inited))
117912458ea0SAnatolij Gustschin 		/* h/w descriptor chain is not initialized yet */
118012458ea0SAnatolij Gustschin 		return 0;
118112458ea0SAnatolij Gustschin 
118212458ea0SAnatolij Gustschin 	switch (chan->device->id) {
118312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
118412458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
118512458ea0SAnatolij Gustschin 		dma_reg = chan->device->dma_reg;
118612458ea0SAnatolij Gustschin 		return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK);
118712458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
118812458ea0SAnatolij Gustschin 		xor_reg = chan->device->xor_reg;
118912458ea0SAnatolij Gustschin 		return ioread32be(&xor_reg->ccbalr);
119012458ea0SAnatolij Gustschin 	}
119112458ea0SAnatolij Gustschin 	return 0;
119212458ea0SAnatolij Gustschin }
119312458ea0SAnatolij Gustschin 
119412458ea0SAnatolij Gustschin /**
119512458ea0SAnatolij Gustschin  * ppc440spe_chan_run - enable the channel
119612458ea0SAnatolij Gustschin  */
ppc440spe_chan_run(struct ppc440spe_adma_chan * chan)119712458ea0SAnatolij Gustschin static void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan)
119812458ea0SAnatolij Gustschin {
119912458ea0SAnatolij Gustschin 	struct xor_regs *xor_reg;
120012458ea0SAnatolij Gustschin 
120112458ea0SAnatolij Gustschin 	switch (chan->device->id) {
120212458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
120312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
120412458ea0SAnatolij Gustschin 		/* DMAs are always enabled, do nothing */
120512458ea0SAnatolij Gustschin 		break;
120612458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
120712458ea0SAnatolij Gustschin 		/* drain write buffer */
120812458ea0SAnatolij Gustschin 		xor_reg = chan->device->xor_reg;
120912458ea0SAnatolij Gustschin 
121012458ea0SAnatolij Gustschin 		/* fetch descriptor pointed to in <link> */
121112458ea0SAnatolij Gustschin 		iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT,
121212458ea0SAnatolij Gustschin 			    &xor_reg->crsr);
121312458ea0SAnatolij Gustschin 		break;
121412458ea0SAnatolij Gustschin 	}
121512458ea0SAnatolij Gustschin }
121612458ea0SAnatolij Gustschin 
121712458ea0SAnatolij Gustschin /******************************************************************************
121812458ea0SAnatolij Gustschin  * ADMA device level
121912458ea0SAnatolij Gustschin  ******************************************************************************/
122012458ea0SAnatolij Gustschin 
122112458ea0SAnatolij Gustschin static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan);
122212458ea0SAnatolij Gustschin static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan);
122312458ea0SAnatolij Gustschin 
122412458ea0SAnatolij Gustschin static dma_cookie_t
122512458ea0SAnatolij Gustschin ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx);
122612458ea0SAnatolij Gustschin 
122712458ea0SAnatolij Gustschin static void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *tx,
122812458ea0SAnatolij Gustschin 				    dma_addr_t addr, int index);
122912458ea0SAnatolij Gustschin static void
123012458ea0SAnatolij Gustschin ppc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot *tx,
123112458ea0SAnatolij Gustschin 				  dma_addr_t addr, int index);
123212458ea0SAnatolij Gustschin 
123312458ea0SAnatolij Gustschin static void
123412458ea0SAnatolij Gustschin ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *tx,
123512458ea0SAnatolij Gustschin 			   dma_addr_t *paddr, unsigned long flags);
123612458ea0SAnatolij Gustschin static void
123712458ea0SAnatolij Gustschin ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *tx,
123812458ea0SAnatolij Gustschin 			  dma_addr_t addr, int index);
123912458ea0SAnatolij Gustschin static void
124012458ea0SAnatolij Gustschin ppc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot *tx,
124112458ea0SAnatolij Gustschin 			       unsigned char mult, int index, int dst_pos);
124212458ea0SAnatolij Gustschin static void
124312458ea0SAnatolij Gustschin ppc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot *tx,
124412458ea0SAnatolij Gustschin 				   dma_addr_t paddr, dma_addr_t qaddr);
124512458ea0SAnatolij Gustschin 
124612458ea0SAnatolij Gustschin static struct page *ppc440spe_rxor_srcs[32];
124712458ea0SAnatolij Gustschin 
124812458ea0SAnatolij Gustschin /**
124912458ea0SAnatolij Gustschin  * ppc440spe_can_rxor - check if the operands may be processed with RXOR
125012458ea0SAnatolij Gustschin  */
ppc440spe_can_rxor(struct page ** srcs,int src_cnt,size_t len)125112458ea0SAnatolij Gustschin static int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len)
125212458ea0SAnatolij Gustschin {
125312458ea0SAnatolij Gustschin 	int i, order = 0, state = 0;
125412458ea0SAnatolij Gustschin 	int idx = 0;
125512458ea0SAnatolij Gustschin 
125612458ea0SAnatolij Gustschin 	if (unlikely(!(src_cnt > 1)))
125712458ea0SAnatolij Gustschin 		return 0;
125812458ea0SAnatolij Gustschin 
125912458ea0SAnatolij Gustschin 	BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs));
126012458ea0SAnatolij Gustschin 
126112458ea0SAnatolij Gustschin 	/* Skip holes in the source list before checking */
126212458ea0SAnatolij Gustschin 	for (i = 0; i < src_cnt; i++) {
126312458ea0SAnatolij Gustschin 		if (!srcs[i])
126412458ea0SAnatolij Gustschin 			continue;
126512458ea0SAnatolij Gustschin 		ppc440spe_rxor_srcs[idx++] = srcs[i];
126612458ea0SAnatolij Gustschin 	}
126712458ea0SAnatolij Gustschin 	src_cnt = idx;
126812458ea0SAnatolij Gustschin 
126912458ea0SAnatolij Gustschin 	for (i = 1; i < src_cnt; i++) {
127012458ea0SAnatolij Gustschin 		char *cur_addr = page_address(ppc440spe_rxor_srcs[i]);
127112458ea0SAnatolij Gustschin 		char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]);
127212458ea0SAnatolij Gustschin 
127312458ea0SAnatolij Gustschin 		switch (state) {
127412458ea0SAnatolij Gustschin 		case 0:
127512458ea0SAnatolij Gustschin 			if (cur_addr == old_addr + len) {
127612458ea0SAnatolij Gustschin 				/* direct RXOR */
127712458ea0SAnatolij Gustschin 				order = 1;
127812458ea0SAnatolij Gustschin 				state = 1;
127912458ea0SAnatolij Gustschin 			} else if (old_addr == cur_addr + len) {
128012458ea0SAnatolij Gustschin 				/* reverse RXOR */
128112458ea0SAnatolij Gustschin 				order = -1;
128212458ea0SAnatolij Gustschin 				state = 1;
128312458ea0SAnatolij Gustschin 			} else
128412458ea0SAnatolij Gustschin 				goto out;
128512458ea0SAnatolij Gustschin 			break;
128612458ea0SAnatolij Gustschin 		case 1:
128712458ea0SAnatolij Gustschin 			if ((i == src_cnt - 2) ||
128812458ea0SAnatolij Gustschin 			    (order == -1 && cur_addr != old_addr - len)) {
128912458ea0SAnatolij Gustschin 				order = 0;
129012458ea0SAnatolij Gustschin 				state = 0;
129112458ea0SAnatolij Gustschin 			} else if ((cur_addr == old_addr + len * order) ||
129212458ea0SAnatolij Gustschin 				   (cur_addr == old_addr + 2 * len) ||
129312458ea0SAnatolij Gustschin 				   (cur_addr == old_addr + 3 * len)) {
129412458ea0SAnatolij Gustschin 				state = 2;
129512458ea0SAnatolij Gustschin 			} else {
129612458ea0SAnatolij Gustschin 				order = 0;
129712458ea0SAnatolij Gustschin 				state = 0;
129812458ea0SAnatolij Gustschin 			}
129912458ea0SAnatolij Gustschin 			break;
130012458ea0SAnatolij Gustschin 		case 2:
130112458ea0SAnatolij Gustschin 			order = 0;
130212458ea0SAnatolij Gustschin 			state = 0;
130312458ea0SAnatolij Gustschin 			break;
130412458ea0SAnatolij Gustschin 		}
130512458ea0SAnatolij Gustschin 	}
130612458ea0SAnatolij Gustschin 
130712458ea0SAnatolij Gustschin out:
130812458ea0SAnatolij Gustschin 	if (state == 1 || state == 2)
130912458ea0SAnatolij Gustschin 		return 1;
131012458ea0SAnatolij Gustschin 
131112458ea0SAnatolij Gustschin 	return 0;
131212458ea0SAnatolij Gustschin }
131312458ea0SAnatolij Gustschin 
131412458ea0SAnatolij Gustschin /**
131512458ea0SAnatolij Gustschin  * ppc440spe_adma_device_estimate - estimate the efficiency of processing
131612458ea0SAnatolij Gustschin  *	the operation given on this channel. It's assumed that 'chan' is
131712458ea0SAnatolij Gustschin  *	capable to process 'cap' type of operation.
131812458ea0SAnatolij Gustschin  * @chan: channel to use
131912458ea0SAnatolij Gustschin  * @cap: type of transaction
132012458ea0SAnatolij Gustschin  * @dst_lst: array of destination pointers
132112458ea0SAnatolij Gustschin  * @dst_cnt: number of destination operands
132212458ea0SAnatolij Gustschin  * @src_lst: array of source pointers
132312458ea0SAnatolij Gustschin  * @src_cnt: number of source operands
132412458ea0SAnatolij Gustschin  * @src_sz: size of each source operand
132512458ea0SAnatolij Gustschin  */
ppc440spe_adma_estimate(struct dma_chan * chan,enum dma_transaction_type cap,struct page ** dst_lst,int dst_cnt,struct page ** src_lst,int src_cnt,size_t src_sz)132612458ea0SAnatolij Gustschin static int ppc440spe_adma_estimate(struct dma_chan *chan,
132712458ea0SAnatolij Gustschin 	enum dma_transaction_type cap, struct page **dst_lst, int dst_cnt,
132812458ea0SAnatolij Gustschin 	struct page **src_lst, int src_cnt, size_t src_sz)
132912458ea0SAnatolij Gustschin {
133012458ea0SAnatolij Gustschin 	int ef = 1;
133112458ea0SAnatolij Gustschin 
133212458ea0SAnatolij Gustschin 	if (cap == DMA_PQ || cap == DMA_PQ_VAL) {
133312458ea0SAnatolij Gustschin 		/* If RAID-6 capabilities were not activated don't try
133412458ea0SAnatolij Gustschin 		 * to use them
133512458ea0SAnatolij Gustschin 		 */
133612458ea0SAnatolij Gustschin 		if (unlikely(!ppc440spe_r6_enabled))
133712458ea0SAnatolij Gustschin 			return -1;
133812458ea0SAnatolij Gustschin 	}
133912458ea0SAnatolij Gustschin 	/*  In the current implementation of ppc440spe ADMA driver it
134012458ea0SAnatolij Gustschin 	 * makes sense to pick out only pq case, because it may be
134112458ea0SAnatolij Gustschin 	 * processed:
134212458ea0SAnatolij Gustschin 	 * (1) either using Biskup method on DMA2;
134312458ea0SAnatolij Gustschin 	 * (2) or on DMA0/1.
134412458ea0SAnatolij Gustschin 	 *  Thus we give a favour to (1) if the sources are suitable;
134512458ea0SAnatolij Gustschin 	 * else let it be processed on one of the DMA0/1 engines.
134612458ea0SAnatolij Gustschin 	 *  In the sum_product case where destination is also the
134712458ea0SAnatolij Gustschin 	 * source process it on DMA0/1 only.
134812458ea0SAnatolij Gustschin 	 */
134912458ea0SAnatolij Gustschin 	if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) {
135012458ea0SAnatolij Gustschin 
135112458ea0SAnatolij Gustschin 		if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1])
135212458ea0SAnatolij Gustschin 			ef = 0; /* sum_product case, process on DMA0/1 */
135312458ea0SAnatolij Gustschin 		else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz))
135412458ea0SAnatolij Gustschin 			ef = 3; /* override (DMA0/1 + idle) */
135512458ea0SAnatolij Gustschin 		else
135612458ea0SAnatolij Gustschin 			ef = 0; /* can't process on DMA2 if !rxor */
135712458ea0SAnatolij Gustschin 	}
135812458ea0SAnatolij Gustschin 
135912458ea0SAnatolij Gustschin 	/* channel idleness increases the priority */
136012458ea0SAnatolij Gustschin 	if (likely(ef) &&
136112458ea0SAnatolij Gustschin 	    !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan)))
136212458ea0SAnatolij Gustschin 		ef++;
136312458ea0SAnatolij Gustschin 
136412458ea0SAnatolij Gustschin 	return ef;
136512458ea0SAnatolij Gustschin }
136612458ea0SAnatolij Gustschin 
136712458ea0SAnatolij Gustschin struct dma_chan *
ppc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,struct page ** dst_lst,int dst_cnt,struct page ** src_lst,int src_cnt,size_t src_sz)136812458ea0SAnatolij Gustschin ppc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,
136912458ea0SAnatolij Gustschin 	struct page **dst_lst, int dst_cnt, struct page **src_lst,
137012458ea0SAnatolij Gustschin 	int src_cnt, size_t src_sz)
137112458ea0SAnatolij Gustschin {
137212458ea0SAnatolij Gustschin 	struct dma_chan *best_chan = NULL;
137312458ea0SAnatolij Gustschin 	struct ppc_dma_chan_ref *ref;
137412458ea0SAnatolij Gustschin 	int best_rank = -1;
137512458ea0SAnatolij Gustschin 
137612458ea0SAnatolij Gustschin 	if (unlikely(!src_sz))
137712458ea0SAnatolij Gustschin 		return NULL;
137812458ea0SAnatolij Gustschin 	if (src_sz > PAGE_SIZE) {
137912458ea0SAnatolij Gustschin 		/*
138012458ea0SAnatolij Gustschin 		 * should a user of the api ever pass > PAGE_SIZE requests
138112458ea0SAnatolij Gustschin 		 * we sort out cases where temporary page-sized buffers
138212458ea0SAnatolij Gustschin 		 * are used.
138312458ea0SAnatolij Gustschin 		 */
138412458ea0SAnatolij Gustschin 		switch (cap) {
138512458ea0SAnatolij Gustschin 		case DMA_PQ:
138612458ea0SAnatolij Gustschin 			if (src_cnt == 1 && dst_lst[1] == src_lst[0])
138712458ea0SAnatolij Gustschin 				return NULL;
138812458ea0SAnatolij Gustschin 			if (src_cnt == 2 && dst_lst[1] == src_lst[1])
138912458ea0SAnatolij Gustschin 				return NULL;
139012458ea0SAnatolij Gustschin 			break;
139112458ea0SAnatolij Gustschin 		case DMA_PQ_VAL:
139212458ea0SAnatolij Gustschin 		case DMA_XOR_VAL:
139312458ea0SAnatolij Gustschin 			return NULL;
139412458ea0SAnatolij Gustschin 		default:
139512458ea0SAnatolij Gustschin 			break;
139612458ea0SAnatolij Gustschin 		}
139712458ea0SAnatolij Gustschin 	}
139812458ea0SAnatolij Gustschin 
139912458ea0SAnatolij Gustschin 	list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) {
140012458ea0SAnatolij Gustschin 		if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
140112458ea0SAnatolij Gustschin 			int rank;
140212458ea0SAnatolij Gustschin 
140312458ea0SAnatolij Gustschin 			rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst,
140412458ea0SAnatolij Gustschin 					dst_cnt, src_lst, src_cnt, src_sz);
140512458ea0SAnatolij Gustschin 			if (rank > best_rank) {
140612458ea0SAnatolij Gustschin 				best_rank = rank;
140712458ea0SAnatolij Gustschin 				best_chan = ref->chan;
140812458ea0SAnatolij Gustschin 			}
140912458ea0SAnatolij Gustschin 		}
141012458ea0SAnatolij Gustschin 	}
141112458ea0SAnatolij Gustschin 
141212458ea0SAnatolij Gustschin 	return best_chan;
141312458ea0SAnatolij Gustschin }
141412458ea0SAnatolij Gustschin EXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel);
141512458ea0SAnatolij Gustschin 
141612458ea0SAnatolij Gustschin /**
141712458ea0SAnatolij Gustschin  * ppc440spe_get_group_entry - get group entry with index idx
141812458ea0SAnatolij Gustschin  * @tdesc: is the last allocated slot in the group.
141912458ea0SAnatolij Gustschin  */
142012458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *
ppc440spe_get_group_entry(struct ppc440spe_adma_desc_slot * tdesc,u32 entry_idx)142112458ea0SAnatolij Gustschin ppc440spe_get_group_entry(struct ppc440spe_adma_desc_slot *tdesc, u32 entry_idx)
142212458ea0SAnatolij Gustschin {
142312458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
142412458ea0SAnatolij Gustschin 	int i = 0;
142512458ea0SAnatolij Gustschin 
142612458ea0SAnatolij Gustschin 	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
142712458ea0SAnatolij Gustschin 		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
142812458ea0SAnatolij Gustschin 			__func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
142912458ea0SAnatolij Gustschin 		BUG();
143012458ea0SAnatolij Gustschin 	}
143112458ea0SAnatolij Gustschin 
143212458ea0SAnatolij Gustschin 	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
143312458ea0SAnatolij Gustschin 		if (i++ == entry_idx)
143412458ea0SAnatolij Gustschin 			break;
143512458ea0SAnatolij Gustschin 	}
143612458ea0SAnatolij Gustschin 	return iter;
143712458ea0SAnatolij Gustschin }
143812458ea0SAnatolij Gustschin 
143912458ea0SAnatolij Gustschin /**
144012458ea0SAnatolij Gustschin  * ppc440spe_adma_free_slots - flags descriptor slots for reuse
144112458ea0SAnatolij Gustschin  * @slot: Slot to free
144212458ea0SAnatolij Gustschin  * Caller must hold &ppc440spe_chan->lock while calling this function
144312458ea0SAnatolij Gustschin  */
ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot * slot,struct ppc440spe_adma_chan * chan)144412458ea0SAnatolij Gustschin static void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
144512458ea0SAnatolij Gustschin 				      struct ppc440spe_adma_chan *chan)
144612458ea0SAnatolij Gustschin {
144712458ea0SAnatolij Gustschin 	int stride = slot->slots_per_op;
144812458ea0SAnatolij Gustschin 
144912458ea0SAnatolij Gustschin 	while (stride--) {
145012458ea0SAnatolij Gustschin 		slot->slots_per_op = 0;
145112458ea0SAnatolij Gustschin 		slot = list_entry(slot->slot_node.next,
145212458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot,
145312458ea0SAnatolij Gustschin 				slot_node);
145412458ea0SAnatolij Gustschin 	}
145512458ea0SAnatolij Gustschin }
145612458ea0SAnatolij Gustschin 
145712458ea0SAnatolij Gustschin /**
145812458ea0SAnatolij Gustschin  * ppc440spe_adma_run_tx_complete_actions - call functions to be called
145912458ea0SAnatolij Gustschin  * upon completion
146012458ea0SAnatolij Gustschin  */
ppc440spe_adma_run_tx_complete_actions(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan,dma_cookie_t cookie)146112458ea0SAnatolij Gustschin static dma_cookie_t ppc440spe_adma_run_tx_complete_actions(
146212458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
146312458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan,
146412458ea0SAnatolij Gustschin 		dma_cookie_t cookie)
146512458ea0SAnatolij Gustschin {
146612458ea0SAnatolij Gustschin 	BUG_ON(desc->async_tx.cookie < 0);
146712458ea0SAnatolij Gustschin 	if (desc->async_tx.cookie > 0) {
146812458ea0SAnatolij Gustschin 		cookie = desc->async_tx.cookie;
146912458ea0SAnatolij Gustschin 		desc->async_tx.cookie = 0;
147012458ea0SAnatolij Gustschin 
1471ed9f2c58SDave Jiang 		dma_descriptor_unmap(&desc->async_tx);
147212458ea0SAnatolij Gustschin 		/* call the callback (must not sleep or submit new
147312458ea0SAnatolij Gustschin 		 * operations to this channel)
147412458ea0SAnatolij Gustschin 		 */
147544967bf7SDave Jiang 		dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL);
147612458ea0SAnatolij Gustschin 	}
147712458ea0SAnatolij Gustschin 
147812458ea0SAnatolij Gustschin 	/* run dependent operations */
147912458ea0SAnatolij Gustschin 	dma_run_dependencies(&desc->async_tx);
148012458ea0SAnatolij Gustschin 
148112458ea0SAnatolij Gustschin 	return cookie;
148212458ea0SAnatolij Gustschin }
148312458ea0SAnatolij Gustschin 
148412458ea0SAnatolij Gustschin /**
148512458ea0SAnatolij Gustschin  * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set)
148612458ea0SAnatolij Gustschin  */
ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_adma_chan * chan)148712458ea0SAnatolij Gustschin static int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc,
148812458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan)
148912458ea0SAnatolij Gustschin {
149012458ea0SAnatolij Gustschin 	/* the client is allowed to attach dependent operations
149112458ea0SAnatolij Gustschin 	 * until 'ack' is set
149212458ea0SAnatolij Gustschin 	 */
149312458ea0SAnatolij Gustschin 	if (!async_tx_test_ack(&desc->async_tx))
149412458ea0SAnatolij Gustschin 		return 0;
149512458ea0SAnatolij Gustschin 
149612458ea0SAnatolij Gustschin 	/* leave the last descriptor in the chain
149712458ea0SAnatolij Gustschin 	 * so we can append to it
149812458ea0SAnatolij Gustschin 	 */
149912458ea0SAnatolij Gustschin 	if (list_is_last(&desc->chain_node, &chan->chain) ||
150012458ea0SAnatolij Gustschin 	    desc->phys == ppc440spe_chan_get_current_descriptor(chan))
150112458ea0SAnatolij Gustschin 		return 1;
150212458ea0SAnatolij Gustschin 
150312458ea0SAnatolij Gustschin 	if (chan->device->id != PPC440SPE_XOR_ID) {
150412458ea0SAnatolij Gustschin 		/* our DMA interrupt handler clears opc field of
150512458ea0SAnatolij Gustschin 		 * each processed descriptor. For all types of
150612458ea0SAnatolij Gustschin 		 * operations except for ZeroSum we do not actually
150712458ea0SAnatolij Gustschin 		 * need ack from the interrupt handler. ZeroSum is a
150812458ea0SAnatolij Gustschin 		 * special case since the result of this operation
150912458ea0SAnatolij Gustschin 		 * is available from the handler only, so if we see
151012458ea0SAnatolij Gustschin 		 * such type of descriptor (which is unprocessed yet)
151112458ea0SAnatolij Gustschin 		 * then leave it in chain.
151212458ea0SAnatolij Gustschin 		 */
151312458ea0SAnatolij Gustschin 		struct dma_cdb *cdb = desc->hw_desc;
151412458ea0SAnatolij Gustschin 		if (cdb->opc == DMA_CDB_OPC_DCHECK128)
151512458ea0SAnatolij Gustschin 			return 1;
151612458ea0SAnatolij Gustschin 	}
151712458ea0SAnatolij Gustschin 
151812458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n",
151912458ea0SAnatolij Gustschin 		desc->phys, desc->idx, desc->slots_per_op);
152012458ea0SAnatolij Gustschin 
152112458ea0SAnatolij Gustschin 	list_del(&desc->chain_node);
152212458ea0SAnatolij Gustschin 	ppc440spe_adma_free_slots(desc, chan);
152312458ea0SAnatolij Gustschin 	return 0;
152412458ea0SAnatolij Gustschin }
152512458ea0SAnatolij Gustschin 
152612458ea0SAnatolij Gustschin /**
152712458ea0SAnatolij Gustschin  * __ppc440spe_adma_slot_cleanup - this is the common clean-up routine
152812458ea0SAnatolij Gustschin  *	which runs through the channel CDBs list until reach the descriptor
152912458ea0SAnatolij Gustschin  *	currently processed. When routine determines that all CDBs of group
153012458ea0SAnatolij Gustschin  *	are completed then corresponding callbacks (if any) are called and slots
153112458ea0SAnatolij Gustschin  *	are freed.
153212458ea0SAnatolij Gustschin  */
__ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan * chan)153312458ea0SAnatolij Gustschin static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
153412458ea0SAnatolij Gustschin {
153512458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter, *_iter, *group_start = NULL;
153612458ea0SAnatolij Gustschin 	dma_cookie_t cookie = 0;
153712458ea0SAnatolij Gustschin 	u32 current_desc = ppc440spe_chan_get_current_descriptor(chan);
153812458ea0SAnatolij Gustschin 	int busy = ppc440spe_chan_is_busy(chan);
153912458ea0SAnatolij Gustschin 	int seen_current = 0, slot_cnt = 0, slots_per_op = 0;
154012458ea0SAnatolij Gustschin 
154112458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev, "ppc440spe adma%d: %s\n",
154212458ea0SAnatolij Gustschin 		chan->device->id, __func__);
154312458ea0SAnatolij Gustschin 
154412458ea0SAnatolij Gustschin 	if (!current_desc) {
154512458ea0SAnatolij Gustschin 		/*  There were no transactions yet, so
154612458ea0SAnatolij Gustschin 		 * nothing to clean
154712458ea0SAnatolij Gustschin 		 */
154812458ea0SAnatolij Gustschin 		return;
154912458ea0SAnatolij Gustschin 	}
155012458ea0SAnatolij Gustschin 
155112458ea0SAnatolij Gustschin 	/* free completed slots from the chain starting with
155212458ea0SAnatolij Gustschin 	 * the oldest descriptor
155312458ea0SAnatolij Gustschin 	 */
155412458ea0SAnatolij Gustschin 	list_for_each_entry_safe(iter, _iter, &chan->chain,
155512458ea0SAnatolij Gustschin 					chain_node) {
155612458ea0SAnatolij Gustschin 		dev_dbg(chan->device->common.dev, "\tcookie: %d slot: %d "
155712458ea0SAnatolij Gustschin 		    "busy: %d this_desc: %#llx next_desc: %#x "
155812458ea0SAnatolij Gustschin 		    "cur: %#x ack: %d\n",
155912458ea0SAnatolij Gustschin 		    iter->async_tx.cookie, iter->idx, busy, iter->phys,
156012458ea0SAnatolij Gustschin 		    ppc440spe_desc_get_link(iter, chan), current_desc,
156112458ea0SAnatolij Gustschin 		    async_tx_test_ack(&iter->async_tx));
156212458ea0SAnatolij Gustschin 		prefetch(_iter);
156312458ea0SAnatolij Gustschin 		prefetch(&_iter->async_tx);
156412458ea0SAnatolij Gustschin 
156512458ea0SAnatolij Gustschin 		/* do not advance past the current descriptor loaded into the
156612458ea0SAnatolij Gustschin 		 * hardware channel,subsequent descriptors are either in process
156712458ea0SAnatolij Gustschin 		 * or have not been submitted
156812458ea0SAnatolij Gustschin 		 */
156912458ea0SAnatolij Gustschin 		if (seen_current)
157012458ea0SAnatolij Gustschin 			break;
157112458ea0SAnatolij Gustschin 
157212458ea0SAnatolij Gustschin 		/* stop the search if we reach the current descriptor and the
157312458ea0SAnatolij Gustschin 		 * channel is busy, or if it appears that the current descriptor
157412458ea0SAnatolij Gustschin 		 * needs to be re-read (i.e. has been appended to)
157512458ea0SAnatolij Gustschin 		 */
157612458ea0SAnatolij Gustschin 		if (iter->phys == current_desc) {
157712458ea0SAnatolij Gustschin 			BUG_ON(seen_current++);
157812458ea0SAnatolij Gustschin 			if (busy || ppc440spe_desc_get_link(iter, chan)) {
157912458ea0SAnatolij Gustschin 				/* not all descriptors of the group have
158012458ea0SAnatolij Gustschin 				 * been completed; exit.
158112458ea0SAnatolij Gustschin 				 */
158212458ea0SAnatolij Gustschin 				break;
158312458ea0SAnatolij Gustschin 			}
158412458ea0SAnatolij Gustschin 		}
158512458ea0SAnatolij Gustschin 
158612458ea0SAnatolij Gustschin 		/* detect the start of a group transaction */
158712458ea0SAnatolij Gustschin 		if (!slot_cnt && !slots_per_op) {
158812458ea0SAnatolij Gustschin 			slot_cnt = iter->slot_cnt;
158912458ea0SAnatolij Gustschin 			slots_per_op = iter->slots_per_op;
159012458ea0SAnatolij Gustschin 			if (slot_cnt <= slots_per_op) {
159112458ea0SAnatolij Gustschin 				slot_cnt = 0;
159212458ea0SAnatolij Gustschin 				slots_per_op = 0;
159312458ea0SAnatolij Gustschin 			}
159412458ea0SAnatolij Gustschin 		}
159512458ea0SAnatolij Gustschin 
159612458ea0SAnatolij Gustschin 		if (slot_cnt) {
159712458ea0SAnatolij Gustschin 			if (!group_start)
159812458ea0SAnatolij Gustschin 				group_start = iter;
159912458ea0SAnatolij Gustschin 			slot_cnt -= slots_per_op;
160012458ea0SAnatolij Gustschin 		}
160112458ea0SAnatolij Gustschin 
160212458ea0SAnatolij Gustschin 		/* all the members of a group are complete */
160312458ea0SAnatolij Gustschin 		if (slots_per_op != 0 && slot_cnt == 0) {
160412458ea0SAnatolij Gustschin 			struct ppc440spe_adma_desc_slot *grp_iter, *_grp_iter;
160512458ea0SAnatolij Gustschin 			int end_of_chain = 0;
160612458ea0SAnatolij Gustschin 
160712458ea0SAnatolij Gustschin 			/* clean up the group */
160812458ea0SAnatolij Gustschin 			slot_cnt = group_start->slot_cnt;
160912458ea0SAnatolij Gustschin 			grp_iter = group_start;
161012458ea0SAnatolij Gustschin 			list_for_each_entry_safe_from(grp_iter, _grp_iter,
161112458ea0SAnatolij Gustschin 				&chan->chain, chain_node) {
161212458ea0SAnatolij Gustschin 
161312458ea0SAnatolij Gustschin 				cookie = ppc440spe_adma_run_tx_complete_actions(
161412458ea0SAnatolij Gustschin 					grp_iter, chan, cookie);
161512458ea0SAnatolij Gustschin 
161612458ea0SAnatolij Gustschin 				slot_cnt -= slots_per_op;
161712458ea0SAnatolij Gustschin 				end_of_chain = ppc440spe_adma_clean_slot(
161812458ea0SAnatolij Gustschin 				    grp_iter, chan);
161912458ea0SAnatolij Gustschin 				if (end_of_chain && slot_cnt) {
162012458ea0SAnatolij Gustschin 					/* Should wait for ZeroSum completion */
162112458ea0SAnatolij Gustschin 					if (cookie > 0)
16224d4e58deSRussell King - ARM Linux 						chan->common.completed_cookie = cookie;
162312458ea0SAnatolij Gustschin 					return;
162412458ea0SAnatolij Gustschin 				}
162512458ea0SAnatolij Gustschin 
162612458ea0SAnatolij Gustschin 				if (slot_cnt == 0 || end_of_chain)
162712458ea0SAnatolij Gustschin 					break;
162812458ea0SAnatolij Gustschin 			}
162912458ea0SAnatolij Gustschin 
163012458ea0SAnatolij Gustschin 			/* the group should be complete at this point */
163112458ea0SAnatolij Gustschin 			BUG_ON(slot_cnt);
163212458ea0SAnatolij Gustschin 
163312458ea0SAnatolij Gustschin 			slots_per_op = 0;
163412458ea0SAnatolij Gustschin 			group_start = NULL;
163512458ea0SAnatolij Gustschin 			if (end_of_chain)
163612458ea0SAnatolij Gustschin 				break;
163712458ea0SAnatolij Gustschin 			else
163812458ea0SAnatolij Gustschin 				continue;
163912458ea0SAnatolij Gustschin 		} else if (slots_per_op) /* wait for group completion */
164012458ea0SAnatolij Gustschin 			continue;
164112458ea0SAnatolij Gustschin 
164212458ea0SAnatolij Gustschin 		cookie = ppc440spe_adma_run_tx_complete_actions(iter, chan,
164312458ea0SAnatolij Gustschin 		    cookie);
164412458ea0SAnatolij Gustschin 
164512458ea0SAnatolij Gustschin 		if (ppc440spe_adma_clean_slot(iter, chan))
164612458ea0SAnatolij Gustschin 			break;
164712458ea0SAnatolij Gustschin 	}
164812458ea0SAnatolij Gustschin 
164912458ea0SAnatolij Gustschin 	BUG_ON(!seen_current);
165012458ea0SAnatolij Gustschin 
165112458ea0SAnatolij Gustschin 	if (cookie > 0) {
16524d4e58deSRussell King - ARM Linux 		chan->common.completed_cookie = cookie;
165312458ea0SAnatolij Gustschin 		pr_debug("\tcompleted cookie %d\n", cookie);
165412458ea0SAnatolij Gustschin 	}
165512458ea0SAnatolij Gustschin 
165612458ea0SAnatolij Gustschin }
165712458ea0SAnatolij Gustschin 
165812458ea0SAnatolij Gustschin /**
165912458ea0SAnatolij Gustschin  * ppc440spe_adma_tasklet - clean up watch-dog initiator
166012458ea0SAnatolij Gustschin  */
ppc440spe_adma_tasklet(struct tasklet_struct * t)16617f828176SAllen Pais static void ppc440spe_adma_tasklet(struct tasklet_struct *t)
166212458ea0SAnatolij Gustschin {
16637f828176SAllen Pais 	struct ppc440spe_adma_chan *chan = from_tasklet(chan, t, irq_tasklet);
166412458ea0SAnatolij Gustschin 
166512458ea0SAnatolij Gustschin 	spin_lock_nested(&chan->lock, SINGLE_DEPTH_NESTING);
166612458ea0SAnatolij Gustschin 	__ppc440spe_adma_slot_cleanup(chan);
166712458ea0SAnatolij Gustschin 	spin_unlock(&chan->lock);
166812458ea0SAnatolij Gustschin }
166912458ea0SAnatolij Gustschin 
167012458ea0SAnatolij Gustschin /**
167112458ea0SAnatolij Gustschin  * ppc440spe_adma_slot_cleanup - clean up scheduled initiator
167212458ea0SAnatolij Gustschin  */
ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan * chan)167312458ea0SAnatolij Gustschin static void ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
167412458ea0SAnatolij Gustschin {
167512458ea0SAnatolij Gustschin 	spin_lock_bh(&chan->lock);
167612458ea0SAnatolij Gustschin 	__ppc440spe_adma_slot_cleanup(chan);
167712458ea0SAnatolij Gustschin 	spin_unlock_bh(&chan->lock);
167812458ea0SAnatolij Gustschin }
167912458ea0SAnatolij Gustschin 
168012458ea0SAnatolij Gustschin /**
168112458ea0SAnatolij Gustschin  * ppc440spe_adma_alloc_slots - allocate free slots (if any)
168212458ea0SAnatolij Gustschin  */
ppc440spe_adma_alloc_slots(struct ppc440spe_adma_chan * chan,int num_slots,int slots_per_op)168312458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
168412458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan, int num_slots,
168512458ea0SAnatolij Gustschin 		int slots_per_op)
168612458ea0SAnatolij Gustschin {
168712458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter = NULL, *_iter;
168812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *alloc_start = NULL;
168912458ea0SAnatolij Gustschin 	int slots_found, retry = 0;
1690417c7d0dSCai Huoqing 	LIST_HEAD(chain);
169112458ea0SAnatolij Gustschin 
169212458ea0SAnatolij Gustschin 
169312458ea0SAnatolij Gustschin 	BUG_ON(!num_slots || !slots_per_op);
169412458ea0SAnatolij Gustschin 	/* start search from the last allocated descrtiptor
169512458ea0SAnatolij Gustschin 	 * if a contiguous allocation can not be found start searching
169612458ea0SAnatolij Gustschin 	 * from the beginning of the list
169712458ea0SAnatolij Gustschin 	 */
169812458ea0SAnatolij Gustschin retry:
169912458ea0SAnatolij Gustschin 	slots_found = 0;
170012458ea0SAnatolij Gustschin 	if (retry == 0)
170112458ea0SAnatolij Gustschin 		iter = chan->last_used;
170212458ea0SAnatolij Gustschin 	else
170312458ea0SAnatolij Gustschin 		iter = list_entry(&chan->all_slots,
170412458ea0SAnatolij Gustschin 				  struct ppc440spe_adma_desc_slot,
170512458ea0SAnatolij Gustschin 				  slot_node);
170612458ea0SAnatolij Gustschin 	list_for_each_entry_safe_continue(iter, _iter, &chan->all_slots,
170712458ea0SAnatolij Gustschin 	    slot_node) {
170812458ea0SAnatolij Gustschin 		prefetch(_iter);
170912458ea0SAnatolij Gustschin 		prefetch(&_iter->async_tx);
171012458ea0SAnatolij Gustschin 		if (iter->slots_per_op) {
171112458ea0SAnatolij Gustschin 			slots_found = 0;
171212458ea0SAnatolij Gustschin 			continue;
171312458ea0SAnatolij Gustschin 		}
171412458ea0SAnatolij Gustschin 
171512458ea0SAnatolij Gustschin 		/* start the allocation if the slot is correctly aligned */
171612458ea0SAnatolij Gustschin 		if (!slots_found++)
171712458ea0SAnatolij Gustschin 			alloc_start = iter;
171812458ea0SAnatolij Gustschin 
171912458ea0SAnatolij Gustschin 		if (slots_found == num_slots) {
172012458ea0SAnatolij Gustschin 			struct ppc440spe_adma_desc_slot *alloc_tail = NULL;
172112458ea0SAnatolij Gustschin 			struct ppc440spe_adma_desc_slot *last_used = NULL;
172212458ea0SAnatolij Gustschin 
172312458ea0SAnatolij Gustschin 			iter = alloc_start;
172412458ea0SAnatolij Gustschin 			while (num_slots) {
172512458ea0SAnatolij Gustschin 				int i;
172612458ea0SAnatolij Gustschin 				/* pre-ack all but the last descriptor */
172712458ea0SAnatolij Gustschin 				if (num_slots != slots_per_op)
172812458ea0SAnatolij Gustschin 					async_tx_ack(&iter->async_tx);
172912458ea0SAnatolij Gustschin 
173012458ea0SAnatolij Gustschin 				list_add_tail(&iter->chain_node, &chain);
173112458ea0SAnatolij Gustschin 				alloc_tail = iter;
173212458ea0SAnatolij Gustschin 				iter->async_tx.cookie = 0;
173312458ea0SAnatolij Gustschin 				iter->hw_next = NULL;
173412458ea0SAnatolij Gustschin 				iter->flags = 0;
173512458ea0SAnatolij Gustschin 				iter->slot_cnt = num_slots;
173612458ea0SAnatolij Gustschin 				iter->xor_check_result = NULL;
173712458ea0SAnatolij Gustschin 				for (i = 0; i < slots_per_op; i++) {
173812458ea0SAnatolij Gustschin 					iter->slots_per_op = slots_per_op - i;
173912458ea0SAnatolij Gustschin 					last_used = iter;
174012458ea0SAnatolij Gustschin 					iter = list_entry(iter->slot_node.next,
174112458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
174212458ea0SAnatolij Gustschin 						slot_node);
174312458ea0SAnatolij Gustschin 				}
174412458ea0SAnatolij Gustschin 				num_slots -= slots_per_op;
174512458ea0SAnatolij Gustschin 			}
174612458ea0SAnatolij Gustschin 			alloc_tail->group_head = alloc_start;
174712458ea0SAnatolij Gustschin 			alloc_tail->async_tx.cookie = -EBUSY;
174812458ea0SAnatolij Gustschin 			list_splice(&chain, &alloc_tail->group_list);
174912458ea0SAnatolij Gustschin 			chan->last_used = last_used;
175012458ea0SAnatolij Gustschin 			return alloc_tail;
175112458ea0SAnatolij Gustschin 		}
175212458ea0SAnatolij Gustschin 	}
175312458ea0SAnatolij Gustschin 	if (!retry++)
175412458ea0SAnatolij Gustschin 		goto retry;
175512458ea0SAnatolij Gustschin 
175612458ea0SAnatolij Gustschin 	/* try to free some slots if the allocation fails */
175712458ea0SAnatolij Gustschin 	tasklet_schedule(&chan->irq_tasklet);
175812458ea0SAnatolij Gustschin 	return NULL;
175912458ea0SAnatolij Gustschin }
176012458ea0SAnatolij Gustschin 
176112458ea0SAnatolij Gustschin /**
176212458ea0SAnatolij Gustschin  * ppc440spe_adma_alloc_chan_resources -  allocate pools for CDB slots
176312458ea0SAnatolij Gustschin  */
ppc440spe_adma_alloc_chan_resources(struct dma_chan * chan)176412458ea0SAnatolij Gustschin static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan)
176512458ea0SAnatolij Gustschin {
176612458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
176712458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *slot = NULL;
176812458ea0SAnatolij Gustschin 	char *hw_desc;
176912458ea0SAnatolij Gustschin 	int i, db_sz;
177012458ea0SAnatolij Gustschin 	int init;
177112458ea0SAnatolij Gustschin 
177212458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
177312458ea0SAnatolij Gustschin 	init = ppc440spe_chan->slots_allocated ? 0 : 1;
177412458ea0SAnatolij Gustschin 	chan->chan_id = ppc440spe_chan->device->id;
177512458ea0SAnatolij Gustschin 
177612458ea0SAnatolij Gustschin 	/* Allocate descriptor slots */
177712458ea0SAnatolij Gustschin 	i = ppc440spe_chan->slots_allocated;
177812458ea0SAnatolij Gustschin 	if (ppc440spe_chan->device->id != PPC440SPE_XOR_ID)
177912458ea0SAnatolij Gustschin 		db_sz = sizeof(struct dma_cdb);
178012458ea0SAnatolij Gustschin 	else
178112458ea0SAnatolij Gustschin 		db_sz = sizeof(struct xor_cb);
178212458ea0SAnatolij Gustschin 
178312458ea0SAnatolij Gustschin 	for (; i < (ppc440spe_chan->device->pool_size / db_sz); i++) {
178412458ea0SAnatolij Gustschin 		slot = kzalloc(sizeof(struct ppc440spe_adma_desc_slot),
178512458ea0SAnatolij Gustschin 			       GFP_KERNEL);
178612458ea0SAnatolij Gustschin 		if (!slot) {
178712458ea0SAnatolij Gustschin 			printk(KERN_INFO "SPE ADMA Channel only initialized"
178812458ea0SAnatolij Gustschin 				" %d descriptor slots", i--);
178912458ea0SAnatolij Gustschin 			break;
179012458ea0SAnatolij Gustschin 		}
179112458ea0SAnatolij Gustschin 
179212458ea0SAnatolij Gustschin 		hw_desc = (char *) ppc440spe_chan->device->dma_desc_pool_virt;
179312458ea0SAnatolij Gustschin 		slot->hw_desc = (void *) &hw_desc[i * db_sz];
179412458ea0SAnatolij Gustschin 		dma_async_tx_descriptor_init(&slot->async_tx, chan);
179512458ea0SAnatolij Gustschin 		slot->async_tx.tx_submit = ppc440spe_adma_tx_submit;
179612458ea0SAnatolij Gustschin 		INIT_LIST_HEAD(&slot->chain_node);
179712458ea0SAnatolij Gustschin 		INIT_LIST_HEAD(&slot->slot_node);
179812458ea0SAnatolij Gustschin 		INIT_LIST_HEAD(&slot->group_list);
179912458ea0SAnatolij Gustschin 		slot->phys = ppc440spe_chan->device->dma_desc_pool + i * db_sz;
180012458ea0SAnatolij Gustschin 		slot->idx = i;
180112458ea0SAnatolij Gustschin 
180212458ea0SAnatolij Gustschin 		spin_lock_bh(&ppc440spe_chan->lock);
180312458ea0SAnatolij Gustschin 		ppc440spe_chan->slots_allocated++;
180412458ea0SAnatolij Gustschin 		list_add_tail(&slot->slot_node, &ppc440spe_chan->all_slots);
180512458ea0SAnatolij Gustschin 		spin_unlock_bh(&ppc440spe_chan->lock);
180612458ea0SAnatolij Gustschin 	}
180712458ea0SAnatolij Gustschin 
180812458ea0SAnatolij Gustschin 	if (i && !ppc440spe_chan->last_used) {
180912458ea0SAnatolij Gustschin 		ppc440spe_chan->last_used =
181012458ea0SAnatolij Gustschin 			list_entry(ppc440spe_chan->all_slots.next,
181112458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot,
181212458ea0SAnatolij Gustschin 				slot_node);
181312458ea0SAnatolij Gustschin 	}
181412458ea0SAnatolij Gustschin 
181512458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
181612458ea0SAnatolij Gustschin 		"ppc440spe adma%d: allocated %d descriptor slots\n",
181712458ea0SAnatolij Gustschin 		ppc440spe_chan->device->id, i);
181812458ea0SAnatolij Gustschin 
181912458ea0SAnatolij Gustschin 	/* initialize the channel and the chain with a null operation */
182012458ea0SAnatolij Gustschin 	if (init) {
182112458ea0SAnatolij Gustschin 		switch (ppc440spe_chan->device->id) {
182212458ea0SAnatolij Gustschin 		case PPC440SPE_DMA0_ID:
182312458ea0SAnatolij Gustschin 		case PPC440SPE_DMA1_ID:
182412458ea0SAnatolij Gustschin 			ppc440spe_chan->hw_chain_inited = 0;
182512458ea0SAnatolij Gustschin 			/* Use WXOR for self-testing */
182612458ea0SAnatolij Gustschin 			if (!ppc440spe_r6_tchan)
182712458ea0SAnatolij Gustschin 				ppc440spe_r6_tchan = ppc440spe_chan;
182812458ea0SAnatolij Gustschin 			break;
182912458ea0SAnatolij Gustschin 		case PPC440SPE_XOR_ID:
183012458ea0SAnatolij Gustschin 			ppc440spe_chan_start_null_xor(ppc440spe_chan);
183112458ea0SAnatolij Gustschin 			break;
183212458ea0SAnatolij Gustschin 		default:
183312458ea0SAnatolij Gustschin 			BUG();
183412458ea0SAnatolij Gustschin 		}
183512458ea0SAnatolij Gustschin 		ppc440spe_chan->needs_unmap = 1;
183612458ea0SAnatolij Gustschin 	}
183712458ea0SAnatolij Gustschin 
183812458ea0SAnatolij Gustschin 	return (i > 0) ? i : -ENOMEM;
183912458ea0SAnatolij Gustschin }
184012458ea0SAnatolij Gustschin 
184112458ea0SAnatolij Gustschin /**
184212458ea0SAnatolij Gustschin  * ppc440spe_rxor_set_region_data -
184312458ea0SAnatolij Gustschin  */
ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot * desc,u8 xor_arg_no,u32 mask)184412458ea0SAnatolij Gustschin static void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
184512458ea0SAnatolij Gustschin 	u8 xor_arg_no, u32 mask)
184612458ea0SAnatolij Gustschin {
184712458ea0SAnatolij Gustschin 	struct xor_cb *xcb = desc->hw_desc;
184812458ea0SAnatolij Gustschin 
184912458ea0SAnatolij Gustschin 	xcb->ops[xor_arg_no].h |= mask;
185012458ea0SAnatolij Gustschin }
185112458ea0SAnatolij Gustschin 
185212458ea0SAnatolij Gustschin /**
185312458ea0SAnatolij Gustschin  * ppc440spe_rxor_set_src -
185412458ea0SAnatolij Gustschin  */
ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot * desc,u8 xor_arg_no,dma_addr_t addr)185512458ea0SAnatolij Gustschin static void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc,
185612458ea0SAnatolij Gustschin 	u8 xor_arg_no, dma_addr_t addr)
185712458ea0SAnatolij Gustschin {
185812458ea0SAnatolij Gustschin 	struct xor_cb *xcb = desc->hw_desc;
185912458ea0SAnatolij Gustschin 
186012458ea0SAnatolij Gustschin 	xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE;
186112458ea0SAnatolij Gustschin 	xcb->ops[xor_arg_no].l = addr;
186212458ea0SAnatolij Gustschin }
186312458ea0SAnatolij Gustschin 
186412458ea0SAnatolij Gustschin /**
186512458ea0SAnatolij Gustschin  * ppc440spe_rxor_set_mult -
186612458ea0SAnatolij Gustschin  */
ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot * desc,u8 xor_arg_no,u8 idx,u8 mult)186712458ea0SAnatolij Gustschin static void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
186812458ea0SAnatolij Gustschin 	u8 xor_arg_no, u8 idx, u8 mult)
186912458ea0SAnatolij Gustschin {
187012458ea0SAnatolij Gustschin 	struct xor_cb *xcb = desc->hw_desc;
187112458ea0SAnatolij Gustschin 
187212458ea0SAnatolij Gustschin 	xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8);
187312458ea0SAnatolij Gustschin }
187412458ea0SAnatolij Gustschin 
187512458ea0SAnatolij Gustschin /**
187612458ea0SAnatolij Gustschin  * ppc440spe_adma_check_threshold - append CDBs to h/w chain if threshold
187712458ea0SAnatolij Gustschin  *	has been achieved
187812458ea0SAnatolij Gustschin  */
ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan * chan)187912458ea0SAnatolij Gustschin static void ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan *chan)
188012458ea0SAnatolij Gustschin {
188112458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev, "ppc440spe adma%d: pending: %d\n",
188212458ea0SAnatolij Gustschin 		chan->device->id, chan->pending);
188312458ea0SAnatolij Gustschin 
188412458ea0SAnatolij Gustschin 	if (chan->pending >= PPC440SPE_ADMA_THRESHOLD) {
188512458ea0SAnatolij Gustschin 		chan->pending = 0;
188612458ea0SAnatolij Gustschin 		ppc440spe_chan_append(chan);
188712458ea0SAnatolij Gustschin 	}
188812458ea0SAnatolij Gustschin }
188912458ea0SAnatolij Gustschin 
189012458ea0SAnatolij Gustschin /**
189112458ea0SAnatolij Gustschin  * ppc440spe_adma_tx_submit - submit new descriptor group to the channel
189212458ea0SAnatolij Gustschin  *	(it's not necessary that descriptors will be submitted to the h/w
189312458ea0SAnatolij Gustschin  *	chains too right now)
189412458ea0SAnatolij Gustschin  */
ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor * tx)189512458ea0SAnatolij Gustschin static dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
189612458ea0SAnatolij Gustschin {
189712458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc;
189812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(tx->chan);
189912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *group_start, *old_chain_tail;
190012458ea0SAnatolij Gustschin 	int slot_cnt;
190112458ea0SAnatolij Gustschin 	int slots_per_op;
190212458ea0SAnatolij Gustschin 	dma_cookie_t cookie;
190312458ea0SAnatolij Gustschin 
190412458ea0SAnatolij Gustschin 	sw_desc = tx_to_ppc440spe_adma_slot(tx);
190512458ea0SAnatolij Gustschin 
190612458ea0SAnatolij Gustschin 	group_start = sw_desc->group_head;
190712458ea0SAnatolij Gustschin 	slot_cnt = group_start->slot_cnt;
190812458ea0SAnatolij Gustschin 	slots_per_op = group_start->slots_per_op;
190912458ea0SAnatolij Gustschin 
191012458ea0SAnatolij Gustschin 	spin_lock_bh(&chan->lock);
1911884485e1SRussell King - ARM Linux 	cookie = dma_cookie_assign(tx);
191212458ea0SAnatolij Gustschin 
191312458ea0SAnatolij Gustschin 	if (unlikely(list_empty(&chan->chain))) {
191412458ea0SAnatolij Gustschin 		/* first peer */
191512458ea0SAnatolij Gustschin 		list_splice_init(&sw_desc->group_list, &chan->chain);
191612458ea0SAnatolij Gustschin 		chan_first_cdb[chan->device->id] = group_start;
191712458ea0SAnatolij Gustschin 	} else {
191812458ea0SAnatolij Gustschin 		/* isn't first peer, bind CDBs to chain */
191912458ea0SAnatolij Gustschin 		old_chain_tail = list_entry(chan->chain.prev,
192012458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
192112458ea0SAnatolij Gustschin 					chain_node);
192212458ea0SAnatolij Gustschin 		list_splice_init(&sw_desc->group_list,
192312458ea0SAnatolij Gustschin 		    &old_chain_tail->chain_node);
192412458ea0SAnatolij Gustschin 		/* fix up the hardware chain */
192512458ea0SAnatolij Gustschin 		ppc440spe_desc_set_link(chan, old_chain_tail, group_start);
192612458ea0SAnatolij Gustschin 	}
192712458ea0SAnatolij Gustschin 
192812458ea0SAnatolij Gustschin 	/* increment the pending count by the number of operations */
192912458ea0SAnatolij Gustschin 	chan->pending += slot_cnt / slots_per_op;
193012458ea0SAnatolij Gustschin 	ppc440spe_adma_check_threshold(chan);
193112458ea0SAnatolij Gustschin 	spin_unlock_bh(&chan->lock);
193212458ea0SAnatolij Gustschin 
193312458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev,
193412458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s cookie: %d slot: %d tx %p\n",
193512458ea0SAnatolij Gustschin 		chan->device->id, __func__,
193612458ea0SAnatolij Gustschin 		sw_desc->async_tx.cookie, sw_desc->idx, sw_desc);
193712458ea0SAnatolij Gustschin 
193812458ea0SAnatolij Gustschin 	return cookie;
193912458ea0SAnatolij Gustschin }
194012458ea0SAnatolij Gustschin 
194112458ea0SAnatolij Gustschin /**
194212458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_interrupt - prepare CDB for a pseudo DMA operation
194312458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_interrupt(struct dma_chan * chan,unsigned long flags)194412458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_interrupt(
194512458ea0SAnatolij Gustschin 		struct dma_chan *chan, unsigned long flags)
194612458ea0SAnatolij Gustschin {
194712458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
194812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
194912458ea0SAnatolij Gustschin 	int slot_cnt, slots_per_op;
195012458ea0SAnatolij Gustschin 
195112458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
195212458ea0SAnatolij Gustschin 
195312458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
195412458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s\n", ppc440spe_chan->device->id,
195512458ea0SAnatolij Gustschin 		__func__);
195612458ea0SAnatolij Gustschin 
195712458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
195812458ea0SAnatolij Gustschin 	slot_cnt = slots_per_op = 1;
195912458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
196012458ea0SAnatolij Gustschin 			slots_per_op);
196112458ea0SAnatolij Gustschin 	if (sw_desc) {
196212458ea0SAnatolij Gustschin 		group_start = sw_desc->group_head;
196312458ea0SAnatolij Gustschin 		ppc440spe_desc_init_interrupt(group_start, ppc440spe_chan);
196412458ea0SAnatolij Gustschin 		group_start->unmap_len = 0;
196512458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
196612458ea0SAnatolij Gustschin 	}
196712458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
196812458ea0SAnatolij Gustschin 
196912458ea0SAnatolij Gustschin 	return sw_desc ? &sw_desc->async_tx : NULL;
197012458ea0SAnatolij Gustschin }
197112458ea0SAnatolij Gustschin 
197212458ea0SAnatolij Gustschin /**
197312458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_memcpy - prepare CDB for a MEMCPY operation
197412458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_memcpy(struct dma_chan * chan,dma_addr_t dma_dest,dma_addr_t dma_src,size_t len,unsigned long flags)197512458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy(
197612458ea0SAnatolij Gustschin 		struct dma_chan *chan, dma_addr_t dma_dest,
197712458ea0SAnatolij Gustschin 		dma_addr_t dma_src, size_t len, unsigned long flags)
197812458ea0SAnatolij Gustschin {
197912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
198012458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
198112458ea0SAnatolij Gustschin 	int slot_cnt, slots_per_op;
198212458ea0SAnatolij Gustschin 
198312458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
198412458ea0SAnatolij Gustschin 
198512458ea0SAnatolij Gustschin 	if (unlikely(!len))
198612458ea0SAnatolij Gustschin 		return NULL;
198712458ea0SAnatolij Gustschin 
1988427cdf19SColy Li 	BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT);
198912458ea0SAnatolij Gustschin 
199012458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
199112458ea0SAnatolij Gustschin 
199212458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
199312458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s len: %u int_en %d\n",
199412458ea0SAnatolij Gustschin 		ppc440spe_chan->device->id, __func__, len,
199512458ea0SAnatolij Gustschin 		flags & DMA_PREP_INTERRUPT ? 1 : 0);
199612458ea0SAnatolij Gustschin 	slot_cnt = slots_per_op = 1;
199712458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
199812458ea0SAnatolij Gustschin 		slots_per_op);
199912458ea0SAnatolij Gustschin 	if (sw_desc) {
200012458ea0SAnatolij Gustschin 		group_start = sw_desc->group_head;
200112458ea0SAnatolij Gustschin 		ppc440spe_desc_init_memcpy(group_start, flags);
200212458ea0SAnatolij Gustschin 		ppc440spe_adma_set_dest(group_start, dma_dest, 0);
200312458ea0SAnatolij Gustschin 		ppc440spe_adma_memcpy_xor_set_src(group_start, dma_src, 0);
200412458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
200512458ea0SAnatolij Gustschin 		sw_desc->unmap_len = len;
200612458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
200712458ea0SAnatolij Gustschin 	}
200812458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
200912458ea0SAnatolij Gustschin 
201012458ea0SAnatolij Gustschin 	return sw_desc ? &sw_desc->async_tx : NULL;
201112458ea0SAnatolij Gustschin }
201212458ea0SAnatolij Gustschin 
201312458ea0SAnatolij Gustschin /**
201412458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_xor - prepare CDB for a XOR operation
201512458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_xor(struct dma_chan * chan,dma_addr_t dma_dest,dma_addr_t * dma_src,u32 src_cnt,size_t len,unsigned long flags)201612458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor(
201712458ea0SAnatolij Gustschin 		struct dma_chan *chan, dma_addr_t dma_dest,
201812458ea0SAnatolij Gustschin 		dma_addr_t *dma_src, u32 src_cnt, size_t len,
201912458ea0SAnatolij Gustschin 		unsigned long flags)
202012458ea0SAnatolij Gustschin {
202112458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
202212458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
202312458ea0SAnatolij Gustschin 	int slot_cnt, slots_per_op;
202412458ea0SAnatolij Gustschin 
202512458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
202612458ea0SAnatolij Gustschin 
202712458ea0SAnatolij Gustschin 	ADMA_LL_DBG(prep_dma_xor_dbg(ppc440spe_chan->device->id,
202812458ea0SAnatolij Gustschin 				     dma_dest, dma_src, src_cnt));
202912458ea0SAnatolij Gustschin 	if (unlikely(!len))
203012458ea0SAnatolij Gustschin 		return NULL;
2031427cdf19SColy Li 	BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
203212458ea0SAnatolij Gustschin 
203312458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
203412458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
203512458ea0SAnatolij Gustschin 		ppc440spe_chan->device->id, __func__, src_cnt, len,
203612458ea0SAnatolij Gustschin 		flags & DMA_PREP_INTERRUPT ? 1 : 0);
203712458ea0SAnatolij Gustschin 
203812458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
203912458ea0SAnatolij Gustschin 	slot_cnt = ppc440spe_chan_xor_slot_count(len, src_cnt, &slots_per_op);
204012458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
204112458ea0SAnatolij Gustschin 			slots_per_op);
204212458ea0SAnatolij Gustschin 	if (sw_desc) {
204312458ea0SAnatolij Gustschin 		group_start = sw_desc->group_head;
204412458ea0SAnatolij Gustschin 		ppc440spe_desc_init_xor(group_start, src_cnt, flags);
204512458ea0SAnatolij Gustschin 		ppc440spe_adma_set_dest(group_start, dma_dest, 0);
204612458ea0SAnatolij Gustschin 		while (src_cnt--)
204712458ea0SAnatolij Gustschin 			ppc440spe_adma_memcpy_xor_set_src(group_start,
204812458ea0SAnatolij Gustschin 				dma_src[src_cnt], src_cnt);
204912458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
205012458ea0SAnatolij Gustschin 		sw_desc->unmap_len = len;
205112458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
205212458ea0SAnatolij Gustschin 	}
205312458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
205412458ea0SAnatolij Gustschin 
205512458ea0SAnatolij Gustschin 	return sw_desc ? &sw_desc->async_tx : NULL;
205612458ea0SAnatolij Gustschin }
205712458ea0SAnatolij Gustschin 
205812458ea0SAnatolij Gustschin static inline void
205912458ea0SAnatolij Gustschin ppc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot *desc,
206012458ea0SAnatolij Gustschin 				int src_cnt);
206112458ea0SAnatolij Gustschin static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor);
206212458ea0SAnatolij Gustschin 
206312458ea0SAnatolij Gustschin /**
206412458ea0SAnatolij Gustschin  * ppc440spe_adma_init_dma2rxor_slot -
206512458ea0SAnatolij Gustschin  */
ppc440spe_adma_init_dma2rxor_slot(struct ppc440spe_adma_desc_slot * desc,dma_addr_t * src,int src_cnt)206612458ea0SAnatolij Gustschin static void ppc440spe_adma_init_dma2rxor_slot(
206712458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
206812458ea0SAnatolij Gustschin 		dma_addr_t *src, int src_cnt)
206912458ea0SAnatolij Gustschin {
207012458ea0SAnatolij Gustschin 	int i;
207112458ea0SAnatolij Gustschin 
207212458ea0SAnatolij Gustschin 	/* initialize CDB */
207312458ea0SAnatolij Gustschin 	for (i = 0; i < src_cnt; i++) {
207412458ea0SAnatolij Gustschin 		ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i,
207512458ea0SAnatolij Gustschin 						 desc->src_cnt, (u32)src[i]);
207612458ea0SAnatolij Gustschin 	}
207712458ea0SAnatolij Gustschin }
207812458ea0SAnatolij Gustschin 
207912458ea0SAnatolij Gustschin /**
208012458ea0SAnatolij Gustschin  * ppc440spe_dma01_prep_mult -
208112458ea0SAnatolij Gustschin  * for Q operation where destination is also the source
208212458ea0SAnatolij Gustschin  */
ppc440spe_dma01_prep_mult(struct ppc440spe_adma_chan * ppc440spe_chan,dma_addr_t * dst,int dst_cnt,dma_addr_t * src,int src_cnt,const unsigned char * scf,size_t len,unsigned long flags)208312458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_mult(
208412458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *ppc440spe_chan,
208512458ea0SAnatolij Gustschin 		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
208612458ea0SAnatolij Gustschin 		const unsigned char *scf, size_t len, unsigned long flags)
208712458ea0SAnatolij Gustschin {
208812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
208912458ea0SAnatolij Gustschin 	unsigned long op = 0;
209012458ea0SAnatolij Gustschin 	int slot_cnt;
209112458ea0SAnatolij Gustschin 
209212458ea0SAnatolij Gustschin 	set_bit(PPC440SPE_DESC_WXOR, &op);
209312458ea0SAnatolij Gustschin 	slot_cnt = 2;
209412458ea0SAnatolij Gustschin 
209512458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
209612458ea0SAnatolij Gustschin 
209712458ea0SAnatolij Gustschin 	/* use WXOR, each descriptor occupies one slot */
209812458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
209912458ea0SAnatolij Gustschin 	if (sw_desc) {
210012458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan;
210112458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *iter;
210212458ea0SAnatolij Gustschin 		struct dma_cdb *hw_desc;
210312458ea0SAnatolij Gustschin 
210412458ea0SAnatolij Gustschin 		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
210512458ea0SAnatolij Gustschin 		set_bits(op, &sw_desc->flags);
210612458ea0SAnatolij Gustschin 		sw_desc->src_cnt = src_cnt;
210712458ea0SAnatolij Gustschin 		sw_desc->dst_cnt = dst_cnt;
210812458ea0SAnatolij Gustschin 		/* First descriptor, zero data in the destination and copy it
210912458ea0SAnatolij Gustschin 		 * to q page using MULTICAST transfer.
211012458ea0SAnatolij Gustschin 		 */
211112458ea0SAnatolij Gustschin 		iter = list_first_entry(&sw_desc->group_list,
211212458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
211312458ea0SAnatolij Gustschin 					chain_node);
211412458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
211512458ea0SAnatolij Gustschin 		/* set 'next' pointer */
211612458ea0SAnatolij Gustschin 		iter->hw_next = list_entry(iter->chain_node.next,
211712458ea0SAnatolij Gustschin 					   struct ppc440spe_adma_desc_slot,
211812458ea0SAnatolij Gustschin 					   chain_node);
211912458ea0SAnatolij Gustschin 		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
212012458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
212112458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
212212458ea0SAnatolij Gustschin 
212312458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan,
212412458ea0SAnatolij Gustschin 					     DMA_CUED_XOR_BASE, dst[0], 0);
212512458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1);
212612458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
212712458ea0SAnatolij Gustschin 					    src[0]);
212812458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
212912458ea0SAnatolij Gustschin 		iter->unmap_len = len;
213012458ea0SAnatolij Gustschin 
213112458ea0SAnatolij Gustschin 		/*
213212458ea0SAnatolij Gustschin 		 * Second descriptor, multiply data from the q page
213312458ea0SAnatolij Gustschin 		 * and store the result in real destination.
213412458ea0SAnatolij Gustschin 		 */
213512458ea0SAnatolij Gustschin 		iter = list_first_entry(&iter->chain_node,
213612458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
213712458ea0SAnatolij Gustschin 					chain_node);
213812458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
213912458ea0SAnatolij Gustschin 		iter->hw_next = NULL;
214012458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_INTERRUPT)
214112458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_DESC_INT, &iter->flags);
214212458ea0SAnatolij Gustschin 		else
214312458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
214412458ea0SAnatolij Gustschin 
214512458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
214612458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
214712458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(iter, chan, 0,
214812458ea0SAnatolij Gustschin 					    DMA_CUED_XOR_HB, dst[1]);
214912458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan,
215012458ea0SAnatolij Gustschin 					     DMA_CUED_XOR_BASE, dst[0], 0);
215112458ea0SAnatolij Gustschin 
215212458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
215312458ea0SAnatolij Gustschin 					    DMA_CDB_SG_DST1, scf[0]);
215412458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
215512458ea0SAnatolij Gustschin 		iter->unmap_len = len;
215612458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
215712458ea0SAnatolij Gustschin 	}
215812458ea0SAnatolij Gustschin 
215912458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
216012458ea0SAnatolij Gustschin 
216112458ea0SAnatolij Gustschin 	return sw_desc;
216212458ea0SAnatolij Gustschin }
216312458ea0SAnatolij Gustschin 
216412458ea0SAnatolij Gustschin /**
216512458ea0SAnatolij Gustschin  * ppc440spe_dma01_prep_sum_product -
216612458ea0SAnatolij Gustschin  * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also
216712458ea0SAnatolij Gustschin  * the source.
216812458ea0SAnatolij Gustschin  */
ppc440spe_dma01_prep_sum_product(struct ppc440spe_adma_chan * ppc440spe_chan,dma_addr_t * dst,dma_addr_t * src,int src_cnt,const unsigned char * scf,size_t len,unsigned long flags)216912458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_sum_product(
217012458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *ppc440spe_chan,
217112458ea0SAnatolij Gustschin 		dma_addr_t *dst, dma_addr_t *src, int src_cnt,
217212458ea0SAnatolij Gustschin 		const unsigned char *scf, size_t len, unsigned long flags)
217312458ea0SAnatolij Gustschin {
217412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
217512458ea0SAnatolij Gustschin 	unsigned long op = 0;
217612458ea0SAnatolij Gustschin 	int slot_cnt;
217712458ea0SAnatolij Gustschin 
217812458ea0SAnatolij Gustschin 	set_bit(PPC440SPE_DESC_WXOR, &op);
217912458ea0SAnatolij Gustschin 	slot_cnt = 3;
218012458ea0SAnatolij Gustschin 
218112458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
218212458ea0SAnatolij Gustschin 
218312458ea0SAnatolij Gustschin 	/* WXOR, each descriptor occupies one slot */
218412458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
218512458ea0SAnatolij Gustschin 	if (sw_desc) {
218612458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan;
218712458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *iter;
218812458ea0SAnatolij Gustschin 		struct dma_cdb *hw_desc;
218912458ea0SAnatolij Gustschin 
219012458ea0SAnatolij Gustschin 		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
219112458ea0SAnatolij Gustschin 		set_bits(op, &sw_desc->flags);
219212458ea0SAnatolij Gustschin 		sw_desc->src_cnt = src_cnt;
219312458ea0SAnatolij Gustschin 		sw_desc->dst_cnt = 1;
219412458ea0SAnatolij Gustschin 		/* 1st descriptor, src[1] data to q page and zero destination */
219512458ea0SAnatolij Gustschin 		iter = list_first_entry(&sw_desc->group_list,
219612458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
219712458ea0SAnatolij Gustschin 					chain_node);
219812458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
219912458ea0SAnatolij Gustschin 		iter->hw_next = list_entry(iter->chain_node.next,
220012458ea0SAnatolij Gustschin 					   struct ppc440spe_adma_desc_slot,
220112458ea0SAnatolij Gustschin 					   chain_node);
220212458ea0SAnatolij Gustschin 		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
220312458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
220412458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
220512458ea0SAnatolij Gustschin 
220612458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
220712458ea0SAnatolij Gustschin 					     *dst, 0);
220812458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan, 0,
220912458ea0SAnatolij Gustschin 					     ppc440spe_chan->qdest, 1);
221012458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
221112458ea0SAnatolij Gustschin 					    src[1]);
221212458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
221312458ea0SAnatolij Gustschin 		iter->unmap_len = len;
221412458ea0SAnatolij Gustschin 
221512458ea0SAnatolij Gustschin 		/* 2nd descriptor, multiply src[1] data and store the
221612458ea0SAnatolij Gustschin 		 * result in destination */
221712458ea0SAnatolij Gustschin 		iter = list_first_entry(&iter->chain_node,
221812458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
221912458ea0SAnatolij Gustschin 					chain_node);
222012458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
222112458ea0SAnatolij Gustschin 		/* set 'next' pointer */
222212458ea0SAnatolij Gustschin 		iter->hw_next = list_entry(iter->chain_node.next,
222312458ea0SAnatolij Gustschin 					   struct ppc440spe_adma_desc_slot,
222412458ea0SAnatolij Gustschin 					   chain_node);
222512458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_INTERRUPT)
222612458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_DESC_INT, &iter->flags);
222712458ea0SAnatolij Gustschin 		else
222812458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
222912458ea0SAnatolij Gustschin 
223012458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
223112458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
223212458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
223312458ea0SAnatolij Gustschin 					    ppc440spe_chan->qdest);
223412458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
223512458ea0SAnatolij Gustschin 					     *dst, 0);
223612458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_mult(iter, chan,	DMA_CUED_MULT1_OFF,
223712458ea0SAnatolij Gustschin 					    DMA_CDB_SG_DST1, scf[1]);
223812458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
223912458ea0SAnatolij Gustschin 		iter->unmap_len = len;
224012458ea0SAnatolij Gustschin 
224112458ea0SAnatolij Gustschin 		/*
224212458ea0SAnatolij Gustschin 		 * 3rd descriptor, multiply src[0] data and xor it
224312458ea0SAnatolij Gustschin 		 * with destination
224412458ea0SAnatolij Gustschin 		 */
224512458ea0SAnatolij Gustschin 		iter = list_first_entry(&iter->chain_node,
224612458ea0SAnatolij Gustschin 					struct ppc440spe_adma_desc_slot,
224712458ea0SAnatolij Gustschin 					chain_node);
224812458ea0SAnatolij Gustschin 		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
224912458ea0SAnatolij Gustschin 		iter->hw_next = NULL;
225012458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_INTERRUPT)
225112458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_DESC_INT, &iter->flags);
225212458ea0SAnatolij Gustschin 		else
225312458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
225412458ea0SAnatolij Gustschin 
225512458ea0SAnatolij Gustschin 		hw_desc = iter->hw_desc;
225612458ea0SAnatolij Gustschin 		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
225712458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
225812458ea0SAnatolij Gustschin 					    src[0]);
225912458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
226012458ea0SAnatolij Gustschin 					     *dst, 0);
226112458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
226212458ea0SAnatolij Gustschin 					    DMA_CDB_SG_DST1, scf[0]);
226312458ea0SAnatolij Gustschin 		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
226412458ea0SAnatolij Gustschin 		iter->unmap_len = len;
226512458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
226612458ea0SAnatolij Gustschin 	}
226712458ea0SAnatolij Gustschin 
226812458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
226912458ea0SAnatolij Gustschin 
227012458ea0SAnatolij Gustschin 	return sw_desc;
227112458ea0SAnatolij Gustschin }
227212458ea0SAnatolij Gustschin 
ppc440spe_dma01_prep_pq(struct ppc440spe_adma_chan * ppc440spe_chan,dma_addr_t * dst,int dst_cnt,dma_addr_t * src,int src_cnt,const unsigned char * scf,size_t len,unsigned long flags)227312458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq(
227412458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *ppc440spe_chan,
227512458ea0SAnatolij Gustschin 		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
227612458ea0SAnatolij Gustschin 		const unsigned char *scf, size_t len, unsigned long flags)
227712458ea0SAnatolij Gustschin {
227812458ea0SAnatolij Gustschin 	int slot_cnt;
227912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
228012458ea0SAnatolij Gustschin 	unsigned long op = 0;
228112458ea0SAnatolij Gustschin 	unsigned char mult = 1;
228212458ea0SAnatolij Gustschin 
228312458ea0SAnatolij Gustschin 	pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
228412458ea0SAnatolij Gustschin 		 __func__, dst_cnt, src_cnt, len);
228512458ea0SAnatolij Gustschin 	/*  select operations WXOR/RXOR depending on the
228612458ea0SAnatolij Gustschin 	 * source addresses of operators and the number
228712458ea0SAnatolij Gustschin 	 * of destinations (RXOR support only Q-parity calculations)
228812458ea0SAnatolij Gustschin 	 */
228912458ea0SAnatolij Gustschin 	set_bit(PPC440SPE_DESC_WXOR, &op);
229012458ea0SAnatolij Gustschin 	if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
229112458ea0SAnatolij Gustschin 		/* no active RXOR;
229212458ea0SAnatolij Gustschin 		 * do RXOR if:
229312458ea0SAnatolij Gustschin 		 * - there are more than 1 source,
229412458ea0SAnatolij Gustschin 		 * - len is aligned on 512-byte boundary,
229512458ea0SAnatolij Gustschin 		 * - source addresses fit to one of 4 possible regions.
229612458ea0SAnatolij Gustschin 		 */
229712458ea0SAnatolij Gustschin 		if (src_cnt > 1 &&
229812458ea0SAnatolij Gustschin 		    !(len & MQ0_CF2H_RXOR_BS_MASK) &&
229912458ea0SAnatolij Gustschin 		    (src[0] + len) == src[1]) {
230012458ea0SAnatolij Gustschin 			/* may do RXOR R1 R2 */
230112458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_DESC_RXOR, &op);
230212458ea0SAnatolij Gustschin 			if (src_cnt != 2) {
230312458ea0SAnatolij Gustschin 				/* may try to enhance region of RXOR */
230412458ea0SAnatolij Gustschin 				if ((src[1] + len) == src[2]) {
230512458ea0SAnatolij Gustschin 					/* do RXOR R1 R2 R3 */
230612458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_RXOR123,
230712458ea0SAnatolij Gustschin 						&op);
230812458ea0SAnatolij Gustschin 				} else if ((src[1] + len * 2) == src[2]) {
230912458ea0SAnatolij Gustschin 					/* do RXOR R1 R2 R4 */
231012458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_RXOR124, &op);
231112458ea0SAnatolij Gustschin 				} else if ((src[1] + len * 3) == src[2]) {
231212458ea0SAnatolij Gustschin 					/* do RXOR R1 R2 R5 */
231312458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_RXOR125,
231412458ea0SAnatolij Gustschin 						&op);
231512458ea0SAnatolij Gustschin 				} else {
231612458ea0SAnatolij Gustschin 					/* do RXOR R1 R2 */
231712458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_RXOR12,
231812458ea0SAnatolij Gustschin 						&op);
231912458ea0SAnatolij Gustschin 				}
232012458ea0SAnatolij Gustschin 			} else {
232112458ea0SAnatolij Gustschin 				/* do RXOR R1 R2 */
232212458ea0SAnatolij Gustschin 				set_bit(PPC440SPE_DESC_RXOR12, &op);
232312458ea0SAnatolij Gustschin 			}
232412458ea0SAnatolij Gustschin 		}
232512458ea0SAnatolij Gustschin 
232612458ea0SAnatolij Gustschin 		if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
232712458ea0SAnatolij Gustschin 			/* can not do this operation with RXOR */
232812458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_RXOR_RUN,
232912458ea0SAnatolij Gustschin 				&ppc440spe_rxor_state);
233012458ea0SAnatolij Gustschin 		} else {
233112458ea0SAnatolij Gustschin 			/* can do; set block size right now */
233212458ea0SAnatolij Gustschin 			ppc440spe_desc_set_rxor_block_size(len);
233312458ea0SAnatolij Gustschin 		}
233412458ea0SAnatolij Gustschin 	}
233512458ea0SAnatolij Gustschin 
233612458ea0SAnatolij Gustschin 	/* Number of necessary slots depends on operation type selected */
233712458ea0SAnatolij Gustschin 	if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
233812458ea0SAnatolij Gustschin 		/*  This is a WXOR only chain. Need descriptors for each
233912458ea0SAnatolij Gustschin 		 * source to GF-XOR them with WXOR, and need descriptors
234012458ea0SAnatolij Gustschin 		 * for each destination to zero them with WXOR
234112458ea0SAnatolij Gustschin 		 */
234212458ea0SAnatolij Gustschin 		slot_cnt = src_cnt;
234312458ea0SAnatolij Gustschin 
234412458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_P) {
234512458ea0SAnatolij Gustschin 			slot_cnt++;
234612458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_P, &op);
234712458ea0SAnatolij Gustschin 		}
234812458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_Q) {
234912458ea0SAnatolij Gustschin 			slot_cnt++;
235012458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_Q, &op);
235112458ea0SAnatolij Gustschin 		}
235212458ea0SAnatolij Gustschin 	} else {
235312458ea0SAnatolij Gustschin 		/*  Need 1/2 descriptor for RXOR operation, and
235412458ea0SAnatolij Gustschin 		 * need (src_cnt - (2 or 3)) for WXOR of sources
235512458ea0SAnatolij Gustschin 		 * remained (if any)
235612458ea0SAnatolij Gustschin 		 */
235712458ea0SAnatolij Gustschin 		slot_cnt = dst_cnt;
235812458ea0SAnatolij Gustschin 
235912458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_P)
236012458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_P, &op);
236112458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_Q)
236212458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_Q, &op);
236312458ea0SAnatolij Gustschin 
236412458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_DESC_RXOR12, &op))
236512458ea0SAnatolij Gustschin 			slot_cnt += src_cnt - 2;
236612458ea0SAnatolij Gustschin 		else
236712458ea0SAnatolij Gustschin 			slot_cnt += src_cnt - 3;
236812458ea0SAnatolij Gustschin 
236912458ea0SAnatolij Gustschin 		/*  Thus we have either RXOR only chain or
237012458ea0SAnatolij Gustschin 		 * mixed RXOR/WXOR
237112458ea0SAnatolij Gustschin 		 */
237212458ea0SAnatolij Gustschin 		if (slot_cnt == dst_cnt)
237312458ea0SAnatolij Gustschin 			/* RXOR only chain */
237412458ea0SAnatolij Gustschin 			clear_bit(PPC440SPE_DESC_WXOR, &op);
237512458ea0SAnatolij Gustschin 	}
237612458ea0SAnatolij Gustschin 
237712458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
237812458ea0SAnatolij Gustschin 	/* for both RXOR/WXOR each descriptor occupies one slot */
237912458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
238012458ea0SAnatolij Gustschin 	if (sw_desc) {
238112458ea0SAnatolij Gustschin 		ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt,
238212458ea0SAnatolij Gustschin 				flags, op);
238312458ea0SAnatolij Gustschin 
238412458ea0SAnatolij Gustschin 		/* setup dst/src/mult */
238512458ea0SAnatolij Gustschin 		pr_debug("%s: set dst descriptor 0, 1: 0x%016llx, 0x%016llx\n",
238612458ea0SAnatolij Gustschin 			 __func__, dst[0], dst[1]);
238712458ea0SAnatolij Gustschin 		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
238812458ea0SAnatolij Gustschin 		while (src_cnt--) {
238912458ea0SAnatolij Gustschin 			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
239012458ea0SAnatolij Gustschin 						  src_cnt);
239112458ea0SAnatolij Gustschin 
239212458ea0SAnatolij Gustschin 			/* NOTE: "Multi = 0 is equivalent to = 1" as it
239312458ea0SAnatolij Gustschin 			 * stated in 440SPSPe_RAID6_Addendum_UM_1_17.pdf
239412458ea0SAnatolij Gustschin 			 * doesn't work for RXOR with DMA0/1! Instead, multi=0
239512458ea0SAnatolij Gustschin 			 * leads to zeroing source data after RXOR.
239612458ea0SAnatolij Gustschin 			 * So, for P case set-up mult=1 explicitly.
239712458ea0SAnatolij Gustschin 			 */
239812458ea0SAnatolij Gustschin 			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
239912458ea0SAnatolij Gustschin 				mult = scf[src_cnt];
240012458ea0SAnatolij Gustschin 			ppc440spe_adma_pq_set_src_mult(sw_desc,
240112458ea0SAnatolij Gustschin 				mult, src_cnt,  dst_cnt - 1);
240212458ea0SAnatolij Gustschin 		}
240312458ea0SAnatolij Gustschin 
240412458ea0SAnatolij Gustschin 		/* Setup byte count foreach slot just allocated */
240512458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
240612458ea0SAnatolij Gustschin 		list_for_each_entry(iter, &sw_desc->group_list,
240712458ea0SAnatolij Gustschin 				chain_node) {
240812458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter,
240912458ea0SAnatolij Gustschin 				ppc440spe_chan, len);
241012458ea0SAnatolij Gustschin 			iter->unmap_len = len;
241112458ea0SAnatolij Gustschin 		}
241212458ea0SAnatolij Gustschin 	}
241312458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
241412458ea0SAnatolij Gustschin 
241512458ea0SAnatolij Gustschin 	return sw_desc;
241612458ea0SAnatolij Gustschin }
241712458ea0SAnatolij Gustschin 
ppc440spe_dma2_prep_pq(struct ppc440spe_adma_chan * ppc440spe_chan,dma_addr_t * dst,int dst_cnt,dma_addr_t * src,int src_cnt,const unsigned char * scf,size_t len,unsigned long flags)241812458ea0SAnatolij Gustschin static struct ppc440spe_adma_desc_slot *ppc440spe_dma2_prep_pq(
241912458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *ppc440spe_chan,
242012458ea0SAnatolij Gustschin 		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
242112458ea0SAnatolij Gustschin 		const unsigned char *scf, size_t len, unsigned long flags)
242212458ea0SAnatolij Gustschin {
242312458ea0SAnatolij Gustschin 	int slot_cnt, descs_per_op;
242412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
242512458ea0SAnatolij Gustschin 	unsigned long op = 0;
242612458ea0SAnatolij Gustschin 	unsigned char mult = 1;
242712458ea0SAnatolij Gustschin 
242812458ea0SAnatolij Gustschin 	BUG_ON(!dst_cnt);
242912458ea0SAnatolij Gustschin 	/*pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
243012458ea0SAnatolij Gustschin 		 __func__, dst_cnt, src_cnt, len);*/
243112458ea0SAnatolij Gustschin 
243212458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
243312458ea0SAnatolij Gustschin 	descs_per_op = ppc440spe_dma2_pq_slot_count(src, src_cnt, len);
243412458ea0SAnatolij Gustschin 	if (descs_per_op < 0) {
243512458ea0SAnatolij Gustschin 		spin_unlock_bh(&ppc440spe_chan->lock);
243612458ea0SAnatolij Gustschin 		return NULL;
243712458ea0SAnatolij Gustschin 	}
243812458ea0SAnatolij Gustschin 
243912458ea0SAnatolij Gustschin 	/* depending on number of sources we have 1 or 2 RXOR chains */
244012458ea0SAnatolij Gustschin 	slot_cnt = descs_per_op * dst_cnt;
244112458ea0SAnatolij Gustschin 
244212458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
244312458ea0SAnatolij Gustschin 	if (sw_desc) {
244412458ea0SAnatolij Gustschin 		op = slot_cnt;
244512458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
244612458ea0SAnatolij Gustschin 		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
244712458ea0SAnatolij Gustschin 			ppc440spe_desc_init_dma2pq(iter, dst_cnt, src_cnt,
244812458ea0SAnatolij Gustschin 				--op ? 0 : flags);
244912458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
245012458ea0SAnatolij Gustschin 				len);
245112458ea0SAnatolij Gustschin 			iter->unmap_len = len;
245212458ea0SAnatolij Gustschin 
245312458ea0SAnatolij Gustschin 			ppc440spe_init_rxor_cursor(&(iter->rxor_cursor));
245412458ea0SAnatolij Gustschin 			iter->rxor_cursor.len = len;
245512458ea0SAnatolij Gustschin 			iter->descs_per_op = descs_per_op;
245612458ea0SAnatolij Gustschin 		}
245712458ea0SAnatolij Gustschin 		op = 0;
245812458ea0SAnatolij Gustschin 		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
245912458ea0SAnatolij Gustschin 			op++;
246012458ea0SAnatolij Gustschin 			if (op % descs_per_op == 0)
246112458ea0SAnatolij Gustschin 				ppc440spe_adma_init_dma2rxor_slot(iter, src,
246212458ea0SAnatolij Gustschin 								  src_cnt);
246312458ea0SAnatolij Gustschin 			if (likely(!list_is_last(&iter->chain_node,
246412458ea0SAnatolij Gustschin 						 &sw_desc->group_list))) {
246512458ea0SAnatolij Gustschin 				/* set 'next' pointer */
246612458ea0SAnatolij Gustschin 				iter->hw_next =
246712458ea0SAnatolij Gustschin 					list_entry(iter->chain_node.next,
246812458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
246912458ea0SAnatolij Gustschin 						chain_node);
247012458ea0SAnatolij Gustschin 				ppc440spe_xor_set_link(iter, iter->hw_next);
247112458ea0SAnatolij Gustschin 			} else {
247212458ea0SAnatolij Gustschin 				/* this is the last descriptor. */
247312458ea0SAnatolij Gustschin 				iter->hw_next = NULL;
247412458ea0SAnatolij Gustschin 			}
247512458ea0SAnatolij Gustschin 		}
247612458ea0SAnatolij Gustschin 
247712458ea0SAnatolij Gustschin 		/* fixup head descriptor */
247812458ea0SAnatolij Gustschin 		sw_desc->dst_cnt = dst_cnt;
247912458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_P)
248012458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_P, &sw_desc->flags);
248112458ea0SAnatolij Gustschin 		if (flags & DMA_PREP_ZERO_Q)
248212458ea0SAnatolij Gustschin 			set_bit(PPC440SPE_ZERO_Q, &sw_desc->flags);
248312458ea0SAnatolij Gustschin 
248412458ea0SAnatolij Gustschin 		/* setup dst/src/mult */
248512458ea0SAnatolij Gustschin 		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
248612458ea0SAnatolij Gustschin 
248712458ea0SAnatolij Gustschin 		while (src_cnt--) {
248812458ea0SAnatolij Gustschin 			/* handle descriptors (if dst_cnt == 2) inside
248912458ea0SAnatolij Gustschin 			 * the ppc440spe_adma_pq_set_srcxxx() functions
249012458ea0SAnatolij Gustschin 			 */
249112458ea0SAnatolij Gustschin 			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
249212458ea0SAnatolij Gustschin 						  src_cnt);
249312458ea0SAnatolij Gustschin 			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
249412458ea0SAnatolij Gustschin 				mult = scf[src_cnt];
249512458ea0SAnatolij Gustschin 			ppc440spe_adma_pq_set_src_mult(sw_desc,
249612458ea0SAnatolij Gustschin 					mult, src_cnt, dst_cnt - 1);
249712458ea0SAnatolij Gustschin 		}
249812458ea0SAnatolij Gustschin 	}
249912458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
250012458ea0SAnatolij Gustschin 	ppc440spe_desc_set_rxor_block_size(len);
250112458ea0SAnatolij Gustschin 	return sw_desc;
250212458ea0SAnatolij Gustschin }
250312458ea0SAnatolij Gustschin 
250412458ea0SAnatolij Gustschin /**
250512458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_pq - prepare CDB (group) for a GF-XOR operation
250612458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_pq(struct dma_chan * chan,dma_addr_t * dst,dma_addr_t * src,unsigned int src_cnt,const unsigned char * scf,size_t len,unsigned long flags)250712458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq(
250812458ea0SAnatolij Gustschin 		struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
250912458ea0SAnatolij Gustschin 		unsigned int src_cnt, const unsigned char *scf,
251012458ea0SAnatolij Gustschin 		size_t len, unsigned long flags)
251112458ea0SAnatolij Gustschin {
251212458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
251312458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
251412458ea0SAnatolij Gustschin 	int dst_cnt = 0;
251512458ea0SAnatolij Gustschin 
251612458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
251712458ea0SAnatolij Gustschin 
251812458ea0SAnatolij Gustschin 	ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id,
251912458ea0SAnatolij Gustschin 				    dst, src, src_cnt));
252012458ea0SAnatolij Gustschin 	BUG_ON(!len);
2521427cdf19SColy Li 	BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
252212458ea0SAnatolij Gustschin 	BUG_ON(!src_cnt);
252312458ea0SAnatolij Gustschin 
252412458ea0SAnatolij Gustschin 	if (src_cnt == 1 && dst[1] == src[0]) {
252512458ea0SAnatolij Gustschin 		dma_addr_t dest[2];
252612458ea0SAnatolij Gustschin 
252712458ea0SAnatolij Gustschin 		/* dst[1] is real destination (Q) */
252812458ea0SAnatolij Gustschin 		dest[0] = dst[1];
252912458ea0SAnatolij Gustschin 		/* this is the page to multicast source data to */
253012458ea0SAnatolij Gustschin 		dest[1] = ppc440spe_chan->qdest;
253112458ea0SAnatolij Gustschin 		sw_desc = ppc440spe_dma01_prep_mult(ppc440spe_chan,
253212458ea0SAnatolij Gustschin 				dest, 2, src, src_cnt, scf, len, flags);
253312458ea0SAnatolij Gustschin 		return sw_desc ? &sw_desc->async_tx : NULL;
253412458ea0SAnatolij Gustschin 	}
253512458ea0SAnatolij Gustschin 
253612458ea0SAnatolij Gustschin 	if (src_cnt == 2 && dst[1] == src[1]) {
253712458ea0SAnatolij Gustschin 		sw_desc = ppc440spe_dma01_prep_sum_product(ppc440spe_chan,
253812458ea0SAnatolij Gustschin 					&dst[1], src, 2, scf, len, flags);
253912458ea0SAnatolij Gustschin 		return sw_desc ? &sw_desc->async_tx : NULL;
254012458ea0SAnatolij Gustschin 	}
254112458ea0SAnatolij Gustschin 
254212458ea0SAnatolij Gustschin 	if (!(flags & DMA_PREP_PQ_DISABLE_P)) {
254312458ea0SAnatolij Gustschin 		BUG_ON(!dst[0]);
254412458ea0SAnatolij Gustschin 		dst_cnt++;
254512458ea0SAnatolij Gustschin 		flags |= DMA_PREP_ZERO_P;
254612458ea0SAnatolij Gustschin 	}
254712458ea0SAnatolij Gustschin 
254812458ea0SAnatolij Gustschin 	if (!(flags & DMA_PREP_PQ_DISABLE_Q)) {
254912458ea0SAnatolij Gustschin 		BUG_ON(!dst[1]);
255012458ea0SAnatolij Gustschin 		dst_cnt++;
255112458ea0SAnatolij Gustschin 		flags |= DMA_PREP_ZERO_Q;
255212458ea0SAnatolij Gustschin 	}
255312458ea0SAnatolij Gustschin 
255412458ea0SAnatolij Gustschin 	BUG_ON(!dst_cnt);
255512458ea0SAnatolij Gustschin 
255612458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
255712458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
255812458ea0SAnatolij Gustschin 		ppc440spe_chan->device->id, __func__, src_cnt, len,
255912458ea0SAnatolij Gustschin 		flags & DMA_PREP_INTERRUPT ? 1 : 0);
256012458ea0SAnatolij Gustschin 
256112458ea0SAnatolij Gustschin 	switch (ppc440spe_chan->device->id) {
256212458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
256312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
256412458ea0SAnatolij Gustschin 		sw_desc = ppc440spe_dma01_prep_pq(ppc440spe_chan,
256512458ea0SAnatolij Gustschin 				dst, dst_cnt, src, src_cnt, scf,
256612458ea0SAnatolij Gustschin 				len, flags);
256712458ea0SAnatolij Gustschin 		break;
256812458ea0SAnatolij Gustschin 
256912458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
257012458ea0SAnatolij Gustschin 		sw_desc = ppc440spe_dma2_prep_pq(ppc440spe_chan,
257112458ea0SAnatolij Gustschin 				dst, dst_cnt, src, src_cnt, scf,
257212458ea0SAnatolij Gustschin 				len, flags);
257312458ea0SAnatolij Gustschin 		break;
257412458ea0SAnatolij Gustschin 	}
257512458ea0SAnatolij Gustschin 
257612458ea0SAnatolij Gustschin 	return sw_desc ? &sw_desc->async_tx : NULL;
257712458ea0SAnatolij Gustschin }
257812458ea0SAnatolij Gustschin 
257912458ea0SAnatolij Gustschin /**
258012458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_pqzero_sum - prepare CDB group for
258112458ea0SAnatolij Gustschin  * a PQ_ZERO_SUM operation
258212458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_pqzero_sum(struct dma_chan * chan,dma_addr_t * pq,dma_addr_t * src,unsigned int src_cnt,const unsigned char * scf,size_t len,enum sum_check_flags * pqres,unsigned long flags)258312458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pqzero_sum(
258412458ea0SAnatolij Gustschin 		struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
258512458ea0SAnatolij Gustschin 		unsigned int src_cnt, const unsigned char *scf, size_t len,
258612458ea0SAnatolij Gustschin 		enum sum_check_flags *pqres, unsigned long flags)
258712458ea0SAnatolij Gustschin {
258812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
258912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
259012458ea0SAnatolij Gustschin 	dma_addr_t pdest, qdest;
259112458ea0SAnatolij Gustschin 	int slot_cnt, slots_per_op, idst, dst_cnt;
259212458ea0SAnatolij Gustschin 
259312458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
259412458ea0SAnatolij Gustschin 
259512458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_PQ_DISABLE_P)
259612458ea0SAnatolij Gustschin 		pdest = 0;
259712458ea0SAnatolij Gustschin 	else
259812458ea0SAnatolij Gustschin 		pdest = pq[0];
259912458ea0SAnatolij Gustschin 
260012458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_PQ_DISABLE_Q)
260112458ea0SAnatolij Gustschin 		qdest = 0;
260212458ea0SAnatolij Gustschin 	else
260312458ea0SAnatolij Gustschin 		qdest = pq[1];
260412458ea0SAnatolij Gustschin 
260512458ea0SAnatolij Gustschin 	ADMA_LL_DBG(prep_dma_pqzero_sum_dbg(ppc440spe_chan->device->id,
260612458ea0SAnatolij Gustschin 					    src, src_cnt, scf));
260712458ea0SAnatolij Gustschin 
260812458ea0SAnatolij Gustschin 	/* Always use WXOR for P/Q calculations (two destinations).
260912458ea0SAnatolij Gustschin 	 * Need 1 or 2 extra slots to verify results are zero.
261012458ea0SAnatolij Gustschin 	 */
261112458ea0SAnatolij Gustschin 	idst = dst_cnt = (pdest && qdest) ? 2 : 1;
261212458ea0SAnatolij Gustschin 
261312458ea0SAnatolij Gustschin 	/* One additional slot per destination to clone P/Q
261412458ea0SAnatolij Gustschin 	 * before calculation (we have to preserve destinations).
261512458ea0SAnatolij Gustschin 	 */
261612458ea0SAnatolij Gustschin 	slot_cnt = src_cnt + dst_cnt * 2;
261712458ea0SAnatolij Gustschin 	slots_per_op = 1;
261812458ea0SAnatolij Gustschin 
261912458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
262012458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
262112458ea0SAnatolij Gustschin 					     slots_per_op);
262212458ea0SAnatolij Gustschin 	if (sw_desc) {
262312458ea0SAnatolij Gustschin 		ppc440spe_desc_init_dma01pqzero_sum(sw_desc, dst_cnt, src_cnt);
262412458ea0SAnatolij Gustschin 
262512458ea0SAnatolij Gustschin 		/* Setup byte count for each slot just allocated */
262612458ea0SAnatolij Gustschin 		sw_desc->async_tx.flags = flags;
262712458ea0SAnatolij Gustschin 		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
262812458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
262912458ea0SAnatolij Gustschin 						      len);
263012458ea0SAnatolij Gustschin 			iter->unmap_len = len;
263112458ea0SAnatolij Gustschin 		}
263212458ea0SAnatolij Gustschin 
263312458ea0SAnatolij Gustschin 		if (pdest) {
263412458ea0SAnatolij Gustschin 			struct dma_cdb *hw_desc;
263512458ea0SAnatolij Gustschin 			struct ppc440spe_adma_chan *chan;
263612458ea0SAnatolij Gustschin 
263712458ea0SAnatolij Gustschin 			iter = sw_desc->group_head;
263812458ea0SAnatolij Gustschin 			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
263912458ea0SAnatolij Gustschin 			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
264012458ea0SAnatolij Gustschin 			iter->hw_next = list_entry(iter->chain_node.next,
264112458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
264212458ea0SAnatolij Gustschin 						chain_node);
264312458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
264412458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
264512458ea0SAnatolij Gustschin 			iter->src_cnt = 0;
264612458ea0SAnatolij Gustschin 			iter->dst_cnt = 0;
264712458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan, 0,
264812458ea0SAnatolij Gustschin 						     ppc440spe_chan->pdest, 0);
264912458ea0SAnatolij Gustschin 			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, pdest);
265012458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
265112458ea0SAnatolij Gustschin 						      len);
265212458ea0SAnatolij Gustschin 			iter->unmap_len = 0;
265312458ea0SAnatolij Gustschin 			/* override pdest to preserve original P */
265412458ea0SAnatolij Gustschin 			pdest = ppc440spe_chan->pdest;
265512458ea0SAnatolij Gustschin 		}
265612458ea0SAnatolij Gustschin 		if (qdest) {
265712458ea0SAnatolij Gustschin 			struct dma_cdb *hw_desc;
265812458ea0SAnatolij Gustschin 			struct ppc440spe_adma_chan *chan;
265912458ea0SAnatolij Gustschin 
266012458ea0SAnatolij Gustschin 			iter = list_first_entry(&sw_desc->group_list,
266112458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
266212458ea0SAnatolij Gustschin 						chain_node);
266312458ea0SAnatolij Gustschin 			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
266412458ea0SAnatolij Gustschin 
266512458ea0SAnatolij Gustschin 			if (pdest) {
266612458ea0SAnatolij Gustschin 				iter = list_entry(iter->chain_node.next,
266712458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
266812458ea0SAnatolij Gustschin 						chain_node);
266912458ea0SAnatolij Gustschin 			}
267012458ea0SAnatolij Gustschin 
267112458ea0SAnatolij Gustschin 			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
267212458ea0SAnatolij Gustschin 			iter->hw_next = list_entry(iter->chain_node.next,
267312458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
267412458ea0SAnatolij Gustschin 						chain_node);
267512458ea0SAnatolij Gustschin 			hw_desc = iter->hw_desc;
267612458ea0SAnatolij Gustschin 			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
267712458ea0SAnatolij Gustschin 			iter->src_cnt = 0;
267812458ea0SAnatolij Gustschin 			iter->dst_cnt = 0;
267912458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan, 0,
268012458ea0SAnatolij Gustschin 						     ppc440spe_chan->qdest, 0);
268112458ea0SAnatolij Gustschin 			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, qdest);
268212458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
268312458ea0SAnatolij Gustschin 						      len);
268412458ea0SAnatolij Gustschin 			iter->unmap_len = 0;
268512458ea0SAnatolij Gustschin 			/* override qdest to preserve original Q */
268612458ea0SAnatolij Gustschin 			qdest = ppc440spe_chan->qdest;
268712458ea0SAnatolij Gustschin 		}
268812458ea0SAnatolij Gustschin 
268912458ea0SAnatolij Gustschin 		/* Setup destinations for P/Q ops */
269012458ea0SAnatolij Gustschin 		ppc440spe_adma_pqzero_sum_set_dest(sw_desc, pdest, qdest);
269112458ea0SAnatolij Gustschin 
269212458ea0SAnatolij Gustschin 		/* Setup zero QWORDs into DCHECK CDBs */
269312458ea0SAnatolij Gustschin 		idst = dst_cnt;
269412458ea0SAnatolij Gustschin 		list_for_each_entry_reverse(iter, &sw_desc->group_list,
269512458ea0SAnatolij Gustschin 					    chain_node) {
269612458ea0SAnatolij Gustschin 			/*
269712458ea0SAnatolij Gustschin 			 * The last CDB corresponds to Q-parity check,
269812458ea0SAnatolij Gustschin 			 * the one before last CDB corresponds
269912458ea0SAnatolij Gustschin 			 * P-parity check
270012458ea0SAnatolij Gustschin 			 */
270112458ea0SAnatolij Gustschin 			if (idst == DMA_DEST_MAX_NUM) {
270212458ea0SAnatolij Gustschin 				if (idst == dst_cnt) {
270312458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_QCHECK,
270412458ea0SAnatolij Gustschin 						&iter->flags);
270512458ea0SAnatolij Gustschin 				} else {
270612458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_PCHECK,
270712458ea0SAnatolij Gustschin 						&iter->flags);
270812458ea0SAnatolij Gustschin 				}
270912458ea0SAnatolij Gustschin 			} else {
271012458ea0SAnatolij Gustschin 				if (qdest) {
271112458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_QCHECK,
271212458ea0SAnatolij Gustschin 						&iter->flags);
271312458ea0SAnatolij Gustschin 				} else {
271412458ea0SAnatolij Gustschin 					set_bit(PPC440SPE_DESC_PCHECK,
271512458ea0SAnatolij Gustschin 						&iter->flags);
271612458ea0SAnatolij Gustschin 				}
271712458ea0SAnatolij Gustschin 			}
271812458ea0SAnatolij Gustschin 			iter->xor_check_result = pqres;
271912458ea0SAnatolij Gustschin 
272012458ea0SAnatolij Gustschin 			/*
272112458ea0SAnatolij Gustschin 			 * set it to zero, if check fail then result will
272212458ea0SAnatolij Gustschin 			 * be updated
272312458ea0SAnatolij Gustschin 			 */
272412458ea0SAnatolij Gustschin 			*iter->xor_check_result = 0;
272512458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dcheck(iter, ppc440spe_chan,
272612458ea0SAnatolij Gustschin 				ppc440spe_qword);
272712458ea0SAnatolij Gustschin 
272812458ea0SAnatolij Gustschin 			if (!(--dst_cnt))
272912458ea0SAnatolij Gustschin 				break;
273012458ea0SAnatolij Gustschin 		}
273112458ea0SAnatolij Gustschin 
273212458ea0SAnatolij Gustschin 		/* Setup sources and mults for P/Q ops */
273312458ea0SAnatolij Gustschin 		list_for_each_entry_continue_reverse(iter, &sw_desc->group_list,
273412458ea0SAnatolij Gustschin 						     chain_node) {
273512458ea0SAnatolij Gustschin 			struct ppc440spe_adma_chan *chan;
273612458ea0SAnatolij Gustschin 			u32 mult_dst;
273712458ea0SAnatolij Gustschin 
273812458ea0SAnatolij Gustschin 			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
273912458ea0SAnatolij Gustschin 			ppc440spe_desc_set_src_addr(iter, chan, 0,
274012458ea0SAnatolij Gustschin 						    DMA_CUED_XOR_HB,
274112458ea0SAnatolij Gustschin 						    src[src_cnt - 1]);
274212458ea0SAnatolij Gustschin 			if (qdest) {
274312458ea0SAnatolij Gustschin 				mult_dst = (dst_cnt - 1) ? DMA_CDB_SG_DST2 :
274412458ea0SAnatolij Gustschin 							   DMA_CDB_SG_DST1;
274512458ea0SAnatolij Gustschin 				ppc440spe_desc_set_src_mult(iter, chan,
274612458ea0SAnatolij Gustschin 							    DMA_CUED_MULT1_OFF,
274712458ea0SAnatolij Gustschin 							    mult_dst,
274812458ea0SAnatolij Gustschin 							    scf[src_cnt - 1]);
274912458ea0SAnatolij Gustschin 			}
275012458ea0SAnatolij Gustschin 			if (!(--src_cnt))
275112458ea0SAnatolij Gustschin 				break;
275212458ea0SAnatolij Gustschin 		}
275312458ea0SAnatolij Gustschin 	}
275412458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
275512458ea0SAnatolij Gustschin 	return sw_desc ? &sw_desc->async_tx : NULL;
275612458ea0SAnatolij Gustschin }
275712458ea0SAnatolij Gustschin 
275812458ea0SAnatolij Gustschin /**
275912458ea0SAnatolij Gustschin  * ppc440spe_adma_prep_dma_xor_zero_sum - prepare CDB group for
276012458ea0SAnatolij Gustschin  * XOR ZERO_SUM operation
276112458ea0SAnatolij Gustschin  */
ppc440spe_adma_prep_dma_xor_zero_sum(struct dma_chan * chan,dma_addr_t * src,unsigned int src_cnt,size_t len,enum sum_check_flags * result,unsigned long flags)276212458ea0SAnatolij Gustschin static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor_zero_sum(
276312458ea0SAnatolij Gustschin 		struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt,
276412458ea0SAnatolij Gustschin 		size_t len, enum sum_check_flags *result, unsigned long flags)
276512458ea0SAnatolij Gustschin {
276612458ea0SAnatolij Gustschin 	struct dma_async_tx_descriptor *tx;
276712458ea0SAnatolij Gustschin 	dma_addr_t pq[2];
276812458ea0SAnatolij Gustschin 
276912458ea0SAnatolij Gustschin 	/* validate P, disable Q */
277012458ea0SAnatolij Gustschin 	pq[0] = src[0];
277112458ea0SAnatolij Gustschin 	pq[1] = 0;
277212458ea0SAnatolij Gustschin 	flags |= DMA_PREP_PQ_DISABLE_Q;
277312458ea0SAnatolij Gustschin 
277412458ea0SAnatolij Gustschin 	tx = ppc440spe_adma_prep_dma_pqzero_sum(chan, pq, &src[1],
277512458ea0SAnatolij Gustschin 						src_cnt - 1, 0, len,
277612458ea0SAnatolij Gustschin 						result, flags);
277712458ea0SAnatolij Gustschin 	return tx;
277812458ea0SAnatolij Gustschin }
277912458ea0SAnatolij Gustschin 
278012458ea0SAnatolij Gustschin /**
278112458ea0SAnatolij Gustschin  * ppc440spe_adma_set_dest - set destination address into descriptor
278212458ea0SAnatolij Gustschin  */
ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot * sw_desc,dma_addr_t addr,int index)278312458ea0SAnatolij Gustschin static void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
278412458ea0SAnatolij Gustschin 		dma_addr_t addr, int index)
278512458ea0SAnatolij Gustschin {
278612458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
278712458ea0SAnatolij Gustschin 
278812458ea0SAnatolij Gustschin 	BUG_ON(index >= sw_desc->dst_cnt);
278912458ea0SAnatolij Gustschin 
279012458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
279112458ea0SAnatolij Gustschin 
279212458ea0SAnatolij Gustschin 	switch (chan->device->id) {
279312458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
279412458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
279512458ea0SAnatolij Gustschin 		/* to do: support transfers lengths >
279612458ea0SAnatolij Gustschin 		 * PPC440SPE_ADMA_DMA/XOR_MAX_BYTE_COUNT
279712458ea0SAnatolij Gustschin 		 */
279812458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(sw_desc->group_head,
279912458ea0SAnatolij Gustschin 			chan, 0, addr, index);
280012458ea0SAnatolij Gustschin 		break;
280112458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
280212458ea0SAnatolij Gustschin 		sw_desc = ppc440spe_get_group_entry(sw_desc, index);
280312458ea0SAnatolij Gustschin 		ppc440spe_desc_set_dest_addr(sw_desc,
280412458ea0SAnatolij Gustschin 			chan, 0, addr, index);
280512458ea0SAnatolij Gustschin 		break;
280612458ea0SAnatolij Gustschin 	}
280712458ea0SAnatolij Gustschin }
280812458ea0SAnatolij Gustschin 
ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot * iter,struct ppc440spe_adma_chan * chan,dma_addr_t addr)280912458ea0SAnatolij Gustschin static void ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot *iter,
281012458ea0SAnatolij Gustschin 		struct ppc440spe_adma_chan *chan, dma_addr_t addr)
281112458ea0SAnatolij Gustschin {
281212458ea0SAnatolij Gustschin 	/*  To clear destinations update the descriptor
281312458ea0SAnatolij Gustschin 	 * (P or Q depending on index) as follows:
281412458ea0SAnatolij Gustschin 	 * addr is destination (0 corresponds to SG2):
281512458ea0SAnatolij Gustschin 	 */
281612458ea0SAnatolij Gustschin 	ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, addr, 0);
281712458ea0SAnatolij Gustschin 
281812458ea0SAnatolij Gustschin 	/* ... and the addr is source: */
281912458ea0SAnatolij Gustschin 	ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, addr);
282012458ea0SAnatolij Gustschin 
282112458ea0SAnatolij Gustschin 	/* addr is always SG2 then the mult is always DST1 */
282212458ea0SAnatolij Gustschin 	ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
282312458ea0SAnatolij Gustschin 				    DMA_CDB_SG_DST1, 1);
282412458ea0SAnatolij Gustschin }
282512458ea0SAnatolij Gustschin 
282612458ea0SAnatolij Gustschin /**
282712458ea0SAnatolij Gustschin  * ppc440spe_adma_pq_set_dest - set destination address into descriptor
282812458ea0SAnatolij Gustschin  * for the PQXOR operation
282912458ea0SAnatolij Gustschin  */
ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot * sw_desc,dma_addr_t * addrs,unsigned long flags)283012458ea0SAnatolij Gustschin static void ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
283112458ea0SAnatolij Gustschin 		dma_addr_t *addrs, unsigned long flags)
283212458ea0SAnatolij Gustschin {
283312458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter;
283412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
283512458ea0SAnatolij Gustschin 	dma_addr_t paddr, qaddr;
283612458ea0SAnatolij Gustschin 	dma_addr_t addr = 0, ppath, qpath;
283712458ea0SAnatolij Gustschin 	int index = 0, i;
283812458ea0SAnatolij Gustschin 
283912458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
284012458ea0SAnatolij Gustschin 
284112458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_PQ_DISABLE_P)
284212458ea0SAnatolij Gustschin 		paddr = 0;
284312458ea0SAnatolij Gustschin 	else
284412458ea0SAnatolij Gustschin 		paddr = addrs[0];
284512458ea0SAnatolij Gustschin 
284612458ea0SAnatolij Gustschin 	if (flags & DMA_PREP_PQ_DISABLE_Q)
284712458ea0SAnatolij Gustschin 		qaddr = 0;
284812458ea0SAnatolij Gustschin 	else
284912458ea0SAnatolij Gustschin 		qaddr = addrs[1];
285012458ea0SAnatolij Gustschin 
285112458ea0SAnatolij Gustschin 	if (!paddr || !qaddr)
285212458ea0SAnatolij Gustschin 		addr = paddr ? paddr : qaddr;
285312458ea0SAnatolij Gustschin 
285412458ea0SAnatolij Gustschin 	switch (chan->device->id) {
285512458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
285612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
285712458ea0SAnatolij Gustschin 		/* walk through the WXOR source list and set P/Q-destinations
285812458ea0SAnatolij Gustschin 		 * for each slot:
285912458ea0SAnatolij Gustschin 		 */
286012458ea0SAnatolij Gustschin 		if (!test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
286112458ea0SAnatolij Gustschin 			/* This is WXOR-only chain; may have 1/2 zero descs */
286212458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
286312458ea0SAnatolij Gustschin 				index++;
286412458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
286512458ea0SAnatolij Gustschin 				index++;
286612458ea0SAnatolij Gustschin 
286712458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc, index);
286812458ea0SAnatolij Gustschin 			if (addr) {
286912458ea0SAnatolij Gustschin 				/* one destination */
287012458ea0SAnatolij Gustschin 				list_for_each_entry_from(iter,
287112458ea0SAnatolij Gustschin 					&sw_desc->group_list, chain_node)
287212458ea0SAnatolij Gustschin 					ppc440spe_desc_set_dest_addr(iter, chan,
287312458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, addr, 0);
287412458ea0SAnatolij Gustschin 			} else {
287512458ea0SAnatolij Gustschin 				/* two destinations */
287612458ea0SAnatolij Gustschin 				list_for_each_entry_from(iter,
287712458ea0SAnatolij Gustschin 					&sw_desc->group_list, chain_node) {
287812458ea0SAnatolij Gustschin 					ppc440spe_desc_set_dest_addr(iter, chan,
287912458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, paddr, 0);
288012458ea0SAnatolij Gustschin 					ppc440spe_desc_set_dest_addr(iter, chan,
288112458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, qaddr, 1);
288212458ea0SAnatolij Gustschin 				}
288312458ea0SAnatolij Gustschin 			}
288412458ea0SAnatolij Gustschin 
288512458ea0SAnatolij Gustschin 			if (index) {
288612458ea0SAnatolij Gustschin 				/*  To clear destinations update the descriptor
288712458ea0SAnatolij Gustschin 				 * (1st,2nd, or both depending on flags)
288812458ea0SAnatolij Gustschin 				 */
288912458ea0SAnatolij Gustschin 				index = 0;
289012458ea0SAnatolij Gustschin 				if (test_bit(PPC440SPE_ZERO_P,
289112458ea0SAnatolij Gustschin 						&sw_desc->flags)) {
289212458ea0SAnatolij Gustschin 					iter = ppc440spe_get_group_entry(
289312458ea0SAnatolij Gustschin 							sw_desc, index++);
289412458ea0SAnatolij Gustschin 					ppc440spe_adma_pq_zero_op(iter, chan,
289512458ea0SAnatolij Gustschin 							paddr);
289612458ea0SAnatolij Gustschin 				}
289712458ea0SAnatolij Gustschin 
289812458ea0SAnatolij Gustschin 				if (test_bit(PPC440SPE_ZERO_Q,
289912458ea0SAnatolij Gustschin 						&sw_desc->flags)) {
290012458ea0SAnatolij Gustschin 					iter = ppc440spe_get_group_entry(
290112458ea0SAnatolij Gustschin 							sw_desc, index++);
290212458ea0SAnatolij Gustschin 					ppc440spe_adma_pq_zero_op(iter, chan,
290312458ea0SAnatolij Gustschin 							qaddr);
290412458ea0SAnatolij Gustschin 				}
290512458ea0SAnatolij Gustschin 
290612458ea0SAnatolij Gustschin 				return;
290712458ea0SAnatolij Gustschin 			}
290812458ea0SAnatolij Gustschin 		} else {
290912458ea0SAnatolij Gustschin 			/* This is RXOR-only or RXOR/WXOR mixed chain */
291012458ea0SAnatolij Gustschin 
291112458ea0SAnatolij Gustschin 			/* If we want to include destination into calculations,
291212458ea0SAnatolij Gustschin 			 * then make dest addresses cued with mult=1 (XOR).
291312458ea0SAnatolij Gustschin 			 */
291412458ea0SAnatolij Gustschin 			ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
291512458ea0SAnatolij Gustschin 					DMA_CUED_XOR_HB :
291612458ea0SAnatolij Gustschin 					DMA_CUED_XOR_BASE |
291712458ea0SAnatolij Gustschin 						(1 << DMA_CUED_MULT1_OFF);
291812458ea0SAnatolij Gustschin 			qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
291912458ea0SAnatolij Gustschin 					DMA_CUED_XOR_HB :
292012458ea0SAnatolij Gustschin 					DMA_CUED_XOR_BASE |
292112458ea0SAnatolij Gustschin 						(1 << DMA_CUED_MULT1_OFF);
292212458ea0SAnatolij Gustschin 
292312458ea0SAnatolij Gustschin 			/* Setup destination(s) in RXOR slot(s) */
292412458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc, index++);
292512458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan,
292612458ea0SAnatolij Gustschin 						paddr ? ppath : qpath,
292712458ea0SAnatolij Gustschin 						paddr ? paddr : qaddr, 0);
292812458ea0SAnatolij Gustschin 			if (!addr) {
292912458ea0SAnatolij Gustschin 				/* two destinations */
293012458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc,
293112458ea0SAnatolij Gustschin 								 index++);
293212458ea0SAnatolij Gustschin 				ppc440spe_desc_set_dest_addr(iter, chan,
293312458ea0SAnatolij Gustschin 						qpath, qaddr, 0);
293412458ea0SAnatolij Gustschin 			}
293512458ea0SAnatolij Gustschin 
293612458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_DESC_WXOR, &sw_desc->flags)) {
293712458ea0SAnatolij Gustschin 				/* Setup destination(s) in remaining WXOR
293812458ea0SAnatolij Gustschin 				 * slots
293912458ea0SAnatolij Gustschin 				 */
294012458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc,
294112458ea0SAnatolij Gustschin 								 index);
294212458ea0SAnatolij Gustschin 				if (addr) {
294312458ea0SAnatolij Gustschin 					/* one destination */
294412458ea0SAnatolij Gustschin 					list_for_each_entry_from(iter,
294512458ea0SAnatolij Gustschin 					    &sw_desc->group_list,
294612458ea0SAnatolij Gustschin 					    chain_node)
294712458ea0SAnatolij Gustschin 						ppc440spe_desc_set_dest_addr(
294812458ea0SAnatolij Gustschin 							iter, chan,
294912458ea0SAnatolij Gustschin 							DMA_CUED_XOR_BASE,
295012458ea0SAnatolij Gustschin 							addr, 0);
295112458ea0SAnatolij Gustschin 
295212458ea0SAnatolij Gustschin 				} else {
295312458ea0SAnatolij Gustschin 					/* two destinations */
295412458ea0SAnatolij Gustschin 					list_for_each_entry_from(iter,
295512458ea0SAnatolij Gustschin 					    &sw_desc->group_list,
295612458ea0SAnatolij Gustschin 					    chain_node) {
295712458ea0SAnatolij Gustschin 						ppc440spe_desc_set_dest_addr(
295812458ea0SAnatolij Gustschin 							iter, chan,
295912458ea0SAnatolij Gustschin 							DMA_CUED_XOR_BASE,
296012458ea0SAnatolij Gustschin 							paddr, 0);
296112458ea0SAnatolij Gustschin 						ppc440spe_desc_set_dest_addr(
296212458ea0SAnatolij Gustschin 							iter, chan,
296312458ea0SAnatolij Gustschin 							DMA_CUED_XOR_BASE,
296412458ea0SAnatolij Gustschin 							qaddr, 1);
296512458ea0SAnatolij Gustschin 					}
296612458ea0SAnatolij Gustschin 				}
296712458ea0SAnatolij Gustschin 			}
296812458ea0SAnatolij Gustschin 
296912458ea0SAnatolij Gustschin 		}
297012458ea0SAnatolij Gustschin 		break;
297112458ea0SAnatolij Gustschin 
297212458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
297312458ea0SAnatolij Gustschin 		/* DMA2 descriptors have only 1 destination, so there are
297412458ea0SAnatolij Gustschin 		 * two chains - one for each dest.
297512458ea0SAnatolij Gustschin 		 * If we want to include destination into calculations,
297612458ea0SAnatolij Gustschin 		 * then make dest addresses cued with mult=1 (XOR).
297712458ea0SAnatolij Gustschin 		 */
297812458ea0SAnatolij Gustschin 		ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
297912458ea0SAnatolij Gustschin 				DMA_CUED_XOR_HB :
298012458ea0SAnatolij Gustschin 				DMA_CUED_XOR_BASE |
298112458ea0SAnatolij Gustschin 					(1 << DMA_CUED_MULT1_OFF);
298212458ea0SAnatolij Gustschin 
298312458ea0SAnatolij Gustschin 		qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
298412458ea0SAnatolij Gustschin 				DMA_CUED_XOR_HB :
298512458ea0SAnatolij Gustschin 				DMA_CUED_XOR_BASE |
298612458ea0SAnatolij Gustschin 					(1 << DMA_CUED_MULT1_OFF);
298712458ea0SAnatolij Gustschin 
298812458ea0SAnatolij Gustschin 		iter = ppc440spe_get_group_entry(sw_desc, 0);
298912458ea0SAnatolij Gustschin 		for (i = 0; i < sw_desc->descs_per_op; i++) {
299012458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan,
299112458ea0SAnatolij Gustschin 				paddr ? ppath : qpath,
299212458ea0SAnatolij Gustschin 				paddr ? paddr : qaddr, 0);
299312458ea0SAnatolij Gustschin 			iter = list_entry(iter->chain_node.next,
299412458ea0SAnatolij Gustschin 					  struct ppc440spe_adma_desc_slot,
299512458ea0SAnatolij Gustschin 					  chain_node);
299612458ea0SAnatolij Gustschin 		}
299712458ea0SAnatolij Gustschin 
299812458ea0SAnatolij Gustschin 		if (!addr) {
299912458ea0SAnatolij Gustschin 			/* Two destinations; setup Q here */
300012458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc,
300112458ea0SAnatolij Gustschin 				sw_desc->descs_per_op);
300212458ea0SAnatolij Gustschin 			for (i = 0; i < sw_desc->descs_per_op; i++) {
300312458ea0SAnatolij Gustschin 				ppc440spe_desc_set_dest_addr(iter,
300412458ea0SAnatolij Gustschin 					chan, qpath, qaddr, 0);
300512458ea0SAnatolij Gustschin 				iter = list_entry(iter->chain_node.next,
300612458ea0SAnatolij Gustschin 						struct ppc440spe_adma_desc_slot,
300712458ea0SAnatolij Gustschin 						chain_node);
300812458ea0SAnatolij Gustschin 			}
300912458ea0SAnatolij Gustschin 		}
301012458ea0SAnatolij Gustschin 
301112458ea0SAnatolij Gustschin 		break;
301212458ea0SAnatolij Gustschin 	}
301312458ea0SAnatolij Gustschin }
301412458ea0SAnatolij Gustschin 
301512458ea0SAnatolij Gustschin /**
301612458ea0SAnatolij Gustschin  * ppc440spe_adma_pq_zero_sum_set_dest - set destination address into descriptor
301712458ea0SAnatolij Gustschin  * for the PQ_ZERO_SUM operation
301812458ea0SAnatolij Gustschin  */
ppc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot * sw_desc,dma_addr_t paddr,dma_addr_t qaddr)301912458ea0SAnatolij Gustschin static void ppc440spe_adma_pqzero_sum_set_dest(
302012458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *sw_desc,
302112458ea0SAnatolij Gustschin 		dma_addr_t paddr, dma_addr_t qaddr)
302212458ea0SAnatolij Gustschin {
302312458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter, *end;
302412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
302512458ea0SAnatolij Gustschin 	dma_addr_t addr = 0;
302612458ea0SAnatolij Gustschin 	int idx;
302712458ea0SAnatolij Gustschin 
302812458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
302912458ea0SAnatolij Gustschin 
303012458ea0SAnatolij Gustschin 	/* walk through the WXOR source list and set P/Q-destinations
303112458ea0SAnatolij Gustschin 	 * for each slot
303212458ea0SAnatolij Gustschin 	 */
303312458ea0SAnatolij Gustschin 	idx = (paddr && qaddr) ? 2 : 1;
303412458ea0SAnatolij Gustschin 	/* set end */
303512458ea0SAnatolij Gustschin 	list_for_each_entry_reverse(end, &sw_desc->group_list,
303612458ea0SAnatolij Gustschin 				    chain_node) {
303712458ea0SAnatolij Gustschin 		if (!(--idx))
303812458ea0SAnatolij Gustschin 			break;
303912458ea0SAnatolij Gustschin 	}
304012458ea0SAnatolij Gustschin 	/* set start */
304112458ea0SAnatolij Gustschin 	idx = (paddr && qaddr) ? 2 : 1;
304212458ea0SAnatolij Gustschin 	iter = ppc440spe_get_group_entry(sw_desc, idx);
304312458ea0SAnatolij Gustschin 
304412458ea0SAnatolij Gustschin 	if (paddr && qaddr) {
304512458ea0SAnatolij Gustschin 		/* two destinations */
304612458ea0SAnatolij Gustschin 		list_for_each_entry_from(iter, &sw_desc->group_list,
304712458ea0SAnatolij Gustschin 					 chain_node) {
304812458ea0SAnatolij Gustschin 			if (unlikely(iter == end))
304912458ea0SAnatolij Gustschin 				break;
305012458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan,
305112458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, paddr, 0);
305212458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan,
305312458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, qaddr, 1);
305412458ea0SAnatolij Gustschin 		}
305512458ea0SAnatolij Gustschin 	} else {
305612458ea0SAnatolij Gustschin 		/* one destination */
305712458ea0SAnatolij Gustschin 		addr = paddr ? paddr : qaddr;
305812458ea0SAnatolij Gustschin 		list_for_each_entry_from(iter, &sw_desc->group_list,
305912458ea0SAnatolij Gustschin 					 chain_node) {
306012458ea0SAnatolij Gustschin 			if (unlikely(iter == end))
306112458ea0SAnatolij Gustschin 				break;
306212458ea0SAnatolij Gustschin 			ppc440spe_desc_set_dest_addr(iter, chan,
306312458ea0SAnatolij Gustschin 						DMA_CUED_XOR_BASE, addr, 0);
306412458ea0SAnatolij Gustschin 		}
306512458ea0SAnatolij Gustschin 	}
306612458ea0SAnatolij Gustschin 
306712458ea0SAnatolij Gustschin 	/*  The remaining descriptors are DATACHECK. These have no need in
306812458ea0SAnatolij Gustschin 	 * destination. Actually, these destinations are used there
306912458ea0SAnatolij Gustschin 	 * as sources for check operation. So, set addr as source.
307012458ea0SAnatolij Gustschin 	 */
307112458ea0SAnatolij Gustschin 	ppc440spe_desc_set_src_addr(end, chan, 0, 0, addr ? addr : paddr);
307212458ea0SAnatolij Gustschin 
307312458ea0SAnatolij Gustschin 	if (!addr) {
307412458ea0SAnatolij Gustschin 		end = list_entry(end->chain_node.next,
307512458ea0SAnatolij Gustschin 				 struct ppc440spe_adma_desc_slot, chain_node);
307612458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(end, chan, 0, 0, qaddr);
307712458ea0SAnatolij Gustschin 	}
307812458ea0SAnatolij Gustschin }
307912458ea0SAnatolij Gustschin 
308012458ea0SAnatolij Gustschin /**
308112458ea0SAnatolij Gustschin  * ppc440spe_desc_set_xor_src_cnt - set source count into descriptor
308212458ea0SAnatolij Gustschin  */
ppc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot * desc,int src_cnt)308312458ea0SAnatolij Gustschin static inline void ppc440spe_desc_set_xor_src_cnt(
308412458ea0SAnatolij Gustschin 			struct ppc440spe_adma_desc_slot *desc,
308512458ea0SAnatolij Gustschin 			int src_cnt)
308612458ea0SAnatolij Gustschin {
308712458ea0SAnatolij Gustschin 	struct xor_cb *hw_desc = desc->hw_desc;
308812458ea0SAnatolij Gustschin 
308912458ea0SAnatolij Gustschin 	hw_desc->cbc &= ~XOR_CDCR_OAC_MSK;
309012458ea0SAnatolij Gustschin 	hw_desc->cbc |= src_cnt;
309112458ea0SAnatolij Gustschin }
309212458ea0SAnatolij Gustschin 
309312458ea0SAnatolij Gustschin /**
309412458ea0SAnatolij Gustschin  * ppc440spe_adma_pq_set_src - set source address into descriptor
309512458ea0SAnatolij Gustschin  */
ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot * sw_desc,dma_addr_t addr,int index)309612458ea0SAnatolij Gustschin static void ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *sw_desc,
309712458ea0SAnatolij Gustschin 		dma_addr_t addr, int index)
309812458ea0SAnatolij Gustschin {
309912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
310012458ea0SAnatolij Gustschin 	dma_addr_t haddr = 0;
310112458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter = NULL;
310212458ea0SAnatolij Gustschin 
310312458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
310412458ea0SAnatolij Gustschin 
310512458ea0SAnatolij Gustschin 	switch (chan->device->id) {
310612458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
310712458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
310812458ea0SAnatolij Gustschin 		/* DMA0,1 may do: WXOR, RXOR, RXOR+WXORs chain
310912458ea0SAnatolij Gustschin 		 */
311012458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
311112458ea0SAnatolij Gustschin 			/* RXOR-only or RXOR/WXOR operation */
311212458ea0SAnatolij Gustschin 			int iskip = test_bit(PPC440SPE_DESC_RXOR12,
311312458ea0SAnatolij Gustschin 				&sw_desc->flags) ?  2 : 3;
311412458ea0SAnatolij Gustschin 
311512458ea0SAnatolij Gustschin 			if (index == 0) {
311612458ea0SAnatolij Gustschin 				/* 1st slot (RXOR) */
311712458ea0SAnatolij Gustschin 				/* setup sources region (R1-2-3, R1-2-4,
311812458ea0SAnatolij Gustschin 				 * or R1-2-5)
311912458ea0SAnatolij Gustschin 				 */
312012458ea0SAnatolij Gustschin 				if (test_bit(PPC440SPE_DESC_RXOR12,
312112458ea0SAnatolij Gustschin 						&sw_desc->flags))
312212458ea0SAnatolij Gustschin 					haddr = DMA_RXOR12 <<
312312458ea0SAnatolij Gustschin 						DMA_CUED_REGION_OFF;
312412458ea0SAnatolij Gustschin 				else if (test_bit(PPC440SPE_DESC_RXOR123,
312512458ea0SAnatolij Gustschin 				    &sw_desc->flags))
312612458ea0SAnatolij Gustschin 					haddr = DMA_RXOR123 <<
312712458ea0SAnatolij Gustschin 						DMA_CUED_REGION_OFF;
312812458ea0SAnatolij Gustschin 				else if (test_bit(PPC440SPE_DESC_RXOR124,
312912458ea0SAnatolij Gustschin 				    &sw_desc->flags))
313012458ea0SAnatolij Gustschin 					haddr = DMA_RXOR124 <<
313112458ea0SAnatolij Gustschin 						DMA_CUED_REGION_OFF;
313212458ea0SAnatolij Gustschin 				else if (test_bit(PPC440SPE_DESC_RXOR125,
313312458ea0SAnatolij Gustschin 				    &sw_desc->flags))
313412458ea0SAnatolij Gustschin 					haddr = DMA_RXOR125 <<
313512458ea0SAnatolij Gustschin 						DMA_CUED_REGION_OFF;
313612458ea0SAnatolij Gustschin 				else
313712458ea0SAnatolij Gustschin 					BUG();
313812458ea0SAnatolij Gustschin 				haddr |= DMA_CUED_XOR_BASE;
313912458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc, 0);
314012458ea0SAnatolij Gustschin 			} else if (index < iskip) {
314112458ea0SAnatolij Gustschin 				/* 1st slot (RXOR)
314212458ea0SAnatolij Gustschin 				 * shall actually set source address only once
314312458ea0SAnatolij Gustschin 				 * instead of first <iskip>
314412458ea0SAnatolij Gustschin 				 */
314512458ea0SAnatolij Gustschin 				iter = NULL;
314612458ea0SAnatolij Gustschin 			} else {
314712458ea0SAnatolij Gustschin 				/* 2nd/3d and next slots (WXOR);
314812458ea0SAnatolij Gustschin 				 * skip first slot with RXOR
314912458ea0SAnatolij Gustschin 				 */
315012458ea0SAnatolij Gustschin 				haddr = DMA_CUED_XOR_HB;
315112458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc,
315212458ea0SAnatolij Gustschin 				    index - iskip + sw_desc->dst_cnt);
315312458ea0SAnatolij Gustschin 			}
315412458ea0SAnatolij Gustschin 		} else {
315512458ea0SAnatolij Gustschin 			int znum = 0;
315612458ea0SAnatolij Gustschin 
315712458ea0SAnatolij Gustschin 			/* WXOR-only operation; skip first slots with
315812458ea0SAnatolij Gustschin 			 * zeroing destinations
315912458ea0SAnatolij Gustschin 			 */
316012458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
316112458ea0SAnatolij Gustschin 				znum++;
316212458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
316312458ea0SAnatolij Gustschin 				znum++;
316412458ea0SAnatolij Gustschin 
316512458ea0SAnatolij Gustschin 			haddr = DMA_CUED_XOR_HB;
316612458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc,
316712458ea0SAnatolij Gustschin 					index + znum);
316812458ea0SAnatolij Gustschin 		}
316912458ea0SAnatolij Gustschin 
317012458ea0SAnatolij Gustschin 		if (likely(iter)) {
317112458ea0SAnatolij Gustschin 			ppc440spe_desc_set_src_addr(iter, chan, 0, haddr, addr);
317212458ea0SAnatolij Gustschin 
317312458ea0SAnatolij Gustschin 			if (!index &&
317412458ea0SAnatolij Gustschin 			    test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags) &&
317512458ea0SAnatolij Gustschin 			    sw_desc->dst_cnt == 2) {
317612458ea0SAnatolij Gustschin 				/* if we have two destinations for RXOR, then
317712458ea0SAnatolij Gustschin 				 * setup source in the second descr too
317812458ea0SAnatolij Gustschin 				 */
317912458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc, 1);
318012458ea0SAnatolij Gustschin 				ppc440spe_desc_set_src_addr(iter, chan, 0,
318112458ea0SAnatolij Gustschin 					haddr, addr);
318212458ea0SAnatolij Gustschin 			}
318312458ea0SAnatolij Gustschin 		}
318412458ea0SAnatolij Gustschin 		break;
318512458ea0SAnatolij Gustschin 
318612458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
318712458ea0SAnatolij Gustschin 		/* DMA2 may do Biskup */
318812458ea0SAnatolij Gustschin 		iter = sw_desc->group_head;
318912458ea0SAnatolij Gustschin 		if (iter->dst_cnt == 2) {
319012458ea0SAnatolij Gustschin 			/* both P & Q calculations required; set P src here */
319112458ea0SAnatolij Gustschin 			ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
319212458ea0SAnatolij Gustschin 
319312458ea0SAnatolij Gustschin 			/* this is for Q */
319412458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc,
319512458ea0SAnatolij Gustschin 				sw_desc->descs_per_op);
319612458ea0SAnatolij Gustschin 		}
319712458ea0SAnatolij Gustschin 		ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
319812458ea0SAnatolij Gustschin 		break;
319912458ea0SAnatolij Gustschin 	}
320012458ea0SAnatolij Gustschin }
320112458ea0SAnatolij Gustschin 
320212458ea0SAnatolij Gustschin /**
320312458ea0SAnatolij Gustschin  * ppc440spe_adma_memcpy_xor_set_src - set source address into descriptor
320412458ea0SAnatolij Gustschin  */
ppc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot * sw_desc,dma_addr_t addr,int index)320512458ea0SAnatolij Gustschin static void ppc440spe_adma_memcpy_xor_set_src(
320612458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *sw_desc,
320712458ea0SAnatolij Gustschin 		dma_addr_t addr, int index)
320812458ea0SAnatolij Gustschin {
320912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
321012458ea0SAnatolij Gustschin 
321112458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
321212458ea0SAnatolij Gustschin 	sw_desc = sw_desc->group_head;
321312458ea0SAnatolij Gustschin 
321412458ea0SAnatolij Gustschin 	if (likely(sw_desc))
321512458ea0SAnatolij Gustschin 		ppc440spe_desc_set_src_addr(sw_desc, chan, index, 0, addr);
321612458ea0SAnatolij Gustschin }
321712458ea0SAnatolij Gustschin 
321812458ea0SAnatolij Gustschin /**
321912458ea0SAnatolij Gustschin  * ppc440spe_adma_dma2rxor_inc_addr  -
322012458ea0SAnatolij Gustschin  */
ppc440spe_adma_dma2rxor_inc_addr(struct ppc440spe_adma_desc_slot * desc,struct ppc440spe_rxor * cursor,int index,int src_cnt)322112458ea0SAnatolij Gustschin static void ppc440spe_adma_dma2rxor_inc_addr(
322212458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
322312458ea0SAnatolij Gustschin 		struct ppc440spe_rxor *cursor, int index, int src_cnt)
322412458ea0SAnatolij Gustschin {
322512458ea0SAnatolij Gustschin 	cursor->addr_count++;
322612458ea0SAnatolij Gustschin 	if (index == src_cnt - 1) {
322712458ea0SAnatolij Gustschin 		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
322812458ea0SAnatolij Gustschin 	} else if (cursor->addr_count == XOR_MAX_OPS) {
322912458ea0SAnatolij Gustschin 		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
323012458ea0SAnatolij Gustschin 		cursor->addr_count = 0;
323112458ea0SAnatolij Gustschin 		cursor->desc_count++;
323212458ea0SAnatolij Gustschin 	}
323312458ea0SAnatolij Gustschin }
323412458ea0SAnatolij Gustschin 
323512458ea0SAnatolij Gustschin /**
323612458ea0SAnatolij Gustschin  * ppc440spe_adma_dma2rxor_prep_src - setup RXOR types in DMA2 CDB
323712458ea0SAnatolij Gustschin  */
ppc440spe_adma_dma2rxor_prep_src(struct ppc440spe_adma_desc_slot * hdesc,struct ppc440spe_rxor * cursor,int index,int src_cnt,u32 addr)323812458ea0SAnatolij Gustschin static int ppc440spe_adma_dma2rxor_prep_src(
323912458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *hdesc,
324012458ea0SAnatolij Gustschin 		struct ppc440spe_rxor *cursor, int index,
324112458ea0SAnatolij Gustschin 		int src_cnt, u32 addr)
324212458ea0SAnatolij Gustschin {
324312458ea0SAnatolij Gustschin 	u32 sign;
324412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *desc = hdesc;
324512458ea0SAnatolij Gustschin 	int i;
324612458ea0SAnatolij Gustschin 
324712458ea0SAnatolij Gustschin 	for (i = 0; i < cursor->desc_count; i++) {
324812458ea0SAnatolij Gustschin 		desc = list_entry(hdesc->chain_node.next,
324912458ea0SAnatolij Gustschin 				  struct ppc440spe_adma_desc_slot,
325012458ea0SAnatolij Gustschin 				  chain_node);
325112458ea0SAnatolij Gustschin 	}
325212458ea0SAnatolij Gustschin 
325312458ea0SAnatolij Gustschin 	switch (cursor->state) {
325412458ea0SAnatolij Gustschin 	case 0:
325512458ea0SAnatolij Gustschin 		if (addr == cursor->addrl + cursor->len) {
325612458ea0SAnatolij Gustschin 			/* direct RXOR */
325712458ea0SAnatolij Gustschin 			cursor->state = 1;
325812458ea0SAnatolij Gustschin 			cursor->xor_count++;
325912458ea0SAnatolij Gustschin 			if (index == src_cnt-1) {
326012458ea0SAnatolij Gustschin 				ppc440spe_rxor_set_region(desc,
326112458ea0SAnatolij Gustschin 					cursor->addr_count,
326212458ea0SAnatolij Gustschin 					DMA_RXOR12 << DMA_CUED_REGION_OFF);
326312458ea0SAnatolij Gustschin 				ppc440spe_adma_dma2rxor_inc_addr(
326412458ea0SAnatolij Gustschin 					desc, cursor, index, src_cnt);
326512458ea0SAnatolij Gustschin 			}
326612458ea0SAnatolij Gustschin 		} else if (cursor->addrl == addr + cursor->len) {
326712458ea0SAnatolij Gustschin 			/* reverse RXOR */
326812458ea0SAnatolij Gustschin 			cursor->state = 1;
326912458ea0SAnatolij Gustschin 			cursor->xor_count++;
327012458ea0SAnatolij Gustschin 			set_bit(cursor->addr_count, &desc->reverse_flags[0]);
327112458ea0SAnatolij Gustschin 			if (index == src_cnt-1) {
327212458ea0SAnatolij Gustschin 				ppc440spe_rxor_set_region(desc,
327312458ea0SAnatolij Gustschin 					cursor->addr_count,
327412458ea0SAnatolij Gustschin 					DMA_RXOR12 << DMA_CUED_REGION_OFF);
327512458ea0SAnatolij Gustschin 				ppc440spe_adma_dma2rxor_inc_addr(
327612458ea0SAnatolij Gustschin 					desc, cursor, index, src_cnt);
327712458ea0SAnatolij Gustschin 			}
327812458ea0SAnatolij Gustschin 		} else {
327912458ea0SAnatolij Gustschin 			printk(KERN_ERR "Cannot build "
328012458ea0SAnatolij Gustschin 				"DMA2 RXOR command block.\n");
328112458ea0SAnatolij Gustschin 			BUG();
328212458ea0SAnatolij Gustschin 		}
328312458ea0SAnatolij Gustschin 		break;
328412458ea0SAnatolij Gustschin 	case 1:
328512458ea0SAnatolij Gustschin 		sign = test_bit(cursor->addr_count,
328612458ea0SAnatolij Gustschin 				desc->reverse_flags)
328712458ea0SAnatolij Gustschin 			? -1 : 1;
328812458ea0SAnatolij Gustschin 		if (index == src_cnt-2 || (sign == -1
328912458ea0SAnatolij Gustschin 			&& addr != cursor->addrl - 2*cursor->len)) {
329012458ea0SAnatolij Gustschin 			cursor->state = 0;
329112458ea0SAnatolij Gustschin 			cursor->xor_count = 1;
329212458ea0SAnatolij Gustschin 			cursor->addrl = addr;
329312458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_region(desc,
329412458ea0SAnatolij Gustschin 				cursor->addr_count,
329512458ea0SAnatolij Gustschin 				DMA_RXOR12 << DMA_CUED_REGION_OFF);
329612458ea0SAnatolij Gustschin 			ppc440spe_adma_dma2rxor_inc_addr(
329712458ea0SAnatolij Gustschin 				desc, cursor, index, src_cnt);
329812458ea0SAnatolij Gustschin 		} else if (addr == cursor->addrl + 2*sign*cursor->len) {
329912458ea0SAnatolij Gustschin 			cursor->state = 2;
330012458ea0SAnatolij Gustschin 			cursor->xor_count = 0;
330112458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_region(desc,
330212458ea0SAnatolij Gustschin 				cursor->addr_count,
330312458ea0SAnatolij Gustschin 				DMA_RXOR123 << DMA_CUED_REGION_OFF);
330412458ea0SAnatolij Gustschin 			if (index == src_cnt-1) {
330512458ea0SAnatolij Gustschin 				ppc440spe_adma_dma2rxor_inc_addr(
330612458ea0SAnatolij Gustschin 					desc, cursor, index, src_cnt);
330712458ea0SAnatolij Gustschin 			}
330812458ea0SAnatolij Gustschin 		} else if (addr == cursor->addrl + 3*cursor->len) {
330912458ea0SAnatolij Gustschin 			cursor->state = 2;
331012458ea0SAnatolij Gustschin 			cursor->xor_count = 0;
331112458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_region(desc,
331212458ea0SAnatolij Gustschin 				cursor->addr_count,
331312458ea0SAnatolij Gustschin 				DMA_RXOR124 << DMA_CUED_REGION_OFF);
331412458ea0SAnatolij Gustschin 			if (index == src_cnt-1) {
331512458ea0SAnatolij Gustschin 				ppc440spe_adma_dma2rxor_inc_addr(
331612458ea0SAnatolij Gustschin 					desc, cursor, index, src_cnt);
331712458ea0SAnatolij Gustschin 			}
331812458ea0SAnatolij Gustschin 		} else if (addr == cursor->addrl + 4*cursor->len) {
331912458ea0SAnatolij Gustschin 			cursor->state = 2;
332012458ea0SAnatolij Gustschin 			cursor->xor_count = 0;
332112458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_region(desc,
332212458ea0SAnatolij Gustschin 				cursor->addr_count,
332312458ea0SAnatolij Gustschin 				DMA_RXOR125 << DMA_CUED_REGION_OFF);
332412458ea0SAnatolij Gustschin 			if (index == src_cnt-1) {
332512458ea0SAnatolij Gustschin 				ppc440spe_adma_dma2rxor_inc_addr(
332612458ea0SAnatolij Gustschin 					desc, cursor, index, src_cnt);
332712458ea0SAnatolij Gustschin 			}
332812458ea0SAnatolij Gustschin 		} else {
332912458ea0SAnatolij Gustschin 			cursor->state = 0;
333012458ea0SAnatolij Gustschin 			cursor->xor_count = 1;
333112458ea0SAnatolij Gustschin 			cursor->addrl = addr;
333212458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_region(desc,
333312458ea0SAnatolij Gustschin 				cursor->addr_count,
333412458ea0SAnatolij Gustschin 				DMA_RXOR12 << DMA_CUED_REGION_OFF);
333512458ea0SAnatolij Gustschin 			ppc440spe_adma_dma2rxor_inc_addr(
333612458ea0SAnatolij Gustschin 				desc, cursor, index, src_cnt);
333712458ea0SAnatolij Gustschin 		}
333812458ea0SAnatolij Gustschin 		break;
333912458ea0SAnatolij Gustschin 	case 2:
334012458ea0SAnatolij Gustschin 		cursor->state = 0;
334112458ea0SAnatolij Gustschin 		cursor->addrl = addr;
334212458ea0SAnatolij Gustschin 		cursor->xor_count++;
334312458ea0SAnatolij Gustschin 		if (index) {
334412458ea0SAnatolij Gustschin 			ppc440spe_adma_dma2rxor_inc_addr(
334512458ea0SAnatolij Gustschin 				desc, cursor, index, src_cnt);
334612458ea0SAnatolij Gustschin 		}
334712458ea0SAnatolij Gustschin 		break;
334812458ea0SAnatolij Gustschin 	}
334912458ea0SAnatolij Gustschin 
33507eafa6eeSJason Wang 	return 0;
335112458ea0SAnatolij Gustschin }
335212458ea0SAnatolij Gustschin 
335312458ea0SAnatolij Gustschin /**
335412458ea0SAnatolij Gustschin  * ppc440spe_adma_dma2rxor_set_src - set RXOR source address; it's assumed that
335512458ea0SAnatolij Gustschin  *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
335612458ea0SAnatolij Gustschin  */
ppc440spe_adma_dma2rxor_set_src(struct ppc440spe_adma_desc_slot * desc,int index,dma_addr_t addr)335712458ea0SAnatolij Gustschin static void ppc440spe_adma_dma2rxor_set_src(
335812458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
335912458ea0SAnatolij Gustschin 		int index, dma_addr_t addr)
336012458ea0SAnatolij Gustschin {
336112458ea0SAnatolij Gustschin 	struct xor_cb *xcb = desc->hw_desc;
336212458ea0SAnatolij Gustschin 	int k = 0, op = 0, lop = 0;
336312458ea0SAnatolij Gustschin 
336412458ea0SAnatolij Gustschin 	/* get the RXOR operand which corresponds to index addr */
336512458ea0SAnatolij Gustschin 	while (op <= index) {
336612458ea0SAnatolij Gustschin 		lop = op;
336712458ea0SAnatolij Gustschin 		if (k == XOR_MAX_OPS) {
336812458ea0SAnatolij Gustschin 			k = 0;
336912458ea0SAnatolij Gustschin 			desc = list_entry(desc->chain_node.next,
337012458ea0SAnatolij Gustschin 				struct ppc440spe_adma_desc_slot, chain_node);
337112458ea0SAnatolij Gustschin 			xcb = desc->hw_desc;
337212458ea0SAnatolij Gustschin 
337312458ea0SAnatolij Gustschin 		}
337412458ea0SAnatolij Gustschin 		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
337512458ea0SAnatolij Gustschin 		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
337612458ea0SAnatolij Gustschin 			op += 2;
337712458ea0SAnatolij Gustschin 		else
337812458ea0SAnatolij Gustschin 			op += 3;
337912458ea0SAnatolij Gustschin 	}
338012458ea0SAnatolij Gustschin 
338112458ea0SAnatolij Gustschin 	BUG_ON(k < 1);
338212458ea0SAnatolij Gustschin 
338312458ea0SAnatolij Gustschin 	if (test_bit(k-1, desc->reverse_flags)) {
338412458ea0SAnatolij Gustschin 		/* reverse operand order; put last op in RXOR group */
338512458ea0SAnatolij Gustschin 		if (index == op - 1)
338612458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_src(desc, k - 1, addr);
338712458ea0SAnatolij Gustschin 	} else {
338812458ea0SAnatolij Gustschin 		/* direct operand order; put first op in RXOR group */
338912458ea0SAnatolij Gustschin 		if (index == lop)
339012458ea0SAnatolij Gustschin 			ppc440spe_rxor_set_src(desc, k - 1, addr);
339112458ea0SAnatolij Gustschin 	}
339212458ea0SAnatolij Gustschin }
339312458ea0SAnatolij Gustschin 
339412458ea0SAnatolij Gustschin /**
339512458ea0SAnatolij Gustschin  * ppc440spe_adma_dma2rxor_set_mult - set RXOR multipliers; it's assumed that
339612458ea0SAnatolij Gustschin  *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
339712458ea0SAnatolij Gustschin  */
ppc440spe_adma_dma2rxor_set_mult(struct ppc440spe_adma_desc_slot * desc,int index,u8 mult)339812458ea0SAnatolij Gustschin static void ppc440spe_adma_dma2rxor_set_mult(
339912458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *desc,
340012458ea0SAnatolij Gustschin 		int index, u8 mult)
340112458ea0SAnatolij Gustschin {
340212458ea0SAnatolij Gustschin 	struct xor_cb *xcb = desc->hw_desc;
340312458ea0SAnatolij Gustschin 	int k = 0, op = 0, lop = 0;
340412458ea0SAnatolij Gustschin 
340512458ea0SAnatolij Gustschin 	/* get the RXOR operand which corresponds to index mult */
340612458ea0SAnatolij Gustschin 	while (op <= index) {
340712458ea0SAnatolij Gustschin 		lop = op;
340812458ea0SAnatolij Gustschin 		if (k == XOR_MAX_OPS) {
340912458ea0SAnatolij Gustschin 			k = 0;
341012458ea0SAnatolij Gustschin 			desc = list_entry(desc->chain_node.next,
341112458ea0SAnatolij Gustschin 					  struct ppc440spe_adma_desc_slot,
341212458ea0SAnatolij Gustschin 					  chain_node);
341312458ea0SAnatolij Gustschin 			xcb = desc->hw_desc;
341412458ea0SAnatolij Gustschin 
341512458ea0SAnatolij Gustschin 		}
341612458ea0SAnatolij Gustschin 		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
341712458ea0SAnatolij Gustschin 		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
341812458ea0SAnatolij Gustschin 			op += 2;
341912458ea0SAnatolij Gustschin 		else
342012458ea0SAnatolij Gustschin 			op += 3;
342112458ea0SAnatolij Gustschin 	}
342212458ea0SAnatolij Gustschin 
342312458ea0SAnatolij Gustschin 	BUG_ON(k < 1);
342412458ea0SAnatolij Gustschin 	if (test_bit(k-1, desc->reverse_flags)) {
342512458ea0SAnatolij Gustschin 		/* reverse order */
342612458ea0SAnatolij Gustschin 		ppc440spe_rxor_set_mult(desc, k - 1, op - index - 1, mult);
342712458ea0SAnatolij Gustschin 	} else {
342812458ea0SAnatolij Gustschin 		/* direct order */
342912458ea0SAnatolij Gustschin 		ppc440spe_rxor_set_mult(desc, k - 1, index - lop, mult);
343012458ea0SAnatolij Gustschin 	}
343112458ea0SAnatolij Gustschin }
343212458ea0SAnatolij Gustschin 
343312458ea0SAnatolij Gustschin /**
343412458ea0SAnatolij Gustschin  * ppc440spe_init_rxor_cursor -
343512458ea0SAnatolij Gustschin  */
ppc440spe_init_rxor_cursor(struct ppc440spe_rxor * cursor)343612458ea0SAnatolij Gustschin static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor)
343712458ea0SAnatolij Gustschin {
343812458ea0SAnatolij Gustschin 	memset(cursor, 0, sizeof(struct ppc440spe_rxor));
343912458ea0SAnatolij Gustschin 	cursor->state = 2;
344012458ea0SAnatolij Gustschin }
344112458ea0SAnatolij Gustschin 
344212458ea0SAnatolij Gustschin /**
344312458ea0SAnatolij Gustschin  * ppc440spe_adma_pq_set_src_mult - set multiplication coefficient into
344412458ea0SAnatolij Gustschin  * descriptor for the PQXOR operation
344512458ea0SAnatolij Gustschin  */
ppc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot * sw_desc,unsigned char mult,int index,int dst_pos)344612458ea0SAnatolij Gustschin static void ppc440spe_adma_pq_set_src_mult(
344712458ea0SAnatolij Gustschin 		struct ppc440spe_adma_desc_slot *sw_desc,
344812458ea0SAnatolij Gustschin 		unsigned char mult, int index, int dst_pos)
344912458ea0SAnatolij Gustschin {
345012458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
345112458ea0SAnatolij Gustschin 	u32 mult_idx, mult_dst;
345212458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter = NULL, *iter1 = NULL;
345312458ea0SAnatolij Gustschin 
345412458ea0SAnatolij Gustschin 	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
345512458ea0SAnatolij Gustschin 
345612458ea0SAnatolij Gustschin 	switch (chan->device->id) {
345712458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
345812458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
345912458ea0SAnatolij Gustschin 		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
346012458ea0SAnatolij Gustschin 			int region = test_bit(PPC440SPE_DESC_RXOR12,
346112458ea0SAnatolij Gustschin 					&sw_desc->flags) ? 2 : 3;
346212458ea0SAnatolij Gustschin 
346312458ea0SAnatolij Gustschin 			if (index < region) {
346412458ea0SAnatolij Gustschin 				/* RXOR multipliers */
346512458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc,
346612458ea0SAnatolij Gustschin 					sw_desc->dst_cnt - 1);
346712458ea0SAnatolij Gustschin 				if (sw_desc->dst_cnt == 2)
346812458ea0SAnatolij Gustschin 					iter1 = ppc440spe_get_group_entry(
346912458ea0SAnatolij Gustschin 							sw_desc, 0);
347012458ea0SAnatolij Gustschin 
347112458ea0SAnatolij Gustschin 				mult_idx = DMA_CUED_MULT1_OFF + (index << 3);
347212458ea0SAnatolij Gustschin 				mult_dst = DMA_CDB_SG_SRC;
347312458ea0SAnatolij Gustschin 			} else {
347412458ea0SAnatolij Gustschin 				/* WXOR multiplier */
347512458ea0SAnatolij Gustschin 				iter = ppc440spe_get_group_entry(sw_desc,
347612458ea0SAnatolij Gustschin 							index - region +
347712458ea0SAnatolij Gustschin 							sw_desc->dst_cnt);
347812458ea0SAnatolij Gustschin 				mult_idx = DMA_CUED_MULT1_OFF;
347912458ea0SAnatolij Gustschin 				mult_dst = dst_pos ? DMA_CDB_SG_DST2 :
348012458ea0SAnatolij Gustschin 						     DMA_CDB_SG_DST1;
348112458ea0SAnatolij Gustschin 			}
348212458ea0SAnatolij Gustschin 		} else {
348312458ea0SAnatolij Gustschin 			int znum = 0;
348412458ea0SAnatolij Gustschin 
348512458ea0SAnatolij Gustschin 			/* WXOR-only;
348612458ea0SAnatolij Gustschin 			 * skip first slots with destinations (if ZERO_DST has
348712458ea0SAnatolij Gustschin 			 * place)
348812458ea0SAnatolij Gustschin 			 */
348912458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
349012458ea0SAnatolij Gustschin 				znum++;
349112458ea0SAnatolij Gustschin 			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
349212458ea0SAnatolij Gustschin 				znum++;
349312458ea0SAnatolij Gustschin 
349412458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc, index + znum);
349512458ea0SAnatolij Gustschin 			mult_idx = DMA_CUED_MULT1_OFF;
349612458ea0SAnatolij Gustschin 			mult_dst = dst_pos ? DMA_CDB_SG_DST2 : DMA_CDB_SG_DST1;
349712458ea0SAnatolij Gustschin 		}
349812458ea0SAnatolij Gustschin 
349912458ea0SAnatolij Gustschin 		if (likely(iter)) {
350012458ea0SAnatolij Gustschin 			ppc440spe_desc_set_src_mult(iter, chan,
350112458ea0SAnatolij Gustschin 				mult_idx, mult_dst, mult);
350212458ea0SAnatolij Gustschin 
350312458ea0SAnatolij Gustschin 			if (unlikely(iter1)) {
350412458ea0SAnatolij Gustschin 				/* if we have two destinations for RXOR, then
350512458ea0SAnatolij Gustschin 				 * we've just set Q mult. Set-up P now.
350612458ea0SAnatolij Gustschin 				 */
350712458ea0SAnatolij Gustschin 				ppc440spe_desc_set_src_mult(iter1, chan,
350812458ea0SAnatolij Gustschin 					mult_idx, mult_dst, 1);
350912458ea0SAnatolij Gustschin 			}
351012458ea0SAnatolij Gustschin 
351112458ea0SAnatolij Gustschin 		}
351212458ea0SAnatolij Gustschin 		break;
351312458ea0SAnatolij Gustschin 
351412458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
351512458ea0SAnatolij Gustschin 		iter = sw_desc->group_head;
351612458ea0SAnatolij Gustschin 		if (sw_desc->dst_cnt == 2) {
351712458ea0SAnatolij Gustschin 			/* both P & Q calculations required; set P mult here */
351812458ea0SAnatolij Gustschin 			ppc440spe_adma_dma2rxor_set_mult(iter, index, 1);
351912458ea0SAnatolij Gustschin 
352012458ea0SAnatolij Gustschin 			/* and then set Q mult */
352112458ea0SAnatolij Gustschin 			iter = ppc440spe_get_group_entry(sw_desc,
352212458ea0SAnatolij Gustschin 			       sw_desc->descs_per_op);
352312458ea0SAnatolij Gustschin 		}
352412458ea0SAnatolij Gustschin 		ppc440spe_adma_dma2rxor_set_mult(iter, index, mult);
352512458ea0SAnatolij Gustschin 		break;
352612458ea0SAnatolij Gustschin 	}
352712458ea0SAnatolij Gustschin }
352812458ea0SAnatolij Gustschin 
352912458ea0SAnatolij Gustschin /**
353012458ea0SAnatolij Gustschin  * ppc440spe_adma_free_chan_resources - free the resources allocated
353112458ea0SAnatolij Gustschin  */
ppc440spe_adma_free_chan_resources(struct dma_chan * chan)353212458ea0SAnatolij Gustschin static void ppc440spe_adma_free_chan_resources(struct dma_chan *chan)
353312458ea0SAnatolij Gustschin {
353412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
353512458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *iter, *_iter;
353612458ea0SAnatolij Gustschin 	int in_use_descs = 0;
353712458ea0SAnatolij Gustschin 
353812458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
353912458ea0SAnatolij Gustschin 	ppc440spe_adma_slot_cleanup(ppc440spe_chan);
354012458ea0SAnatolij Gustschin 
354112458ea0SAnatolij Gustschin 	spin_lock_bh(&ppc440spe_chan->lock);
354212458ea0SAnatolij Gustschin 	list_for_each_entry_safe(iter, _iter, &ppc440spe_chan->chain,
354312458ea0SAnatolij Gustschin 					chain_node) {
354412458ea0SAnatolij Gustschin 		in_use_descs++;
354512458ea0SAnatolij Gustschin 		list_del(&iter->chain_node);
354612458ea0SAnatolij Gustschin 	}
354712458ea0SAnatolij Gustschin 	list_for_each_entry_safe_reverse(iter, _iter,
354812458ea0SAnatolij Gustschin 			&ppc440spe_chan->all_slots, slot_node) {
354912458ea0SAnatolij Gustschin 		list_del(&iter->slot_node);
355012458ea0SAnatolij Gustschin 		kfree(iter);
355112458ea0SAnatolij Gustschin 		ppc440spe_chan->slots_allocated--;
355212458ea0SAnatolij Gustschin 	}
355312458ea0SAnatolij Gustschin 	ppc440spe_chan->last_used = NULL;
355412458ea0SAnatolij Gustschin 
355512458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
355612458ea0SAnatolij Gustschin 		"ppc440spe adma%d %s slots_allocated %d\n",
355712458ea0SAnatolij Gustschin 		ppc440spe_chan->device->id,
355812458ea0SAnatolij Gustschin 		__func__, ppc440spe_chan->slots_allocated);
355912458ea0SAnatolij Gustschin 	spin_unlock_bh(&ppc440spe_chan->lock);
356012458ea0SAnatolij Gustschin 
356112458ea0SAnatolij Gustschin 	/* one is ok since we left it on there on purpose */
356212458ea0SAnatolij Gustschin 	if (in_use_descs > 1)
356312458ea0SAnatolij Gustschin 		printk(KERN_ERR "SPE: Freeing %d in use descriptors!\n",
356412458ea0SAnatolij Gustschin 			in_use_descs - 1);
356512458ea0SAnatolij Gustschin }
356612458ea0SAnatolij Gustschin 
356712458ea0SAnatolij Gustschin /**
356807934481SLinus Walleij  * ppc440spe_adma_tx_status - poll the status of an ADMA transaction
356912458ea0SAnatolij Gustschin  * @chan: ADMA channel handle
357012458ea0SAnatolij Gustschin  * @cookie: ADMA transaction identifier
357107934481SLinus Walleij  * @txstate: a holder for the current state of the channel
357212458ea0SAnatolij Gustschin  */
ppc440spe_adma_tx_status(struct dma_chan * chan,dma_cookie_t cookie,struct dma_tx_state * txstate)357307934481SLinus Walleij static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan,
357407934481SLinus Walleij 			dma_cookie_t cookie, struct dma_tx_state *txstate)
357512458ea0SAnatolij Gustschin {
357612458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
357712458ea0SAnatolij Gustschin 	enum dma_status ret;
357812458ea0SAnatolij Gustschin 
357912458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
358096a2af41SRussell King - ARM Linux 	ret = dma_cookie_status(chan, cookie, txstate);
35815738992bSVinod Koul 	if (ret == DMA_COMPLETE)
358212458ea0SAnatolij Gustschin 		return ret;
358312458ea0SAnatolij Gustschin 
358412458ea0SAnatolij Gustschin 	ppc440spe_adma_slot_cleanup(ppc440spe_chan);
358512458ea0SAnatolij Gustschin 
358696a2af41SRussell King - ARM Linux 	return dma_cookie_status(chan, cookie, txstate);
358712458ea0SAnatolij Gustschin }
358812458ea0SAnatolij Gustschin 
358912458ea0SAnatolij Gustschin /**
359012458ea0SAnatolij Gustschin  * ppc440spe_adma_eot_handler - end of transfer interrupt handler
359112458ea0SAnatolij Gustschin  */
ppc440spe_adma_eot_handler(int irq,void * data)359212458ea0SAnatolij Gustschin static irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
359312458ea0SAnatolij Gustschin {
359412458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan = data;
359512458ea0SAnatolij Gustschin 
359612458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev,
359712458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
359812458ea0SAnatolij Gustschin 
359912458ea0SAnatolij Gustschin 	tasklet_schedule(&chan->irq_tasklet);
360012458ea0SAnatolij Gustschin 	ppc440spe_adma_device_clear_eot_status(chan);
360112458ea0SAnatolij Gustschin 
360212458ea0SAnatolij Gustschin 	return IRQ_HANDLED;
360312458ea0SAnatolij Gustschin }
360412458ea0SAnatolij Gustschin 
360512458ea0SAnatolij Gustschin /**
360612458ea0SAnatolij Gustschin  * ppc440spe_adma_err_handler - DMA error interrupt handler;
360712458ea0SAnatolij Gustschin  *	do the same things as a eot handler
360812458ea0SAnatolij Gustschin  */
ppc440spe_adma_err_handler(int irq,void * data)360912458ea0SAnatolij Gustschin static irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
361012458ea0SAnatolij Gustschin {
361112458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan = data;
361212458ea0SAnatolij Gustschin 
361312458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev,
361412458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
361512458ea0SAnatolij Gustschin 
361612458ea0SAnatolij Gustschin 	tasklet_schedule(&chan->irq_tasklet);
361712458ea0SAnatolij Gustschin 	ppc440spe_adma_device_clear_eot_status(chan);
361812458ea0SAnatolij Gustschin 
361912458ea0SAnatolij Gustschin 	return IRQ_HANDLED;
362012458ea0SAnatolij Gustschin }
362112458ea0SAnatolij Gustschin 
362212458ea0SAnatolij Gustschin /**
362312458ea0SAnatolij Gustschin  * ppc440spe_test_callback - called when test operation has been done
362412458ea0SAnatolij Gustschin  */
ppc440spe_test_callback(void * unused)362512458ea0SAnatolij Gustschin static void ppc440spe_test_callback(void *unused)
362612458ea0SAnatolij Gustschin {
362712458ea0SAnatolij Gustschin 	complete(&ppc440spe_r6_test_comp);
362812458ea0SAnatolij Gustschin }
362912458ea0SAnatolij Gustschin 
363012458ea0SAnatolij Gustschin /**
363112458ea0SAnatolij Gustschin  * ppc440spe_adma_issue_pending - flush all pending descriptors to h/w
363212458ea0SAnatolij Gustschin  */
ppc440spe_adma_issue_pending(struct dma_chan * chan)363312458ea0SAnatolij Gustschin static void ppc440spe_adma_issue_pending(struct dma_chan *chan)
363412458ea0SAnatolij Gustschin {
363512458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
363612458ea0SAnatolij Gustschin 
363712458ea0SAnatolij Gustschin 	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
363812458ea0SAnatolij Gustschin 	dev_dbg(ppc440spe_chan->device->common.dev,
363912458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s %d \n", ppc440spe_chan->device->id,
364012458ea0SAnatolij Gustschin 		__func__, ppc440spe_chan->pending);
364112458ea0SAnatolij Gustschin 
364212458ea0SAnatolij Gustschin 	if (ppc440spe_chan->pending) {
364312458ea0SAnatolij Gustschin 		ppc440spe_chan->pending = 0;
364412458ea0SAnatolij Gustschin 		ppc440spe_chan_append(ppc440spe_chan);
364512458ea0SAnatolij Gustschin 	}
364612458ea0SAnatolij Gustschin }
364712458ea0SAnatolij Gustschin 
364812458ea0SAnatolij Gustschin /**
364912458ea0SAnatolij Gustschin  * ppc440spe_chan_start_null_xor - initiate the first XOR operation (DMA engines
365012458ea0SAnatolij Gustschin  *	use FIFOs (as opposite to chains used in XOR) so this is a XOR
365112458ea0SAnatolij Gustschin  *	specific operation)
365212458ea0SAnatolij Gustschin  */
ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan * chan)365312458ea0SAnatolij Gustschin static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
365412458ea0SAnatolij Gustschin {
365512458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
365612458ea0SAnatolij Gustschin 	dma_cookie_t cookie;
365712458ea0SAnatolij Gustschin 	int slot_cnt, slots_per_op;
365812458ea0SAnatolij Gustschin 
365912458ea0SAnatolij Gustschin 	dev_dbg(chan->device->common.dev,
366012458ea0SAnatolij Gustschin 		"ppc440spe adma%d: %s\n", chan->device->id, __func__);
366112458ea0SAnatolij Gustschin 
366212458ea0SAnatolij Gustschin 	spin_lock_bh(&chan->lock);
366312458ea0SAnatolij Gustschin 	slot_cnt = ppc440spe_chan_xor_slot_count(0, 2, &slots_per_op);
366412458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(chan, slot_cnt, slots_per_op);
366512458ea0SAnatolij Gustschin 	if (sw_desc) {
366612458ea0SAnatolij Gustschin 		group_start = sw_desc->group_head;
366712458ea0SAnatolij Gustschin 		list_splice_init(&sw_desc->group_list, &chan->chain);
366812458ea0SAnatolij Gustschin 		async_tx_ack(&sw_desc->async_tx);
366912458ea0SAnatolij Gustschin 		ppc440spe_desc_init_null_xor(group_start);
367012458ea0SAnatolij Gustschin 
36712a926e46SRussell King - ARM Linux 		cookie = dma_cookie_assign(&sw_desc->async_tx);
367212458ea0SAnatolij Gustschin 
367312458ea0SAnatolij Gustschin 		/* initialize the completed cookie to be less than
367412458ea0SAnatolij Gustschin 		 * the most recently used cookie
367512458ea0SAnatolij Gustschin 		 */
36764d4e58deSRussell King - ARM Linux 		chan->common.completed_cookie = cookie - 1;
367712458ea0SAnatolij Gustschin 
367812458ea0SAnatolij Gustschin 		/* channel should not be busy */
367912458ea0SAnatolij Gustschin 		BUG_ON(ppc440spe_chan_is_busy(chan));
368012458ea0SAnatolij Gustschin 
368112458ea0SAnatolij Gustschin 		/* set the descriptor address */
368212458ea0SAnatolij Gustschin 		ppc440spe_chan_set_first_xor_descriptor(chan, sw_desc);
368312458ea0SAnatolij Gustschin 
368412458ea0SAnatolij Gustschin 		/* run the descriptor */
368512458ea0SAnatolij Gustschin 		ppc440spe_chan_run(chan);
368612458ea0SAnatolij Gustschin 	} else
368712458ea0SAnatolij Gustschin 		printk(KERN_ERR "ppc440spe adma%d"
368812458ea0SAnatolij Gustschin 			" failed to allocate null descriptor\n",
368912458ea0SAnatolij Gustschin 			chan->device->id);
369012458ea0SAnatolij Gustschin 	spin_unlock_bh(&chan->lock);
369112458ea0SAnatolij Gustschin }
369212458ea0SAnatolij Gustschin 
369312458ea0SAnatolij Gustschin /**
369412458ea0SAnatolij Gustschin  * ppc440spe_test_raid6 - test are RAID-6 capabilities enabled successfully.
369512458ea0SAnatolij Gustschin  *	For this we just perform one WXOR operation with the same source
369612458ea0SAnatolij Gustschin  *	and destination addresses, the GF-multiplier is 1; so if RAID-6
369712458ea0SAnatolij Gustschin  *	capabilities are enabled then we'll get src/dst filled with zero.
369812458ea0SAnatolij Gustschin  */
ppc440spe_test_raid6(struct ppc440spe_adma_chan * chan)369912458ea0SAnatolij Gustschin static int ppc440spe_test_raid6(struct ppc440spe_adma_chan *chan)
370012458ea0SAnatolij Gustschin {
370112458ea0SAnatolij Gustschin 	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
370212458ea0SAnatolij Gustschin 	struct page *pg;
370312458ea0SAnatolij Gustschin 	char *a;
370412458ea0SAnatolij Gustschin 	dma_addr_t dma_addr, addrs[2];
370512458ea0SAnatolij Gustschin 	unsigned long op = 0;
370612458ea0SAnatolij Gustschin 	int rval = 0;
370712458ea0SAnatolij Gustschin 
370812458ea0SAnatolij Gustschin 	set_bit(PPC440SPE_DESC_WXOR, &op);
370912458ea0SAnatolij Gustschin 
371012458ea0SAnatolij Gustschin 	pg = alloc_page(GFP_KERNEL);
371112458ea0SAnatolij Gustschin 	if (!pg)
371212458ea0SAnatolij Gustschin 		return -ENOMEM;
371312458ea0SAnatolij Gustschin 
371412458ea0SAnatolij Gustschin 	spin_lock_bh(&chan->lock);
371512458ea0SAnatolij Gustschin 	sw_desc = ppc440spe_adma_alloc_slots(chan, 1, 1);
371612458ea0SAnatolij Gustschin 	if (sw_desc) {
371712458ea0SAnatolij Gustschin 		/* 1 src, 1 dsr, int_ena, WXOR */
371812458ea0SAnatolij Gustschin 		ppc440spe_desc_init_dma01pq(sw_desc, 1, 1, 1, op);
371912458ea0SAnatolij Gustschin 		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
372012458ea0SAnatolij Gustschin 			ppc440spe_desc_set_byte_count(iter, chan, PAGE_SIZE);
372112458ea0SAnatolij Gustschin 			iter->unmap_len = PAGE_SIZE;
372212458ea0SAnatolij Gustschin 		}
372312458ea0SAnatolij Gustschin 	} else {
372412458ea0SAnatolij Gustschin 		rval = -EFAULT;
372512458ea0SAnatolij Gustschin 		spin_unlock_bh(&chan->lock);
372612458ea0SAnatolij Gustschin 		goto exit;
372712458ea0SAnatolij Gustschin 	}
372812458ea0SAnatolij Gustschin 	spin_unlock_bh(&chan->lock);
372912458ea0SAnatolij Gustschin 
373012458ea0SAnatolij Gustschin 	/* Fill the test page with ones */
373112458ea0SAnatolij Gustschin 	memset(page_address(pg), 0xFF, PAGE_SIZE);
373212458ea0SAnatolij Gustschin 	dma_addr = dma_map_page(chan->device->dev, pg, 0,
373312458ea0SAnatolij Gustschin 				PAGE_SIZE, DMA_BIDIRECTIONAL);
373412458ea0SAnatolij Gustschin 
373512458ea0SAnatolij Gustschin 	/* Setup addresses */
373612458ea0SAnatolij Gustschin 	ppc440spe_adma_pq_set_src(sw_desc, dma_addr, 0);
373712458ea0SAnatolij Gustschin 	ppc440spe_adma_pq_set_src_mult(sw_desc, 1, 0, 0);
373812458ea0SAnatolij Gustschin 	addrs[0] = dma_addr;
373912458ea0SAnatolij Gustschin 	addrs[1] = 0;
374012458ea0SAnatolij Gustschin 	ppc440spe_adma_pq_set_dest(sw_desc, addrs, DMA_PREP_PQ_DISABLE_Q);
374112458ea0SAnatolij Gustschin 
374212458ea0SAnatolij Gustschin 	async_tx_ack(&sw_desc->async_tx);
374312458ea0SAnatolij Gustschin 	sw_desc->async_tx.callback = ppc440spe_test_callback;
374412458ea0SAnatolij Gustschin 	sw_desc->async_tx.callback_param = NULL;
374512458ea0SAnatolij Gustschin 
374612458ea0SAnatolij Gustschin 	init_completion(&ppc440spe_r6_test_comp);
374712458ea0SAnatolij Gustschin 
374812458ea0SAnatolij Gustschin 	ppc440spe_adma_tx_submit(&sw_desc->async_tx);
374912458ea0SAnatolij Gustschin 	ppc440spe_adma_issue_pending(&chan->common);
375012458ea0SAnatolij Gustschin 
375112458ea0SAnatolij Gustschin 	wait_for_completion(&ppc440spe_r6_test_comp);
375212458ea0SAnatolij Gustschin 
375312458ea0SAnatolij Gustschin 	/* Now check if the test page is zeroed */
375412458ea0SAnatolij Gustschin 	a = page_address(pg);
375512458ea0SAnatolij Gustschin 	if ((*(u32 *)a) == 0 && memcmp(a, a+4, PAGE_SIZE-4) == 0) {
375612458ea0SAnatolij Gustschin 		/* page is zero - RAID-6 enabled */
375712458ea0SAnatolij Gustschin 		rval = 0;
375812458ea0SAnatolij Gustschin 	} else {
375912458ea0SAnatolij Gustschin 		/* RAID-6 was not enabled */
376012458ea0SAnatolij Gustschin 		rval = -EINVAL;
376112458ea0SAnatolij Gustschin 	}
376212458ea0SAnatolij Gustschin exit:
376312458ea0SAnatolij Gustschin 	__free_page(pg);
376412458ea0SAnatolij Gustschin 	return rval;
376512458ea0SAnatolij Gustschin }
376612458ea0SAnatolij Gustschin 
ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device * adev)376712458ea0SAnatolij Gustschin static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev)
376812458ea0SAnatolij Gustschin {
376912458ea0SAnatolij Gustschin 	switch (adev->id) {
377012458ea0SAnatolij Gustschin 	case PPC440SPE_DMA0_ID:
377112458ea0SAnatolij Gustschin 	case PPC440SPE_DMA1_ID:
377212458ea0SAnatolij Gustschin 		dma_cap_set(DMA_MEMCPY, adev->common.cap_mask);
377312458ea0SAnatolij Gustschin 		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
377412458ea0SAnatolij Gustschin 		dma_cap_set(DMA_PQ, adev->common.cap_mask);
377512458ea0SAnatolij Gustschin 		dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask);
377612458ea0SAnatolij Gustschin 		dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask);
377712458ea0SAnatolij Gustschin 		break;
377812458ea0SAnatolij Gustschin 	case PPC440SPE_XOR_ID:
377912458ea0SAnatolij Gustschin 		dma_cap_set(DMA_XOR, adev->common.cap_mask);
378012458ea0SAnatolij Gustschin 		dma_cap_set(DMA_PQ, adev->common.cap_mask);
378112458ea0SAnatolij Gustschin 		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
378212458ea0SAnatolij Gustschin 		adev->common.cap_mask = adev->common.cap_mask;
378312458ea0SAnatolij Gustschin 		break;
378412458ea0SAnatolij Gustschin 	}
378512458ea0SAnatolij Gustschin 
378612458ea0SAnatolij Gustschin 	/* Set base routines */
378712458ea0SAnatolij Gustschin 	adev->common.device_alloc_chan_resources =
378812458ea0SAnatolij Gustschin 				ppc440spe_adma_alloc_chan_resources;
378912458ea0SAnatolij Gustschin 	adev->common.device_free_chan_resources =
379012458ea0SAnatolij Gustschin 				ppc440spe_adma_free_chan_resources;
379107934481SLinus Walleij 	adev->common.device_tx_status = ppc440spe_adma_tx_status;
379212458ea0SAnatolij Gustschin 	adev->common.device_issue_pending = ppc440spe_adma_issue_pending;
379312458ea0SAnatolij Gustschin 
379412458ea0SAnatolij Gustschin 	/* Set prep routines based on capability */
379512458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_MEMCPY, adev->common.cap_mask)) {
379612458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_memcpy =
379712458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_memcpy;
379812458ea0SAnatolij Gustschin 	}
379912458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_XOR, adev->common.cap_mask)) {
380012458ea0SAnatolij Gustschin 		adev->common.max_xor = XOR_MAX_OPS;
380112458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_xor =
380212458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_xor;
380312458ea0SAnatolij Gustschin 	}
380412458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_PQ, adev->common.cap_mask)) {
380512458ea0SAnatolij Gustschin 		switch (adev->id) {
380612458ea0SAnatolij Gustschin 		case PPC440SPE_DMA0_ID:
380712458ea0SAnatolij Gustschin 			dma_set_maxpq(&adev->common,
380812458ea0SAnatolij Gustschin 				DMA0_FIFO_SIZE / sizeof(struct dma_cdb), 0);
380912458ea0SAnatolij Gustschin 			break;
381012458ea0SAnatolij Gustschin 		case PPC440SPE_DMA1_ID:
381112458ea0SAnatolij Gustschin 			dma_set_maxpq(&adev->common,
381212458ea0SAnatolij Gustschin 				DMA1_FIFO_SIZE / sizeof(struct dma_cdb), 0);
381312458ea0SAnatolij Gustschin 			break;
381412458ea0SAnatolij Gustschin 		case PPC440SPE_XOR_ID:
381512458ea0SAnatolij Gustschin 			adev->common.max_pq = XOR_MAX_OPS * 3;
381612458ea0SAnatolij Gustschin 			break;
381712458ea0SAnatolij Gustschin 		}
381812458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_pq =
381912458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_pq;
382012458ea0SAnatolij Gustschin 	}
382112458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask)) {
382212458ea0SAnatolij Gustschin 		switch (adev->id) {
382312458ea0SAnatolij Gustschin 		case PPC440SPE_DMA0_ID:
382412458ea0SAnatolij Gustschin 			adev->common.max_pq = DMA0_FIFO_SIZE /
382512458ea0SAnatolij Gustschin 						sizeof(struct dma_cdb);
382612458ea0SAnatolij Gustschin 			break;
382712458ea0SAnatolij Gustschin 		case PPC440SPE_DMA1_ID:
382812458ea0SAnatolij Gustschin 			adev->common.max_pq = DMA1_FIFO_SIZE /
382912458ea0SAnatolij Gustschin 						sizeof(struct dma_cdb);
383012458ea0SAnatolij Gustschin 			break;
383112458ea0SAnatolij Gustschin 		}
383212458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_pq_val =
383312458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_pqzero_sum;
383412458ea0SAnatolij Gustschin 	}
383512458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask)) {
383612458ea0SAnatolij Gustschin 		switch (adev->id) {
383712458ea0SAnatolij Gustschin 		case PPC440SPE_DMA0_ID:
383812458ea0SAnatolij Gustschin 			adev->common.max_xor = DMA0_FIFO_SIZE /
383912458ea0SAnatolij Gustschin 						sizeof(struct dma_cdb);
384012458ea0SAnatolij Gustschin 			break;
384112458ea0SAnatolij Gustschin 		case PPC440SPE_DMA1_ID:
384212458ea0SAnatolij Gustschin 			adev->common.max_xor = DMA1_FIFO_SIZE /
384312458ea0SAnatolij Gustschin 						sizeof(struct dma_cdb);
384412458ea0SAnatolij Gustschin 			break;
384512458ea0SAnatolij Gustschin 		}
384612458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_xor_val =
384712458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_xor_zero_sum;
384812458ea0SAnatolij Gustschin 	}
384912458ea0SAnatolij Gustschin 	if (dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask)) {
385012458ea0SAnatolij Gustschin 		adev->common.device_prep_dma_interrupt =
385112458ea0SAnatolij Gustschin 			ppc440spe_adma_prep_dma_interrupt;
385212458ea0SAnatolij Gustschin 	}
385312458ea0SAnatolij Gustschin 	pr_info("%s: AMCC(R) PPC440SP(E) ADMA Engine: "
38546aa2731cSDan Williams 	  "( %s%s%s%s%s%s)\n",
385512458ea0SAnatolij Gustschin 	  dev_name(adev->dev),
385612458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_PQ, adev->common.cap_mask) ? "pq " : "",
385712458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask) ? "pq_val " : "",
385812458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "",
385912458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask) ? "xor_val " : "",
386012458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "",
386112458ea0SAnatolij Gustschin 	  dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask) ? "intr " : "");
386212458ea0SAnatolij Gustschin }
386312458ea0SAnatolij Gustschin 
ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device * adev,struct ppc440spe_adma_chan * chan,int * initcode)386412458ea0SAnatolij Gustschin static int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
386512458ea0SAnatolij Gustschin 				     struct ppc440spe_adma_chan *chan,
386612458ea0SAnatolij Gustschin 				     int *initcode)
386712458ea0SAnatolij Gustschin {
38682dc11581SGrant Likely 	struct platform_device *ofdev;
386912458ea0SAnatolij Gustschin 	struct device_node *np;
387012458ea0SAnatolij Gustschin 	int ret;
387112458ea0SAnatolij Gustschin 
38722dc11581SGrant Likely 	ofdev = container_of(adev->dev, struct platform_device, dev);
38733e6b02d9SDan Williams 	np = ofdev->dev.of_node;
387412458ea0SAnatolij Gustschin 	if (adev->id != PPC440SPE_XOR_ID) {
387512458ea0SAnatolij Gustschin 		adev->err_irq = irq_of_parse_and_map(np, 1);
3876aa570be6SMichael Ellerman 		if (!adev->err_irq) {
387712458ea0SAnatolij Gustschin 			dev_warn(adev->dev, "no err irq resource?\n");
387812458ea0SAnatolij Gustschin 			*initcode = PPC_ADMA_INIT_IRQ2;
387912458ea0SAnatolij Gustschin 			adev->err_irq = -ENXIO;
388012458ea0SAnatolij Gustschin 		} else
388112458ea0SAnatolij Gustschin 			atomic_inc(&ppc440spe_adma_err_irq_ref);
388212458ea0SAnatolij Gustschin 	} else {
388312458ea0SAnatolij Gustschin 		adev->err_irq = -ENXIO;
388412458ea0SAnatolij Gustschin 	}
388512458ea0SAnatolij Gustschin 
388612458ea0SAnatolij Gustschin 	adev->irq = irq_of_parse_and_map(np, 0);
3887aa570be6SMichael Ellerman 	if (!adev->irq) {
388812458ea0SAnatolij Gustschin 		dev_err(adev->dev, "no irq resource\n");
388912458ea0SAnatolij Gustschin 		*initcode = PPC_ADMA_INIT_IRQ1;
389012458ea0SAnatolij Gustschin 		ret = -ENXIO;
389112458ea0SAnatolij Gustschin 		goto err_irq_map;
389212458ea0SAnatolij Gustschin 	}
389312458ea0SAnatolij Gustschin 	dev_dbg(adev->dev, "irq %d, err irq %d\n",
389412458ea0SAnatolij Gustschin 		adev->irq, adev->err_irq);
389512458ea0SAnatolij Gustschin 
389612458ea0SAnatolij Gustschin 	ret = request_irq(adev->irq, ppc440spe_adma_eot_handler,
389712458ea0SAnatolij Gustschin 			  0, dev_driver_string(adev->dev), chan);
389812458ea0SAnatolij Gustschin 	if (ret) {
389912458ea0SAnatolij Gustschin 		dev_err(adev->dev, "can't request irq %d\n",
390012458ea0SAnatolij Gustschin 			adev->irq);
390112458ea0SAnatolij Gustschin 		*initcode = PPC_ADMA_INIT_IRQ1;
390212458ea0SAnatolij Gustschin 		ret = -EIO;
390312458ea0SAnatolij Gustschin 		goto err_req1;
390412458ea0SAnatolij Gustschin 	}
390512458ea0SAnatolij Gustschin 
390612458ea0SAnatolij Gustschin 	/* only DMA engines have a separate error IRQ
390712458ea0SAnatolij Gustschin 	 * so it's Ok if err_irq < 0 in XOR engine case.
390812458ea0SAnatolij Gustschin 	 */
390912458ea0SAnatolij Gustschin 	if (adev->err_irq > 0) {
391012458ea0SAnatolij Gustschin 		/* both DMA engines share common error IRQ */
391112458ea0SAnatolij Gustschin 		ret = request_irq(adev->err_irq,
391212458ea0SAnatolij Gustschin 				  ppc440spe_adma_err_handler,
391312458ea0SAnatolij Gustschin 				  IRQF_SHARED,
391412458ea0SAnatolij Gustschin 				  dev_driver_string(adev->dev),
391512458ea0SAnatolij Gustschin 				  chan);
391612458ea0SAnatolij Gustschin 		if (ret) {
391712458ea0SAnatolij Gustschin 			dev_err(adev->dev, "can't request irq %d\n",
391812458ea0SAnatolij Gustschin 				adev->err_irq);
391912458ea0SAnatolij Gustschin 			*initcode = PPC_ADMA_INIT_IRQ2;
392012458ea0SAnatolij Gustschin 			ret = -EIO;
392112458ea0SAnatolij Gustschin 			goto err_req2;
392212458ea0SAnatolij Gustschin 		}
392312458ea0SAnatolij Gustschin 	}
392412458ea0SAnatolij Gustschin 
392512458ea0SAnatolij Gustschin 	if (adev->id == PPC440SPE_XOR_ID) {
392612458ea0SAnatolij Gustschin 		/* enable XOR engine interrupts */
392712458ea0SAnatolij Gustschin 		iowrite32be(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
392812458ea0SAnatolij Gustschin 			    XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT,
392912458ea0SAnatolij Gustschin 			    &adev->xor_reg->ier);
393012458ea0SAnatolij Gustschin 	} else {
393112458ea0SAnatolij Gustschin 		u32 mask, enable;
393212458ea0SAnatolij Gustschin 
393312458ea0SAnatolij Gustschin 		np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
393412458ea0SAnatolij Gustschin 		if (!np) {
393512458ea0SAnatolij Gustschin 			pr_err("%s: can't find I2O device tree node\n",
393612458ea0SAnatolij Gustschin 				__func__);
393712458ea0SAnatolij Gustschin 			ret = -ENODEV;
393812458ea0SAnatolij Gustschin 			goto err_req2;
393912458ea0SAnatolij Gustschin 		}
394012458ea0SAnatolij Gustschin 		adev->i2o_reg = of_iomap(np, 0);
394112458ea0SAnatolij Gustschin 		if (!adev->i2o_reg) {
394212458ea0SAnatolij Gustschin 			pr_err("%s: failed to map I2O registers\n", __func__);
394312458ea0SAnatolij Gustschin 			of_node_put(np);
394412458ea0SAnatolij Gustschin 			ret = -EINVAL;
394512458ea0SAnatolij Gustschin 			goto err_req2;
394612458ea0SAnatolij Gustschin 		}
394712458ea0SAnatolij Gustschin 		of_node_put(np);
394812458ea0SAnatolij Gustschin 		/* Unmask 'CS FIFO Attention' interrupts and
394912458ea0SAnatolij Gustschin 		 * enable generating interrupts on errors
395012458ea0SAnatolij Gustschin 		 */
395112458ea0SAnatolij Gustschin 		enable = (adev->id == PPC440SPE_DMA0_ID) ?
395212458ea0SAnatolij Gustschin 			 ~(I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
395312458ea0SAnatolij Gustschin 			 ~(I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
395412458ea0SAnatolij Gustschin 		mask = ioread32(&adev->i2o_reg->iopim) & enable;
395512458ea0SAnatolij Gustschin 		iowrite32(mask, &adev->i2o_reg->iopim);
395612458ea0SAnatolij Gustschin 	}
395712458ea0SAnatolij Gustschin 	return 0;
395812458ea0SAnatolij Gustschin 
395912458ea0SAnatolij Gustschin err_req2:
396012458ea0SAnatolij Gustschin 	free_irq(adev->irq, chan);
396112458ea0SAnatolij Gustschin err_req1:
396212458ea0SAnatolij Gustschin 	irq_dispose_mapping(adev->irq);
396312458ea0SAnatolij Gustschin err_irq_map:
396412458ea0SAnatolij Gustschin 	if (adev->err_irq > 0) {
396512458ea0SAnatolij Gustschin 		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref))
396612458ea0SAnatolij Gustschin 			irq_dispose_mapping(adev->err_irq);
396712458ea0SAnatolij Gustschin 	}
396812458ea0SAnatolij Gustschin 	return ret;
396912458ea0SAnatolij Gustschin }
397012458ea0SAnatolij Gustschin 
ppc440spe_adma_release_irqs(struct ppc440spe_adma_device * adev,struct ppc440spe_adma_chan * chan)397112458ea0SAnatolij Gustschin static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
397212458ea0SAnatolij Gustschin 					struct ppc440spe_adma_chan *chan)
397312458ea0SAnatolij Gustschin {
397412458ea0SAnatolij Gustschin 	u32 mask, disable;
397512458ea0SAnatolij Gustschin 
397612458ea0SAnatolij Gustschin 	if (adev->id == PPC440SPE_XOR_ID) {
397712458ea0SAnatolij Gustschin 		/* disable XOR engine interrupts */
397812458ea0SAnatolij Gustschin 		mask = ioread32be(&adev->xor_reg->ier);
397912458ea0SAnatolij Gustschin 		mask &= ~(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
398012458ea0SAnatolij Gustschin 			  XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT);
398112458ea0SAnatolij Gustschin 		iowrite32be(mask, &adev->xor_reg->ier);
398212458ea0SAnatolij Gustschin 	} else {
398312458ea0SAnatolij Gustschin 		/* disable DMAx engine interrupts */
398412458ea0SAnatolij Gustschin 		disable = (adev->id == PPC440SPE_DMA0_ID) ?
398512458ea0SAnatolij Gustschin 			  (I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
398612458ea0SAnatolij Gustschin 			  (I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
398712458ea0SAnatolij Gustschin 		mask = ioread32(&adev->i2o_reg->iopim) | disable;
398812458ea0SAnatolij Gustschin 		iowrite32(mask, &adev->i2o_reg->iopim);
398912458ea0SAnatolij Gustschin 	}
399012458ea0SAnatolij Gustschin 	free_irq(adev->irq, chan);
399112458ea0SAnatolij Gustschin 	irq_dispose_mapping(adev->irq);
399212458ea0SAnatolij Gustschin 	if (adev->err_irq > 0) {
399312458ea0SAnatolij Gustschin 		free_irq(adev->err_irq, chan);
399412458ea0SAnatolij Gustschin 		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) {
399512458ea0SAnatolij Gustschin 			irq_dispose_mapping(adev->err_irq);
399612458ea0SAnatolij Gustschin 			iounmap(adev->i2o_reg);
399712458ea0SAnatolij Gustschin 		}
399812458ea0SAnatolij Gustschin 	}
399912458ea0SAnatolij Gustschin }
400012458ea0SAnatolij Gustschin 
400112458ea0SAnatolij Gustschin /**
400212458ea0SAnatolij Gustschin  * ppc440spe_adma_probe - probe the asynch device
400312458ea0SAnatolij Gustschin  */
ppc440spe_adma_probe(struct platform_device * ofdev)4004463a1f8bSBill Pemberton static int ppc440spe_adma_probe(struct platform_device *ofdev)
400512458ea0SAnatolij Gustschin {
400605c02542SAnatolij Gustschin 	struct device_node *np = ofdev->dev.of_node;
400712458ea0SAnatolij Gustschin 	struct resource res;
400812458ea0SAnatolij Gustschin 	struct ppc440spe_adma_device *adev;
400912458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *chan;
401012458ea0SAnatolij Gustschin 	struct ppc_dma_chan_ref *ref, *_ref;
401112458ea0SAnatolij Gustschin 	int ret = 0, initcode = PPC_ADMA_INIT_OK;
401212458ea0SAnatolij Gustschin 	const u32 *idx;
401312458ea0SAnatolij Gustschin 	int len;
401412458ea0SAnatolij Gustschin 	void *regs;
401512458ea0SAnatolij Gustschin 	u32 id, pool_size;
401612458ea0SAnatolij Gustschin 
401712458ea0SAnatolij Gustschin 	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
401812458ea0SAnatolij Gustschin 		id = PPC440SPE_XOR_ID;
401912458ea0SAnatolij Gustschin 		/* As far as the XOR engine is concerned, it does not
402012458ea0SAnatolij Gustschin 		 * use FIFOs but uses linked list. So there is no dependency
402112458ea0SAnatolij Gustschin 		 * between pool size to allocate and the engine configuration.
402212458ea0SAnatolij Gustschin 		 */
402312458ea0SAnatolij Gustschin 		pool_size = PAGE_SIZE << 1;
402412458ea0SAnatolij Gustschin 	} else {
402512458ea0SAnatolij Gustschin 		/* it is DMA0 or DMA1 */
402612458ea0SAnatolij Gustschin 		idx = of_get_property(np, "cell-index", &len);
402712458ea0SAnatolij Gustschin 		if (!idx || (len != sizeof(u32))) {
4028c6c93048SRob Herring 			dev_err(&ofdev->dev, "Device node %pOF has missing "
402912458ea0SAnatolij Gustschin 				"or invalid cell-index property\n",
4030c6c93048SRob Herring 				np);
403112458ea0SAnatolij Gustschin 			return -EINVAL;
403212458ea0SAnatolij Gustschin 		}
403312458ea0SAnatolij Gustschin 		id = *idx;
403412458ea0SAnatolij Gustschin 		/* DMA0,1 engines use FIFO to maintain CDBs, so we
403512458ea0SAnatolij Gustschin 		 * should allocate the pool accordingly to size of this
403612458ea0SAnatolij Gustschin 		 * FIFO. Thus, the pool size depends on the FIFO depth:
403712458ea0SAnatolij Gustschin 		 * how much CDBs pointers the FIFO may contain then so
403812458ea0SAnatolij Gustschin 		 * much CDBs we should provide in the pool.
403912458ea0SAnatolij Gustschin 		 * That is
404012458ea0SAnatolij Gustschin 		 *   CDB size = 32B;
404112458ea0SAnatolij Gustschin 		 *   CDBs number = (DMA0_FIFO_SIZE >> 3);
404212458ea0SAnatolij Gustschin 		 *   Pool size = CDBs number * CDB size =
404312458ea0SAnatolij Gustschin 		 *      = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2.
404412458ea0SAnatolij Gustschin 		 */
404512458ea0SAnatolij Gustschin 		pool_size = (id == PPC440SPE_DMA0_ID) ?
404612458ea0SAnatolij Gustschin 			    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
404712458ea0SAnatolij Gustschin 		pool_size <<= 2;
404812458ea0SAnatolij Gustschin 	}
404912458ea0SAnatolij Gustschin 
405012458ea0SAnatolij Gustschin 	if (of_address_to_resource(np, 0, &res)) {
405112458ea0SAnatolij Gustschin 		dev_err(&ofdev->dev, "failed to get memory resource\n");
405212458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_MEMRES;
405312458ea0SAnatolij Gustschin 		ret = -ENODEV;
405412458ea0SAnatolij Gustschin 		goto out;
405512458ea0SAnatolij Gustschin 	}
405612458ea0SAnatolij Gustschin 
405712458ea0SAnatolij Gustschin 	if (!request_mem_region(res.start, resource_size(&res),
405812458ea0SAnatolij Gustschin 				dev_driver_string(&ofdev->dev))) {
4059a584bff5SJoe Perches 		dev_err(&ofdev->dev, "failed to request memory region %pR\n",
4060a584bff5SJoe Perches 			&res);
406112458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_MEMREG;
406212458ea0SAnatolij Gustschin 		ret = -EBUSY;
406312458ea0SAnatolij Gustschin 		goto out;
406412458ea0SAnatolij Gustschin 	}
406512458ea0SAnatolij Gustschin 
406612458ea0SAnatolij Gustschin 	/* create a device */
406712458ea0SAnatolij Gustschin 	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
406812458ea0SAnatolij Gustschin 	if (!adev) {
406912458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_ALLOC;
407012458ea0SAnatolij Gustschin 		ret = -ENOMEM;
407112458ea0SAnatolij Gustschin 		goto err_adev_alloc;
407212458ea0SAnatolij Gustschin 	}
407312458ea0SAnatolij Gustschin 
407412458ea0SAnatolij Gustschin 	adev->id = id;
407512458ea0SAnatolij Gustschin 	adev->pool_size = pool_size;
407612458ea0SAnatolij Gustschin 	/* allocate coherent memory for hardware descriptors */
407712458ea0SAnatolij Gustschin 	adev->dma_desc_pool_virt = dma_alloc_coherent(&ofdev->dev,
407812458ea0SAnatolij Gustschin 					adev->pool_size, &adev->dma_desc_pool,
407912458ea0SAnatolij Gustschin 					GFP_KERNEL);
408012458ea0SAnatolij Gustschin 	if (adev->dma_desc_pool_virt == NULL) {
408112458ea0SAnatolij Gustschin 		dev_err(&ofdev->dev, "failed to allocate %d bytes of coherent "
408212458ea0SAnatolij Gustschin 			"memory for hardware descriptors\n",
408312458ea0SAnatolij Gustschin 			adev->pool_size);
408412458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_COHERENT;
408512458ea0SAnatolij Gustschin 		ret = -ENOMEM;
408612458ea0SAnatolij Gustschin 		goto err_dma_alloc;
408712458ea0SAnatolij Gustschin 	}
4088d73111c6SMasanari Iida 	dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
408912458ea0SAnatolij Gustschin 		adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);
409012458ea0SAnatolij Gustschin 
409112458ea0SAnatolij Gustschin 	regs = ioremap(res.start, resource_size(&res));
409212458ea0SAnatolij Gustschin 	if (!regs) {
409312458ea0SAnatolij Gustschin 		dev_err(&ofdev->dev, "failed to ioremap regs!\n");
4094f3b77727SJulia Lawall 		ret = -ENOMEM;
409512458ea0SAnatolij Gustschin 		goto err_regs_alloc;
409612458ea0SAnatolij Gustschin 	}
409712458ea0SAnatolij Gustschin 
409812458ea0SAnatolij Gustschin 	if (adev->id == PPC440SPE_XOR_ID) {
409912458ea0SAnatolij Gustschin 		adev->xor_reg = regs;
410012458ea0SAnatolij Gustschin 		/* Reset XOR */
410112458ea0SAnatolij Gustschin 		iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr);
410212458ea0SAnatolij Gustschin 		iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr);
410312458ea0SAnatolij Gustschin 	} else {
410412458ea0SAnatolij Gustschin 		size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ?
410512458ea0SAnatolij Gustschin 				   DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
410612458ea0SAnatolij Gustschin 		adev->dma_reg = regs;
410712458ea0SAnatolij Gustschin 		/* DMAx_FIFO_SIZE is defined in bytes,
410812458ea0SAnatolij Gustschin 		 * <fsiz> - is defined in number of CDB pointers (8byte).
410912458ea0SAnatolij Gustschin 		 * DMA FIFO Length = CSlength + CPlength, where
411012458ea0SAnatolij Gustschin 		 * CSlength = CPlength = (fsiz + 1) * 8.
411112458ea0SAnatolij Gustschin 		 */
411212458ea0SAnatolij Gustschin 		iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2),
411312458ea0SAnatolij Gustschin 			  &adev->dma_reg->fsiz);
411412458ea0SAnatolij Gustschin 		/* Configure DMA engine */
411512458ea0SAnatolij Gustschin 		iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN,
411612458ea0SAnatolij Gustschin 			  &adev->dma_reg->cfg);
411712458ea0SAnatolij Gustschin 		/* Clear Status */
411812458ea0SAnatolij Gustschin 		iowrite32(~0, &adev->dma_reg->dsts);
411912458ea0SAnatolij Gustschin 	}
412012458ea0SAnatolij Gustschin 
412112458ea0SAnatolij Gustschin 	adev->dev = &ofdev->dev;
412212458ea0SAnatolij Gustschin 	adev->common.dev = &ofdev->dev;
412312458ea0SAnatolij Gustschin 	INIT_LIST_HEAD(&adev->common.channels);
4124dd3daca1SJingoo Han 	platform_set_drvdata(ofdev, adev);
412512458ea0SAnatolij Gustschin 
412612458ea0SAnatolij Gustschin 	/* create a channel */
412712458ea0SAnatolij Gustschin 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
412812458ea0SAnatolij Gustschin 	if (!chan) {
412912458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_CHANNEL;
413012458ea0SAnatolij Gustschin 		ret = -ENOMEM;
413112458ea0SAnatolij Gustschin 		goto err_chan_alloc;
413212458ea0SAnatolij Gustschin 	}
413312458ea0SAnatolij Gustschin 
413412458ea0SAnatolij Gustschin 	spin_lock_init(&chan->lock);
413512458ea0SAnatolij Gustschin 	INIT_LIST_HEAD(&chan->chain);
413612458ea0SAnatolij Gustschin 	INIT_LIST_HEAD(&chan->all_slots);
413712458ea0SAnatolij Gustschin 	chan->device = adev;
413812458ea0SAnatolij Gustschin 	chan->common.device = &adev->common;
41398ac69546SRussell King - ARM Linux 	dma_cookie_init(&chan->common);
414012458ea0SAnatolij Gustschin 	list_add_tail(&chan->common.device_node, &adev->common.channels);
41417f828176SAllen Pais 	tasklet_setup(&chan->irq_tasklet, ppc440spe_adma_tasklet);
414212458ea0SAnatolij Gustschin 
414312458ea0SAnatolij Gustschin 	/* allocate and map helper pages for async validation or
414412458ea0SAnatolij Gustschin 	 * async_mult/async_sum_product operations on DMA0/1.
414512458ea0SAnatolij Gustschin 	 */
414612458ea0SAnatolij Gustschin 	if (adev->id != PPC440SPE_XOR_ID) {
414712458ea0SAnatolij Gustschin 		chan->pdest_page = alloc_page(GFP_KERNEL);
414812458ea0SAnatolij Gustschin 		chan->qdest_page = alloc_page(GFP_KERNEL);
414912458ea0SAnatolij Gustschin 		if (!chan->pdest_page ||
415012458ea0SAnatolij Gustschin 		    !chan->qdest_page) {
415112458ea0SAnatolij Gustschin 			if (chan->pdest_page)
415212458ea0SAnatolij Gustschin 				__free_page(chan->pdest_page);
415312458ea0SAnatolij Gustschin 			if (chan->qdest_page)
415412458ea0SAnatolij Gustschin 				__free_page(chan->qdest_page);
415512458ea0SAnatolij Gustschin 			ret = -ENOMEM;
415612458ea0SAnatolij Gustschin 			goto err_page_alloc;
415712458ea0SAnatolij Gustschin 		}
415812458ea0SAnatolij Gustschin 		chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0,
415912458ea0SAnatolij Gustschin 					   PAGE_SIZE, DMA_BIDIRECTIONAL);
416012458ea0SAnatolij Gustschin 		chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0,
416112458ea0SAnatolij Gustschin 					   PAGE_SIZE, DMA_BIDIRECTIONAL);
416212458ea0SAnatolij Gustschin 	}
416312458ea0SAnatolij Gustschin 
416412458ea0SAnatolij Gustschin 	ref = kmalloc(sizeof(*ref), GFP_KERNEL);
416512458ea0SAnatolij Gustschin 	if (ref) {
416612458ea0SAnatolij Gustschin 		ref->chan = &chan->common;
416712458ea0SAnatolij Gustschin 		INIT_LIST_HEAD(&ref->node);
416812458ea0SAnatolij Gustschin 		list_add_tail(&ref->node, &ppc440spe_adma_chan_list);
416912458ea0SAnatolij Gustschin 	} else {
417012458ea0SAnatolij Gustschin 		dev_err(&ofdev->dev, "failed to allocate channel reference!\n");
417112458ea0SAnatolij Gustschin 		ret = -ENOMEM;
417212458ea0SAnatolij Gustschin 		goto err_ref_alloc;
417312458ea0SAnatolij Gustschin 	}
417412458ea0SAnatolij Gustschin 
417512458ea0SAnatolij Gustschin 	ret = ppc440spe_adma_setup_irqs(adev, chan, &initcode);
417612458ea0SAnatolij Gustschin 	if (ret)
417712458ea0SAnatolij Gustschin 		goto err_irq;
417812458ea0SAnatolij Gustschin 
417912458ea0SAnatolij Gustschin 	ppc440spe_adma_init_capabilities(adev);
418012458ea0SAnatolij Gustschin 
418112458ea0SAnatolij Gustschin 	ret = dma_async_device_register(&adev->common);
418212458ea0SAnatolij Gustschin 	if (ret) {
418312458ea0SAnatolij Gustschin 		initcode = PPC_ADMA_INIT_REGISTER;
418412458ea0SAnatolij Gustschin 		dev_err(&ofdev->dev, "failed to register dma device\n");
418512458ea0SAnatolij Gustschin 		goto err_dev_reg;
418612458ea0SAnatolij Gustschin 	}
418712458ea0SAnatolij Gustschin 
418812458ea0SAnatolij Gustschin 	goto out;
418912458ea0SAnatolij Gustschin 
419012458ea0SAnatolij Gustschin err_dev_reg:
419112458ea0SAnatolij Gustschin 	ppc440spe_adma_release_irqs(adev, chan);
419212458ea0SAnatolij Gustschin err_irq:
419312458ea0SAnatolij Gustschin 	list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list, node) {
419412458ea0SAnatolij Gustschin 		if (chan == to_ppc440spe_adma_chan(ref->chan)) {
419512458ea0SAnatolij Gustschin 			list_del(&ref->node);
419612458ea0SAnatolij Gustschin 			kfree(ref);
419712458ea0SAnatolij Gustschin 		}
419812458ea0SAnatolij Gustschin 	}
419912458ea0SAnatolij Gustschin err_ref_alloc:
420012458ea0SAnatolij Gustschin 	if (adev->id != PPC440SPE_XOR_ID) {
420112458ea0SAnatolij Gustschin 		dma_unmap_page(&ofdev->dev, chan->pdest,
420212458ea0SAnatolij Gustschin 			       PAGE_SIZE, DMA_BIDIRECTIONAL);
420312458ea0SAnatolij Gustschin 		dma_unmap_page(&ofdev->dev, chan->qdest,
420412458ea0SAnatolij Gustschin 			       PAGE_SIZE, DMA_BIDIRECTIONAL);
420512458ea0SAnatolij Gustschin 		__free_page(chan->pdest_page);
420612458ea0SAnatolij Gustschin 		__free_page(chan->qdest_page);
420712458ea0SAnatolij Gustschin 	}
420812458ea0SAnatolij Gustschin err_page_alloc:
420912458ea0SAnatolij Gustschin 	kfree(chan);
421012458ea0SAnatolij Gustschin err_chan_alloc:
421112458ea0SAnatolij Gustschin 	if (adev->id == PPC440SPE_XOR_ID)
421212458ea0SAnatolij Gustschin 		iounmap(adev->xor_reg);
421312458ea0SAnatolij Gustschin 	else
421412458ea0SAnatolij Gustschin 		iounmap(adev->dma_reg);
421512458ea0SAnatolij Gustschin err_regs_alloc:
421612458ea0SAnatolij Gustschin 	dma_free_coherent(adev->dev, adev->pool_size,
421712458ea0SAnatolij Gustschin 			  adev->dma_desc_pool_virt,
421812458ea0SAnatolij Gustschin 			  adev->dma_desc_pool);
421912458ea0SAnatolij Gustschin err_dma_alloc:
422012458ea0SAnatolij Gustschin 	kfree(adev);
422112458ea0SAnatolij Gustschin err_adev_alloc:
422212458ea0SAnatolij Gustschin 	release_mem_region(res.start, resource_size(&res));
422312458ea0SAnatolij Gustschin out:
422412458ea0SAnatolij Gustschin 	if (id < PPC440SPE_ADMA_ENGINES_NUM)
422512458ea0SAnatolij Gustschin 		ppc440spe_adma_devices[id] = initcode;
422612458ea0SAnatolij Gustschin 
422712458ea0SAnatolij Gustschin 	return ret;
422812458ea0SAnatolij Gustschin }
422912458ea0SAnatolij Gustschin 
423012458ea0SAnatolij Gustschin /**
423112458ea0SAnatolij Gustschin  * ppc440spe_adma_remove - remove the asynch device
423212458ea0SAnatolij Gustschin  */
ppc440spe_adma_remove(struct platform_device * ofdev)42334bf27b8bSGreg Kroah-Hartman static int ppc440spe_adma_remove(struct platform_device *ofdev)
423412458ea0SAnatolij Gustschin {
4235dd3daca1SJingoo Han 	struct ppc440spe_adma_device *adev = platform_get_drvdata(ofdev);
423605c02542SAnatolij Gustschin 	struct device_node *np = ofdev->dev.of_node;
423712458ea0SAnatolij Gustschin 	struct resource res;
423812458ea0SAnatolij Gustschin 	struct dma_chan *chan, *_chan;
423912458ea0SAnatolij Gustschin 	struct ppc_dma_chan_ref *ref, *_ref;
424012458ea0SAnatolij Gustschin 	struct ppc440spe_adma_chan *ppc440spe_chan;
424112458ea0SAnatolij Gustschin 
424212458ea0SAnatolij Gustschin 	if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
424312458ea0SAnatolij Gustschin 		ppc440spe_adma_devices[adev->id] = -1;
424412458ea0SAnatolij Gustschin 
424512458ea0SAnatolij Gustschin 	dma_async_device_unregister(&adev->common);
424612458ea0SAnatolij Gustschin 
424712458ea0SAnatolij Gustschin 	list_for_each_entry_safe(chan, _chan, &adev->common.channels,
424812458ea0SAnatolij Gustschin 				 device_node) {
424912458ea0SAnatolij Gustschin 		ppc440spe_chan = to_ppc440spe_adma_chan(chan);
425012458ea0SAnatolij Gustschin 		ppc440spe_adma_release_irqs(adev, ppc440spe_chan);
425112458ea0SAnatolij Gustschin 		tasklet_kill(&ppc440spe_chan->irq_tasklet);
425212458ea0SAnatolij Gustschin 		if (adev->id != PPC440SPE_XOR_ID) {
425312458ea0SAnatolij Gustschin 			dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest,
425412458ea0SAnatolij Gustschin 					PAGE_SIZE, DMA_BIDIRECTIONAL);
425512458ea0SAnatolij Gustschin 			dma_unmap_page(&ofdev->dev, ppc440spe_chan->qdest,
425612458ea0SAnatolij Gustschin 					PAGE_SIZE, DMA_BIDIRECTIONAL);
425712458ea0SAnatolij Gustschin 			__free_page(ppc440spe_chan->pdest_page);
425812458ea0SAnatolij Gustschin 			__free_page(ppc440spe_chan->qdest_page);
425912458ea0SAnatolij Gustschin 		}
426012458ea0SAnatolij Gustschin 		list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list,
426112458ea0SAnatolij Gustschin 					 node) {
426212458ea0SAnatolij Gustschin 			if (ppc440spe_chan ==
426312458ea0SAnatolij Gustschin 			    to_ppc440spe_adma_chan(ref->chan)) {
426412458ea0SAnatolij Gustschin 				list_del(&ref->node);
426512458ea0SAnatolij Gustschin 				kfree(ref);
426612458ea0SAnatolij Gustschin 			}
426712458ea0SAnatolij Gustschin 		}
426812458ea0SAnatolij Gustschin 		list_del(&chan->device_node);
426912458ea0SAnatolij Gustschin 		kfree(ppc440spe_chan);
427012458ea0SAnatolij Gustschin 	}
427112458ea0SAnatolij Gustschin 
427212458ea0SAnatolij Gustschin 	dma_free_coherent(adev->dev, adev->pool_size,
427312458ea0SAnatolij Gustschin 			  adev->dma_desc_pool_virt, adev->dma_desc_pool);
427412458ea0SAnatolij Gustschin 	if (adev->id == PPC440SPE_XOR_ID)
427512458ea0SAnatolij Gustschin 		iounmap(adev->xor_reg);
427612458ea0SAnatolij Gustschin 	else
427712458ea0SAnatolij Gustschin 		iounmap(adev->dma_reg);
427812458ea0SAnatolij Gustschin 	of_address_to_resource(np, 0, &res);
427912458ea0SAnatolij Gustschin 	release_mem_region(res.start, resource_size(&res));
428012458ea0SAnatolij Gustschin 	kfree(adev);
428112458ea0SAnatolij Gustschin 	return 0;
428212458ea0SAnatolij Gustschin }
428312458ea0SAnatolij Gustschin 
428412458ea0SAnatolij Gustschin /*
428512458ea0SAnatolij Gustschin  * /sys driver interface to enable h/w RAID-6 capabilities
428612458ea0SAnatolij Gustschin  * Files created in e.g. /sys/devices/plb.0/400100100.dma0/driver/
428712458ea0SAnatolij Gustschin  * directory are "devices", "enable" and "poly".
428812458ea0SAnatolij Gustschin  * "devices" shows available engines.
428912458ea0SAnatolij Gustschin  * "enable" is used to enable RAID-6 capabilities or to check
429012458ea0SAnatolij Gustschin  * whether these has been activated.
429112458ea0SAnatolij Gustschin  * "poly" allows setting/checking used polynomial (for PPC440SPe only).
429212458ea0SAnatolij Gustschin  */
429312458ea0SAnatolij Gustschin 
devices_show(struct device_driver * dev,char * buf)429413efe1a0SGreg Kroah-Hartman static ssize_t devices_show(struct device_driver *dev, char *buf)
429512458ea0SAnatolij Gustschin {
429612458ea0SAnatolij Gustschin 	ssize_t size = 0;
429712458ea0SAnatolij Gustschin 	int i;
429812458ea0SAnatolij Gustschin 
429912458ea0SAnatolij Gustschin 	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) {
430012458ea0SAnatolij Gustschin 		if (ppc440spe_adma_devices[i] == -1)
430112458ea0SAnatolij Gustschin 			continue;
4302faab1234Sye xingchen 		size += sysfs_emit_at(buf, size, "PPC440SP(E)-ADMA.%d: %s\n",
4303faab1234Sye xingchen 				     i, ppc_adma_errors[ppc440spe_adma_devices[i]]);
430412458ea0SAnatolij Gustschin 	}
430512458ea0SAnatolij Gustschin 	return size;
430612458ea0SAnatolij Gustschin }
430713efe1a0SGreg Kroah-Hartman static DRIVER_ATTR_RO(devices);
430812458ea0SAnatolij Gustschin 
enable_show(struct device_driver * dev,char * buf)430913efe1a0SGreg Kroah-Hartman static ssize_t enable_show(struct device_driver *dev, char *buf)
431012458ea0SAnatolij Gustschin {
4311faab1234Sye xingchen 	return sysfs_emit(buf, "PPC440SP(e) RAID-6 capabilities are %sABLED.\n",
431212458ea0SAnatolij Gustschin 			  ppc440spe_r6_enabled ? "EN" : "DIS");
431312458ea0SAnatolij Gustschin }
431412458ea0SAnatolij Gustschin 
enable_store(struct device_driver * dev,const char * buf,size_t count)431513efe1a0SGreg Kroah-Hartman static ssize_t enable_store(struct device_driver *dev, const char *buf,
431613efe1a0SGreg Kroah-Hartman 			    size_t count)
431712458ea0SAnatolij Gustschin {
431812458ea0SAnatolij Gustschin 	unsigned long val;
431948ae638bSSalah Triki 	int err;
432012458ea0SAnatolij Gustschin 
432112458ea0SAnatolij Gustschin 	if (!count || count > 11)
432212458ea0SAnatolij Gustschin 		return -EINVAL;
432312458ea0SAnatolij Gustschin 
432412458ea0SAnatolij Gustschin 	if (!ppc440spe_r6_tchan)
432512458ea0SAnatolij Gustschin 		return -EFAULT;
432612458ea0SAnatolij Gustschin 
432712458ea0SAnatolij Gustschin 	/* Write a key */
432848ae638bSSalah Triki 	err = kstrtoul(buf, 16, &val);
432948ae638bSSalah Triki 	if (err)
433048ae638bSSalah Triki 		return err;
433148ae638bSSalah Triki 
433212458ea0SAnatolij Gustschin 	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_XORBA, val);
433312458ea0SAnatolij Gustschin 	isync();
433412458ea0SAnatolij Gustschin 
433512458ea0SAnatolij Gustschin 	/* Verify whether it really works now */
433612458ea0SAnatolij Gustschin 	if (ppc440spe_test_raid6(ppc440spe_r6_tchan) == 0) {
433712458ea0SAnatolij Gustschin 		pr_info("PPC440SP(e) RAID-6 has been activated "
433812458ea0SAnatolij Gustschin 			"successfully\n");
433912458ea0SAnatolij Gustschin 		ppc440spe_r6_enabled = 1;
434012458ea0SAnatolij Gustschin 	} else {
434112458ea0SAnatolij Gustschin 		pr_info("PPC440SP(e) RAID-6 hasn't been activated!"
434212458ea0SAnatolij Gustschin 			" Error key ?\n");
434312458ea0SAnatolij Gustschin 		ppc440spe_r6_enabled = 0;
434412458ea0SAnatolij Gustschin 	}
434512458ea0SAnatolij Gustschin 	return count;
434612458ea0SAnatolij Gustschin }
434713efe1a0SGreg Kroah-Hartman static DRIVER_ATTR_RW(enable);
434812458ea0SAnatolij Gustschin 
poly_show(struct device_driver * dev,char * buf)434927d8d2d7SChristian Lamparter static ssize_t poly_show(struct device_driver *dev, char *buf)
435012458ea0SAnatolij Gustschin {
435112458ea0SAnatolij Gustschin 	ssize_t size = 0;
435212458ea0SAnatolij Gustschin 	u32 reg;
435312458ea0SAnatolij Gustschin 
435412458ea0SAnatolij Gustschin #ifdef CONFIG_440SP
435512458ea0SAnatolij Gustschin 	/* 440SP has fixed polynomial */
435612458ea0SAnatolij Gustschin 	reg = 0x4d;
435712458ea0SAnatolij Gustschin #else
435812458ea0SAnatolij Gustschin 	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
435912458ea0SAnatolij Gustschin 	reg >>= MQ0_CFBHL_POLY;
436012458ea0SAnatolij Gustschin 	reg &= 0xFF;
436112458ea0SAnatolij Gustschin #endif
436212458ea0SAnatolij Gustschin 
4363faab1234Sye xingchen 	size = sysfs_emit(buf, "PPC440SP(e) RAID-6 driver "
436412458ea0SAnatolij Gustschin 			"uses 0x1%02x polynomial.\n", reg);
436512458ea0SAnatolij Gustschin 	return size;
436612458ea0SAnatolij Gustschin }
436712458ea0SAnatolij Gustschin 
poly_store(struct device_driver * dev,const char * buf,size_t count)436813efe1a0SGreg Kroah-Hartman static ssize_t poly_store(struct device_driver *dev, const char *buf,
436913efe1a0SGreg Kroah-Hartman 			  size_t count)
437012458ea0SAnatolij Gustschin {
437112458ea0SAnatolij Gustschin 	unsigned long reg, val;
437248ae638bSSalah Triki 	int err;
437312458ea0SAnatolij Gustschin #ifdef CONFIG_440SP
437412458ea0SAnatolij Gustschin 	/* 440SP uses default 0x14D polynomial only */
437512458ea0SAnatolij Gustschin 	return -EINVAL;
437612458ea0SAnatolij Gustschin #endif
437712458ea0SAnatolij Gustschin 
437812458ea0SAnatolij Gustschin 	if (!count || count > 6)
437912458ea0SAnatolij Gustschin 		return -EINVAL;
438012458ea0SAnatolij Gustschin 
438112458ea0SAnatolij Gustschin 	/* e.g., 0x14D or 0x11D */
438248ae638bSSalah Triki 	err = kstrtoul(buf, 16, &val);
438348ae638bSSalah Triki 	if (err)
438448ae638bSSalah Triki 		return err;
438512458ea0SAnatolij Gustschin 
438612458ea0SAnatolij Gustschin 	if (val & ~0x1FF)
438712458ea0SAnatolij Gustschin 		return -EINVAL;
438812458ea0SAnatolij Gustschin 
438912458ea0SAnatolij Gustschin 	val &= 0xFF;
439012458ea0SAnatolij Gustschin 	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
439112458ea0SAnatolij Gustschin 	reg &= ~(0xFF << MQ0_CFBHL_POLY);
439212458ea0SAnatolij Gustschin 	reg |= val << MQ0_CFBHL_POLY;
439312458ea0SAnatolij Gustschin 	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, reg);
439412458ea0SAnatolij Gustschin 
439512458ea0SAnatolij Gustschin 	return count;
439612458ea0SAnatolij Gustschin }
439713efe1a0SGreg Kroah-Hartman static DRIVER_ATTR_RW(poly);
439812458ea0SAnatolij Gustschin 
439912458ea0SAnatolij Gustschin /*
440012458ea0SAnatolij Gustschin  * Common initialisation for RAID engines; allocate memory for
440112458ea0SAnatolij Gustschin  * DMAx FIFOs, perform configuration common for all DMA engines.
440212458ea0SAnatolij Gustschin  * Further DMA engine specific configuration is done at probe time.
440312458ea0SAnatolij Gustschin  */
ppc440spe_configure_raid_devices(void)440412458ea0SAnatolij Gustschin static int ppc440spe_configure_raid_devices(void)
440512458ea0SAnatolij Gustschin {
440612458ea0SAnatolij Gustschin 	struct device_node *np;
440712458ea0SAnatolij Gustschin 	struct resource i2o_res;
440812458ea0SAnatolij Gustschin 	struct i2o_regs __iomem *i2o_reg;
440912458ea0SAnatolij Gustschin 	dcr_host_t i2o_dcr_host;
441012458ea0SAnatolij Gustschin 	unsigned int dcr_base, dcr_len;
441112458ea0SAnatolij Gustschin 	int i, ret;
441212458ea0SAnatolij Gustschin 
441312458ea0SAnatolij Gustschin 	np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
441412458ea0SAnatolij Gustschin 	if (!np) {
441512458ea0SAnatolij Gustschin 		pr_err("%s: can't find I2O device tree node\n",
441612458ea0SAnatolij Gustschin 			__func__);
441712458ea0SAnatolij Gustschin 		return -ENODEV;
441812458ea0SAnatolij Gustschin 	}
441912458ea0SAnatolij Gustschin 
442012458ea0SAnatolij Gustschin 	if (of_address_to_resource(np, 0, &i2o_res)) {
442112458ea0SAnatolij Gustschin 		of_node_put(np);
442212458ea0SAnatolij Gustschin 		return -EINVAL;
442312458ea0SAnatolij Gustschin 	}
442412458ea0SAnatolij Gustschin 
442512458ea0SAnatolij Gustschin 	i2o_reg = of_iomap(np, 0);
442612458ea0SAnatolij Gustschin 	if (!i2o_reg) {
442712458ea0SAnatolij Gustschin 		pr_err("%s: failed to map I2O registers\n", __func__);
442812458ea0SAnatolij Gustschin 		of_node_put(np);
442912458ea0SAnatolij Gustschin 		return -EINVAL;
443012458ea0SAnatolij Gustschin 	}
443112458ea0SAnatolij Gustschin 
443212458ea0SAnatolij Gustschin 	/* Get I2O DCRs base */
443312458ea0SAnatolij Gustschin 	dcr_base = dcr_resource_start(np, 0);
443412458ea0SAnatolij Gustschin 	dcr_len = dcr_resource_len(np, 0);
443512458ea0SAnatolij Gustschin 	if (!dcr_base && !dcr_len) {
4436c6c93048SRob Herring 		pr_err("%pOF: can't get DCR registers base/len!\n", np);
443712458ea0SAnatolij Gustschin 		of_node_put(np);
443812458ea0SAnatolij Gustschin 		iounmap(i2o_reg);
443912458ea0SAnatolij Gustschin 		return -ENODEV;
444012458ea0SAnatolij Gustschin 	}
444112458ea0SAnatolij Gustschin 
444212458ea0SAnatolij Gustschin 	i2o_dcr_host = dcr_map(np, dcr_base, dcr_len);
444312458ea0SAnatolij Gustschin 	if (!DCR_MAP_OK(i2o_dcr_host)) {
4444c6c93048SRob Herring 		pr_err("%pOF: failed to map DCRs!\n", np);
444512458ea0SAnatolij Gustschin 		of_node_put(np);
444612458ea0SAnatolij Gustschin 		iounmap(i2o_reg);
444712458ea0SAnatolij Gustschin 		return -ENODEV;
444812458ea0SAnatolij Gustschin 	}
444912458ea0SAnatolij Gustschin 	of_node_put(np);
445012458ea0SAnatolij Gustschin 
445112458ea0SAnatolij Gustschin 	/* Provide memory regions for DMA's FIFOs: I2O, DMA0 and DMA1 share
445212458ea0SAnatolij Gustschin 	 * the base address of FIFO memory space.
445312458ea0SAnatolij Gustschin 	 * Actually we need twice more physical memory than programmed in the
445412458ea0SAnatolij Gustschin 	 * <fsiz> register (because there are two FIFOs for each DMA: CP and CS)
445512458ea0SAnatolij Gustschin 	 */
445612458ea0SAnatolij Gustschin 	ppc440spe_dma_fifo_buf = kmalloc((DMA0_FIFO_SIZE + DMA1_FIFO_SIZE) << 1,
445712458ea0SAnatolij Gustschin 					 GFP_KERNEL);
445812458ea0SAnatolij Gustschin 	if (!ppc440spe_dma_fifo_buf) {
445912458ea0SAnatolij Gustschin 		pr_err("%s: DMA FIFO buffer allocation failed.\n", __func__);
446012458ea0SAnatolij Gustschin 		iounmap(i2o_reg);
446112458ea0SAnatolij Gustschin 		dcr_unmap(i2o_dcr_host, dcr_len);
446212458ea0SAnatolij Gustschin 		return -ENOMEM;
446312458ea0SAnatolij Gustschin 	}
446412458ea0SAnatolij Gustschin 
446512458ea0SAnatolij Gustschin 	/*
446612458ea0SAnatolij Gustschin 	 * Configure h/w
446712458ea0SAnatolij Gustschin 	 */
446812458ea0SAnatolij Gustschin 	/* Reset I2O/DMA */
446912458ea0SAnatolij Gustschin 	mtdcri(SDR0, DCRN_SDR0_SRST, DCRN_SDR0_SRST_I2ODMA);
447012458ea0SAnatolij Gustschin 	mtdcri(SDR0, DCRN_SDR0_SRST, 0);
447112458ea0SAnatolij Gustschin 
447212458ea0SAnatolij Gustschin 	/* Setup the base address of mmaped registers */
447312458ea0SAnatolij Gustschin 	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAH, (u32)(i2o_res.start >> 32));
447412458ea0SAnatolij Gustschin 	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAL, (u32)(i2o_res.start) |
447512458ea0SAnatolij Gustschin 						I2O_REG_ENABLE);
447612458ea0SAnatolij Gustschin 	dcr_unmap(i2o_dcr_host, dcr_len);
447712458ea0SAnatolij Gustschin 
447812458ea0SAnatolij Gustschin 	/* Setup FIFO memory space base address */
447912458ea0SAnatolij Gustschin 	iowrite32(0, &i2o_reg->ifbah);
448012458ea0SAnatolij Gustschin 	iowrite32(((u32)__pa(ppc440spe_dma_fifo_buf)), &i2o_reg->ifbal);
448112458ea0SAnatolij Gustschin 
448212458ea0SAnatolij Gustschin 	/* set zero FIFO size for I2O, so the whole
448312458ea0SAnatolij Gustschin 	 * ppc440spe_dma_fifo_buf is used by DMAs.
448412458ea0SAnatolij Gustschin 	 * DMAx_FIFOs will be configured while probe.
448512458ea0SAnatolij Gustschin 	 */
448612458ea0SAnatolij Gustschin 	iowrite32(0, &i2o_reg->ifsiz);
448712458ea0SAnatolij Gustschin 	iounmap(i2o_reg);
448812458ea0SAnatolij Gustschin 
448912458ea0SAnatolij Gustschin 	/* To prepare WXOR/RXOR functionality we need access to
449012458ea0SAnatolij Gustschin 	 * Memory Queue Module DCRs (finally it will be enabled
449112458ea0SAnatolij Gustschin 	 * via /sys interface of the ppc440spe ADMA driver).
449212458ea0SAnatolij Gustschin 	 */
449312458ea0SAnatolij Gustschin 	np = of_find_compatible_node(NULL, NULL, "ibm,mq-440spe");
449412458ea0SAnatolij Gustschin 	if (!np) {
449512458ea0SAnatolij Gustschin 		pr_err("%s: can't find MQ device tree node\n",
449612458ea0SAnatolij Gustschin 			__func__);
449712458ea0SAnatolij Gustschin 		ret = -ENODEV;
449812458ea0SAnatolij Gustschin 		goto out_free;
449912458ea0SAnatolij Gustschin 	}
450012458ea0SAnatolij Gustschin 
450112458ea0SAnatolij Gustschin 	/* Get MQ DCRs base */
450212458ea0SAnatolij Gustschin 	dcr_base = dcr_resource_start(np, 0);
450312458ea0SAnatolij Gustschin 	dcr_len = dcr_resource_len(np, 0);
450412458ea0SAnatolij Gustschin 	if (!dcr_base && !dcr_len) {
4505c6c93048SRob Herring 		pr_err("%pOF: can't get DCR registers base/len!\n", np);
450612458ea0SAnatolij Gustschin 		ret = -ENODEV;
450712458ea0SAnatolij Gustschin 		goto out_mq;
450812458ea0SAnatolij Gustschin 	}
450912458ea0SAnatolij Gustschin 
451012458ea0SAnatolij Gustschin 	ppc440spe_mq_dcr_host = dcr_map(np, dcr_base, dcr_len);
451112458ea0SAnatolij Gustschin 	if (!DCR_MAP_OK(ppc440spe_mq_dcr_host)) {
4512c6c93048SRob Herring 		pr_err("%pOF: failed to map DCRs!\n", np);
451312458ea0SAnatolij Gustschin 		ret = -ENODEV;
451412458ea0SAnatolij Gustschin 		goto out_mq;
451512458ea0SAnatolij Gustschin 	}
451612458ea0SAnatolij Gustschin 	of_node_put(np);
451712458ea0SAnatolij Gustschin 	ppc440spe_mq_dcr_len = dcr_len;
451812458ea0SAnatolij Gustschin 
451912458ea0SAnatolij Gustschin 	/* Set HB alias */
452012458ea0SAnatolij Gustschin 	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_BAUH, DMA_CUED_XOR_HB);
452112458ea0SAnatolij Gustschin 
452212458ea0SAnatolij Gustschin 	/* Set:
452312458ea0SAnatolij Gustschin 	 * - LL transaction passing limit to 1;
452412458ea0SAnatolij Gustschin 	 * - Memory controller cycle limit to 1;
452512458ea0SAnatolij Gustschin 	 * - Galois Polynomial to 0x14d (default)
452612458ea0SAnatolij Gustschin 	 */
452712458ea0SAnatolij Gustschin 	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL,
452812458ea0SAnatolij Gustschin 		  (1 << MQ0_CFBHL_TPLM) | (1 << MQ0_CFBHL_HBCL) |
452912458ea0SAnatolij Gustschin 		  (PPC440SPE_DEFAULT_POLY << MQ0_CFBHL_POLY));
453012458ea0SAnatolij Gustschin 
453112458ea0SAnatolij Gustschin 	atomic_set(&ppc440spe_adma_err_irq_ref, 0);
453212458ea0SAnatolij Gustschin 	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++)
453312458ea0SAnatolij Gustschin 		ppc440spe_adma_devices[i] = -1;
453412458ea0SAnatolij Gustschin 
453512458ea0SAnatolij Gustschin 	return 0;
453612458ea0SAnatolij Gustschin 
453712458ea0SAnatolij Gustschin out_mq:
453812458ea0SAnatolij Gustschin 	of_node_put(np);
453912458ea0SAnatolij Gustschin out_free:
454012458ea0SAnatolij Gustschin 	kfree(ppc440spe_dma_fifo_buf);
454112458ea0SAnatolij Gustschin 	return ret;
454212458ea0SAnatolij Gustschin }
454312458ea0SAnatolij Gustschin 
45444bf27b8bSGreg Kroah-Hartman static const struct of_device_id ppc440spe_adma_of_match[] = {
454512458ea0SAnatolij Gustschin 	{ .compatible	= "ibm,dma-440spe", },
454612458ea0SAnatolij Gustschin 	{ .compatible	= "amcc,xor-accelerator", },
454712458ea0SAnatolij Gustschin 	{},
454812458ea0SAnatolij Gustschin };
454912458ea0SAnatolij Gustschin MODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match);
455012458ea0SAnatolij Gustschin 
455100006124SGrant Likely static struct platform_driver ppc440spe_adma_driver = {
455212458ea0SAnatolij Gustschin 	.probe = ppc440spe_adma_probe,
4553a7d6e3ecSBill Pemberton 	.remove = ppc440spe_adma_remove,
455412458ea0SAnatolij Gustschin 	.driver = {
455512458ea0SAnatolij Gustschin 		.name = "PPC440SP(E)-ADMA",
45564018294bSGrant Likely 		.of_match_table = ppc440spe_adma_of_match,
455712458ea0SAnatolij Gustschin 	},
455812458ea0SAnatolij Gustschin };
455912458ea0SAnatolij Gustschin 
ppc440spe_adma_init(void)456012458ea0SAnatolij Gustschin static __init int ppc440spe_adma_init(void)
456112458ea0SAnatolij Gustschin {
456212458ea0SAnatolij Gustschin 	int ret;
456312458ea0SAnatolij Gustschin 
456412458ea0SAnatolij Gustschin 	ret = ppc440spe_configure_raid_devices();
456512458ea0SAnatolij Gustschin 	if (ret)
456612458ea0SAnatolij Gustschin 		return ret;
456712458ea0SAnatolij Gustschin 
456800006124SGrant Likely 	ret = platform_driver_register(&ppc440spe_adma_driver);
456912458ea0SAnatolij Gustschin 	if (ret) {
457012458ea0SAnatolij Gustschin 		pr_err("%s: failed to register platform driver\n",
457112458ea0SAnatolij Gustschin 			__func__);
457212458ea0SAnatolij Gustschin 		goto out_reg;
457312458ea0SAnatolij Gustschin 	}
457412458ea0SAnatolij Gustschin 
457512458ea0SAnatolij Gustschin 	/* Initialization status */
457612458ea0SAnatolij Gustschin 	ret = driver_create_file(&ppc440spe_adma_driver.driver,
457712458ea0SAnatolij Gustschin 				 &driver_attr_devices);
457812458ea0SAnatolij Gustschin 	if (ret)
457912458ea0SAnatolij Gustschin 		goto out_dev;
458012458ea0SAnatolij Gustschin 
458112458ea0SAnatolij Gustschin 	/* RAID-6 h/w enable entry */
458212458ea0SAnatolij Gustschin 	ret = driver_create_file(&ppc440spe_adma_driver.driver,
458312458ea0SAnatolij Gustschin 				 &driver_attr_enable);
458412458ea0SAnatolij Gustschin 	if (ret)
458512458ea0SAnatolij Gustschin 		goto out_en;
458612458ea0SAnatolij Gustschin 
458712458ea0SAnatolij Gustschin 	/* GF polynomial to use */
458812458ea0SAnatolij Gustschin 	ret = driver_create_file(&ppc440spe_adma_driver.driver,
458912458ea0SAnatolij Gustschin 				 &driver_attr_poly);
459012458ea0SAnatolij Gustschin 	if (!ret)
459112458ea0SAnatolij Gustschin 		return ret;
459212458ea0SAnatolij Gustschin 
459312458ea0SAnatolij Gustschin 	driver_remove_file(&ppc440spe_adma_driver.driver,
459412458ea0SAnatolij Gustschin 			   &driver_attr_enable);
459512458ea0SAnatolij Gustschin out_en:
459612458ea0SAnatolij Gustschin 	driver_remove_file(&ppc440spe_adma_driver.driver,
459712458ea0SAnatolij Gustschin 			   &driver_attr_devices);
459812458ea0SAnatolij Gustschin out_dev:
459912458ea0SAnatolij Gustschin 	/* User will not be able to enable h/w RAID-6 */
460012458ea0SAnatolij Gustschin 	pr_err("%s: failed to create RAID-6 driver interface\n",
460112458ea0SAnatolij Gustschin 		__func__);
460200006124SGrant Likely 	platform_driver_unregister(&ppc440spe_adma_driver);
460312458ea0SAnatolij Gustschin out_reg:
460412458ea0SAnatolij Gustschin 	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
460512458ea0SAnatolij Gustschin 	kfree(ppc440spe_dma_fifo_buf);
460612458ea0SAnatolij Gustschin 	return ret;
460712458ea0SAnatolij Gustschin }
460812458ea0SAnatolij Gustschin 
ppc440spe_adma_exit(void)460912458ea0SAnatolij Gustschin static void __exit ppc440spe_adma_exit(void)
461012458ea0SAnatolij Gustschin {
461112458ea0SAnatolij Gustschin 	driver_remove_file(&ppc440spe_adma_driver.driver,
461212458ea0SAnatolij Gustschin 			   &driver_attr_poly);
461312458ea0SAnatolij Gustschin 	driver_remove_file(&ppc440spe_adma_driver.driver,
461412458ea0SAnatolij Gustschin 			   &driver_attr_enable);
461512458ea0SAnatolij Gustschin 	driver_remove_file(&ppc440spe_adma_driver.driver,
461612458ea0SAnatolij Gustschin 			   &driver_attr_devices);
461700006124SGrant Likely 	platform_driver_unregister(&ppc440spe_adma_driver);
461812458ea0SAnatolij Gustschin 	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
461912458ea0SAnatolij Gustschin 	kfree(ppc440spe_dma_fifo_buf);
462012458ea0SAnatolij Gustschin }
462112458ea0SAnatolij Gustschin 
462212458ea0SAnatolij Gustschin arch_initcall(ppc440spe_adma_init);
462312458ea0SAnatolij Gustschin module_exit(ppc440spe_adma_exit);
462412458ea0SAnatolij Gustschin 
462512458ea0SAnatolij Gustschin MODULE_AUTHOR("Yuri Tikhonov <yur@emcraft.com>");
462612458ea0SAnatolij Gustschin MODULE_DESCRIPTION("PPC440SPE ADMA Engine Driver");
462712458ea0SAnatolij Gustschin MODULE_LICENSE("GPL");
4628