18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2009 DENX Software Engineering. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Yuri Tikhonov <yur@emcraft.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Further porting to arch/powerpc by 88c2ecf20Sopenharmony_ci * Anatolij Gustschin <agust@denx.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * This driver supports the asynchrounous DMA copy and RAID engines available 138c2ecf20Sopenharmony_ci * on the AMCC PPC440SPe Processors. 148c2ecf20Sopenharmony_ci * Based on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x) 158c2ecf20Sopenharmony_ci * ADMA driver written by D.Williams. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/async_tx.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 238c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 288c2ecf20Sopenharmony_ci#include <linux/of.h> 298c2ecf20Sopenharmony_ci#include <linux/of_address.h> 308c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 318c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 328c2ecf20Sopenharmony_ci#include <asm/dcr.h> 338c2ecf20Sopenharmony_ci#include <asm/dcr-regs.h> 348c2ecf20Sopenharmony_ci#include "adma.h" 358c2ecf20Sopenharmony_ci#include "../dmaengine.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cienum ppc_adma_init_code { 388c2ecf20Sopenharmony_ci PPC_ADMA_INIT_OK = 0, 398c2ecf20Sopenharmony_ci PPC_ADMA_INIT_MEMRES, 408c2ecf20Sopenharmony_ci PPC_ADMA_INIT_MEMREG, 418c2ecf20Sopenharmony_ci PPC_ADMA_INIT_ALLOC, 428c2ecf20Sopenharmony_ci PPC_ADMA_INIT_COHERENT, 438c2ecf20Sopenharmony_ci PPC_ADMA_INIT_CHANNEL, 448c2ecf20Sopenharmony_ci PPC_ADMA_INIT_IRQ1, 458c2ecf20Sopenharmony_ci PPC_ADMA_INIT_IRQ2, 468c2ecf20Sopenharmony_ci PPC_ADMA_INIT_REGISTER 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic char *ppc_adma_errors[] = { 508c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_OK] = "ok", 518c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_MEMRES] = "failed to get memory resource", 528c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_MEMREG] = "failed to request memory region", 538c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev " 548c2ecf20Sopenharmony_ci "structure", 558c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for " 568c2ecf20Sopenharmony_ci "hardware descriptors", 578c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel", 588c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_IRQ1] = "failed to request first irq", 598c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_IRQ2] = "failed to request second irq", 608c2ecf20Sopenharmony_ci [PPC_ADMA_INIT_REGISTER] = "failed to register dma async device", 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic enum ppc_adma_init_code 648c2ecf20Sopenharmony_cippc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM]; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct ppc_dma_chan_ref { 678c2ecf20Sopenharmony_ci struct dma_chan *chan; 688c2ecf20Sopenharmony_ci struct list_head node; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* The list of channels exported by ppc440spe ADMA */ 728c2ecf20Sopenharmony_cistruct list_head 738c2ecf20Sopenharmony_cippc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* This flag is set when want to refetch the xor chain in the interrupt 768c2ecf20Sopenharmony_ci * handler 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic u32 do_xor_refetch; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Pointer to DMA0, DMA1 CP/CS FIFO */ 818c2ecf20Sopenharmony_cistatic void *ppc440spe_dma_fifo_buf; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* Pointers to last submitted to DMA0, DMA1 CDBs */ 848c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *chan_last_sub[3]; 858c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *chan_first_cdb[3]; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Pointer to last linked and submitted xor CB */ 888c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *xor_last_linked; 898c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *xor_last_submit; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* This array is used in data-check operations for storing a pattern */ 928c2ecf20Sopenharmony_cistatic char ppc440spe_qword[16]; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic atomic_t ppc440spe_adma_err_irq_ref; 958c2ecf20Sopenharmony_cistatic dcr_host_t ppc440spe_mq_dcr_host; 968c2ecf20Sopenharmony_cistatic unsigned int ppc440spe_mq_dcr_len; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Since RXOR operations use the common register (MQ0_CF2H) for setting-up 998c2ecf20Sopenharmony_ci * the block size in transactions, then we do not allow to activate more than 1008c2ecf20Sopenharmony_ci * only one RXOR transactions simultaneously. So use this var to store 1018c2ecf20Sopenharmony_ci * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is 1028c2ecf20Sopenharmony_ci * set) or not (PPC440SPE_RXOR_RUN is clear). 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic unsigned long ppc440spe_rxor_state; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* These are used in enable & check routines 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic u32 ppc440spe_r6_enabled; 1098c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_chan *ppc440spe_r6_tchan; 1108c2ecf20Sopenharmony_cistatic struct completion ppc440spe_r6_test_comp; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int ppc440spe_adma_dma2rxor_prep_src( 1138c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 1148c2ecf20Sopenharmony_ci struct ppc440spe_rxor *cursor, int index, 1158c2ecf20Sopenharmony_ci int src_cnt, u32 addr); 1168c2ecf20Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_src( 1178c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 1188c2ecf20Sopenharmony_ci int index, dma_addr_t addr); 1198c2ecf20Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_mult( 1208c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 1218c2ecf20Sopenharmony_ci int index, u8 mult); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#ifdef ADMA_LL_DEBUG 1248c2ecf20Sopenharmony_ci#define ADMA_LL_DBG(x) ({ if (1) x; 0; }) 1258c2ecf20Sopenharmony_ci#else 1268c2ecf20Sopenharmony_ci#define ADMA_LL_DBG(x) ({ if (0) x; 0; }) 1278c2ecf20Sopenharmony_ci#endif 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void print_cb(struct ppc440spe_adma_chan *chan, void *block) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct dma_cdb *cdb; 1328c2ecf20Sopenharmony_ci struct xor_cb *cb; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (chan->device->id) { 1368c2ecf20Sopenharmony_ci case 0: 1378c2ecf20Sopenharmony_ci case 1: 1388c2ecf20Sopenharmony_ci cdb = block; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("CDB at %p [%d]:\n" 1418c2ecf20Sopenharmony_ci "\t attr 0x%02x opc 0x%02x cnt 0x%08x\n" 1428c2ecf20Sopenharmony_ci "\t sg1u 0x%08x sg1l 0x%08x\n" 1438c2ecf20Sopenharmony_ci "\t sg2u 0x%08x sg2l 0x%08x\n" 1448c2ecf20Sopenharmony_ci "\t sg3u 0x%08x sg3l 0x%08x\n", 1458c2ecf20Sopenharmony_ci cdb, chan->device->id, 1468c2ecf20Sopenharmony_ci cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt), 1478c2ecf20Sopenharmony_ci le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l), 1488c2ecf20Sopenharmony_ci le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l), 1498c2ecf20Sopenharmony_ci le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l) 1508c2ecf20Sopenharmony_ci ); 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci case 2: 1538c2ecf20Sopenharmony_ci cb = block; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pr_debug("CB at %p [%d]:\n" 1568c2ecf20Sopenharmony_ci "\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n" 1578c2ecf20Sopenharmony_ci "\t cbtah 0x%08x cbtal 0x%08x\n" 1588c2ecf20Sopenharmony_ci "\t cblah 0x%08x cblal 0x%08x\n", 1598c2ecf20Sopenharmony_ci cb, chan->device->id, 1608c2ecf20Sopenharmony_ci cb->cbc, cb->cbbc, cb->cbs, 1618c2ecf20Sopenharmony_ci cb->cbtah, cb->cbtal, 1628c2ecf20Sopenharmony_ci cb->cblah, cb->cblal); 1638c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 1648c2ecf20Sopenharmony_ci if (i && !cb->ops[i].h && !cb->ops[i].l) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n", 1678c2ecf20Sopenharmony_ci i, cb->ops[i].h, cb->ops[i].l); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void print_cb_list(struct ppc440spe_adma_chan *chan, 1748c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci for (; iter; iter = iter->hw_next) 1778c2ecf20Sopenharmony_ci print_cb(chan, iter->hw_desc); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src, 1818c2ecf20Sopenharmony_ci unsigned int src_cnt) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pr_debug("\n%s(%d):\nsrc: ", __func__, id); 1868c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) 1878c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx ", src[i]); 1888c2ecf20Sopenharmony_ci pr_debug("dst:\n\t0x%016llx\n", dst); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src, 1928c2ecf20Sopenharmony_ci unsigned int src_cnt) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int i; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci pr_debug("\n%s(%d):\nsrc: ", __func__, id); 1978c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) 1988c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx ", src[i]); 1998c2ecf20Sopenharmony_ci pr_debug("dst: "); 2008c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 2018c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx ", dst[i]); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src, 2058c2ecf20Sopenharmony_ci unsigned int src_cnt, 2068c2ecf20Sopenharmony_ci const unsigned char *scf) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci pr_debug("\n%s(%d):\nsrc(coef): ", __func__, id); 2118c2ecf20Sopenharmony_ci if (scf) { 2128c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) 2138c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx(0x%02x) ", src[i], scf[i]); 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) 2168c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx(no) ", src[i]); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pr_debug("dst: "); 2208c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 2218c2ecf20Sopenharmony_ci pr_debug("\t0x%016llx ", src[src_cnt + i]); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/****************************************************************************** 2258c2ecf20Sopenharmony_ci * Command (Descriptor) Blocks low-level routines 2268c2ecf20Sopenharmony_ci ******************************************************************************/ 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT 2298c2ecf20Sopenharmony_ci * pseudo operation 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot *desc, 2328c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct xor_cb *p; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci switch (chan->device->id) { 2378c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 2388c2ecf20Sopenharmony_ci p = desc->hw_desc; 2398c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct xor_cb)); 2408c2ecf20Sopenharmony_ci /* NOP with Command Block Complete Enable */ 2418c2ecf20Sopenharmony_ci p->cbc = XOR_CBCR_CBCE_BIT; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 2448c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 2458c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct dma_cdb)); 2468c2ecf20Sopenharmony_ci /* NOP with interrupt */ 2478c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &desc->flags); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id, 2518c2ecf20Sopenharmony_ci __func__); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR 2588c2ecf20Sopenharmony_ci * pseudo operation 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct xor_cb)); 2638c2ecf20Sopenharmony_ci desc->hw_next = NULL; 2648c2ecf20Sopenharmony_ci desc->src_cnt = 0; 2658c2ecf20Sopenharmony_ci desc->dst_cnt = 1; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc, 2728c2ecf20Sopenharmony_ci int src_cnt, unsigned long flags) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct xor_cb *hw_desc = desc->hw_desc; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct xor_cb)); 2778c2ecf20Sopenharmony_ci desc->hw_next = NULL; 2788c2ecf20Sopenharmony_ci desc->src_cnt = src_cnt; 2798c2ecf20Sopenharmony_ci desc->dst_cnt = 1; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt; 2828c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 2838c2ecf20Sopenharmony_ci /* Enable interrupt on completion */ 2848c2ecf20Sopenharmony_ci hw_desc->cbc |= XOR_CBCR_CBCE_BIT; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ 2898c2ecf20Sopenharmony_ci * operation in DMA2 controller 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc, 2928c2ecf20Sopenharmony_ci int dst_cnt, int src_cnt, unsigned long flags) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct xor_cb *hw_desc = desc->hw_desc; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct xor_cb)); 2978c2ecf20Sopenharmony_ci desc->hw_next = NULL; 2988c2ecf20Sopenharmony_ci desc->src_cnt = src_cnt; 2998c2ecf20Sopenharmony_ci desc->dst_cnt = dst_cnt; 3008c2ecf20Sopenharmony_ci memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags)); 3018c2ecf20Sopenharmony_ci desc->descs_per_op = 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci hw_desc->cbc = XOR_CBCR_TGT_BIT; 3048c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 3058c2ecf20Sopenharmony_ci /* Enable interrupt on completion */ 3068c2ecf20Sopenharmony_ci hw_desc->cbc |= XOR_CBCR_CBCE_BIT; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#define DMA_CTRL_FLAGS_LAST DMA_PREP_FENCE 3108c2ecf20Sopenharmony_ci#define DMA_PREP_ZERO_P (DMA_CTRL_FLAGS_LAST << 1) 3118c2ecf20Sopenharmony_ci#define DMA_PREP_ZERO_Q (DMA_PREP_ZERO_P << 1) 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/** 3148c2ecf20Sopenharmony_ci * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation 3158c2ecf20Sopenharmony_ci * with DMA0/1 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc, 3188c2ecf20Sopenharmony_ci int dst_cnt, int src_cnt, unsigned long flags, 3198c2ecf20Sopenharmony_ci unsigned long op) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 3228c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 3238c2ecf20Sopenharmony_ci u8 dopc; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Common initialization of a PQ descriptors chain */ 3268c2ecf20Sopenharmony_ci set_bits(op, &desc->flags); 3278c2ecf20Sopenharmony_ci desc->src_cnt = src_cnt; 3288c2ecf20Sopenharmony_ci desc->dst_cnt = dst_cnt; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* WXOR MULTICAST if both P and Q are being computed 3318c2ecf20Sopenharmony_ci * MV_SG1_SG2 if Q only 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ? 3348c2ecf20Sopenharmony_ci DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci list_for_each_entry(iter, &desc->group_list, chain_node) { 3378c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 3388c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (likely(!list_is_last(&iter->chain_node, 3418c2ecf20Sopenharmony_ci &desc->group_list))) { 3428c2ecf20Sopenharmony_ci /* set 'next' pointer */ 3438c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 3448c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 3458c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci /* this is the last descriptor. 3488c2ecf20Sopenharmony_ci * this slot will be pasted from ADMA level 3498c2ecf20Sopenharmony_ci * each time it wants to configure parameters 3508c2ecf20Sopenharmony_ci * of the transaction (src, dst, ...) 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci iter->hw_next = NULL; 3538c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 3548c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &iter->flags); 3558c2ecf20Sopenharmony_ci else 3568c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Set OPS depending on WXOR/RXOR type of operation */ 3618c2ecf20Sopenharmony_ci if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) { 3628c2ecf20Sopenharmony_ci /* This is a WXOR only chain: 3638c2ecf20Sopenharmony_ci * - first descriptors are for zeroing destinations 3648c2ecf20Sopenharmony_ci * if PPC440SPE_ZERO_P/Q set; 3658c2ecf20Sopenharmony_ci * - descriptors remained are for GF-XOR operations. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci iter = list_first_entry(&desc->group_list, 3688c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 3698c2ecf20Sopenharmony_ci chain_node); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) { 3728c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 3738c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 3748c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 3758c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 3768c2ecf20Sopenharmony_ci chain_node); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) { 3808c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 3818c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 3828c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 3838c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 3848c2ecf20Sopenharmony_ci chain_node); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &desc->group_list, chain_node) { 3888c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 3898c2ecf20Sopenharmony_ci hw_desc->opc = dopc; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci /* This is either RXOR-only or mixed RXOR/WXOR */ 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* The first 1 or 2 slots in chain are always RXOR, 3958c2ecf20Sopenharmony_ci * if need to calculate P & Q, then there are two 3968c2ecf20Sopenharmony_ci * RXOR slots; if only P or only Q, then there is one 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci iter = list_first_entry(&desc->group_list, 3998c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 4008c2ecf20Sopenharmony_ci chain_node); 4018c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 4028c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (desc->dst_cnt == DMA_DEST_MAX_NUM) { 4058c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 4068c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 4078c2ecf20Sopenharmony_ci chain_node); 4088c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 4098c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* The remaining descs (if any) are WXORs */ 4138c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) { 4148c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 4158c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 4168c2ecf20Sopenharmony_ci chain_node); 4178c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &desc->group_list, 4188c2ecf20Sopenharmony_ci chain_node) { 4198c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 4208c2ecf20Sopenharmony_ci hw_desc->opc = dopc; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/** 4278c2ecf20Sopenharmony_ci * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor 4288c2ecf20Sopenharmony_ci * for PQ_ZERO_SUM operation 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_dma01pqzero_sum( 4318c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 4328c2ecf20Sopenharmony_ci int dst_cnt, int src_cnt) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 4358c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 4368c2ecf20Sopenharmony_ci int i = 0; 4378c2ecf20Sopenharmony_ci u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST : 4388c2ecf20Sopenharmony_ci DMA_CDB_OPC_MV_SG1_SG2; 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * Initialize starting from 2nd or 3rd descriptor dependent 4418c2ecf20Sopenharmony_ci * on dst_cnt. First one or two slots are for cloning P 4428c2ecf20Sopenharmony_ci * and/or Q to chan->pdest and/or chan->qdest as we have 4438c2ecf20Sopenharmony_ci * to preserve original P/Q. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci iter = list_first_entry(&desc->group_list, 4468c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 4478c2ecf20Sopenharmony_ci iter = list_entry(iter->chain_node.next, 4488c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (dst_cnt > 1) { 4518c2ecf20Sopenharmony_ci iter = list_entry(iter->chain_node.next, 4528c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci /* initialize each source descriptor in chain */ 4558c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &desc->group_list, chain_node) { 4568c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 4578c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 4588c2ecf20Sopenharmony_ci iter->src_cnt = 0; 4598c2ecf20Sopenharmony_ci iter->dst_cnt = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* This is a ZERO_SUM operation: 4628c2ecf20Sopenharmony_ci * - <src_cnt> descriptors starting from 2nd or 3rd 4638c2ecf20Sopenharmony_ci * descriptor are for GF-XOR operations; 4648c2ecf20Sopenharmony_ci * - remaining <dst_cnt> descriptors are for checking the result 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci if (i++ < src_cnt) 4678c2ecf20Sopenharmony_ci /* MV_SG1_SG2 if only Q is being verified 4688c2ecf20Sopenharmony_ci * MULTICAST if both P and Q are being verified 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci hw_desc->opc = dopc; 4718c2ecf20Sopenharmony_ci else 4728c2ecf20Sopenharmony_ci /* DMA_CDB_OPC_DCHECK128 operation */ 4738c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_DCHECK128; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (likely(!list_is_last(&iter->chain_node, 4768c2ecf20Sopenharmony_ci &desc->group_list))) { 4778c2ecf20Sopenharmony_ci /* set 'next' pointer */ 4788c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 4798c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 4808c2ecf20Sopenharmony_ci chain_node); 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci /* this is the last descriptor. 4838c2ecf20Sopenharmony_ci * this slot will be pasted from ADMA level 4848c2ecf20Sopenharmony_ci * each time it wants to configure parameters 4858c2ecf20Sopenharmony_ci * of the transaction (src, dst, ...) 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci iter->hw_next = NULL; 4888c2ecf20Sopenharmony_ci /* always enable interrupt generation since we get 4898c2ecf20Sopenharmony_ci * the status of pqzero from the handler 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &iter->flags); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci desc->src_cnt = src_cnt; 4958c2ecf20Sopenharmony_ci desc->dst_cnt = dst_cnt; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_cistatic void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc, 5028c2ecf20Sopenharmony_ci unsigned long flags) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc = desc->hw_desc; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci memset(desc->hw_desc, 0, sizeof(struct dma_cdb)); 5078c2ecf20Sopenharmony_ci desc->hw_next = NULL; 5088c2ecf20Sopenharmony_ci desc->src_cnt = 1; 5098c2ecf20Sopenharmony_ci desc->dst_cnt = 1; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 5128c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &desc->flags); 5138c2ecf20Sopenharmony_ci else 5148c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &desc->flags); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/** 5208c2ecf20Sopenharmony_ci * ppc440spe_desc_set_src_addr - set source address into the descriptor 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc, 5238c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 5248c2ecf20Sopenharmony_ci int src_idx, dma_addr_t addrh, 5258c2ecf20Sopenharmony_ci dma_addr_t addrl) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct dma_cdb *dma_hw_desc; 5288c2ecf20Sopenharmony_ci struct xor_cb *xor_hw_desc; 5298c2ecf20Sopenharmony_ci phys_addr_t addr64, tmplow, tmphi; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci switch (chan->device->id) { 5328c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 5338c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 5348c2ecf20Sopenharmony_ci if (!addrh) { 5358c2ecf20Sopenharmony_ci addr64 = addrl; 5368c2ecf20Sopenharmony_ci tmphi = (addr64 >> 32); 5378c2ecf20Sopenharmony_ci tmplow = (addr64 & 0xFFFFFFFF); 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci tmphi = addrh; 5408c2ecf20Sopenharmony_ci tmplow = addrl; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci dma_hw_desc = desc->hw_desc; 5438c2ecf20Sopenharmony_ci dma_hw_desc->sg1l = cpu_to_le32((u32)tmplow); 5448c2ecf20Sopenharmony_ci dma_hw_desc->sg1u |= cpu_to_le32((u32)tmphi); 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 5478c2ecf20Sopenharmony_ci xor_hw_desc = desc->hw_desc; 5488c2ecf20Sopenharmony_ci xor_hw_desc->ops[src_idx].l = addrl; 5498c2ecf20Sopenharmony_ci xor_hw_desc->ops[src_idx].h |= addrh; 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/** 5558c2ecf20Sopenharmony_ci * ppc440spe_desc_set_src_mult - set source address mult into the descriptor 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc, 5588c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, u32 mult_index, 5598c2ecf20Sopenharmony_ci int sg_index, unsigned char mult_value) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct dma_cdb *dma_hw_desc; 5628c2ecf20Sopenharmony_ci struct xor_cb *xor_hw_desc; 5638c2ecf20Sopenharmony_ci u32 *psgu; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci switch (chan->device->id) { 5668c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 5678c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 5688c2ecf20Sopenharmony_ci dma_hw_desc = desc->hw_desc; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci switch (sg_index) { 5718c2ecf20Sopenharmony_ci /* for RXOR operations set multiplier 5728c2ecf20Sopenharmony_ci * into source cued address 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci case DMA_CDB_SG_SRC: 5758c2ecf20Sopenharmony_ci psgu = &dma_hw_desc->sg1u; 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci /* for WXOR operations set multiplier 5788c2ecf20Sopenharmony_ci * into destination cued address(es) 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci case DMA_CDB_SG_DST1: 5818c2ecf20Sopenharmony_ci psgu = &dma_hw_desc->sg2u; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case DMA_CDB_SG_DST2: 5848c2ecf20Sopenharmony_ci psgu = &dma_hw_desc->sg3u; 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci default: 5878c2ecf20Sopenharmony_ci BUG(); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci *psgu |= cpu_to_le32(mult_value << mult_index); 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 5938c2ecf20Sopenharmony_ci xor_hw_desc = desc->hw_desc; 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci default: 5968c2ecf20Sopenharmony_ci BUG(); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/** 6018c2ecf20Sopenharmony_ci * ppc440spe_desc_set_dest_addr - set destination address into the descriptor 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc, 6048c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 6058c2ecf20Sopenharmony_ci dma_addr_t addrh, dma_addr_t addrl, 6068c2ecf20Sopenharmony_ci u32 dst_idx) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct dma_cdb *dma_hw_desc; 6098c2ecf20Sopenharmony_ci struct xor_cb *xor_hw_desc; 6108c2ecf20Sopenharmony_ci phys_addr_t addr64, tmphi, tmplow; 6118c2ecf20Sopenharmony_ci u32 *psgu, *psgl; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci switch (chan->device->id) { 6148c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 6158c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 6168c2ecf20Sopenharmony_ci if (!addrh) { 6178c2ecf20Sopenharmony_ci addr64 = addrl; 6188c2ecf20Sopenharmony_ci tmphi = (addr64 >> 32); 6198c2ecf20Sopenharmony_ci tmplow = (addr64 & 0xFFFFFFFF); 6208c2ecf20Sopenharmony_ci } else { 6218c2ecf20Sopenharmony_ci tmphi = addrh; 6228c2ecf20Sopenharmony_ci tmplow = addrl; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci dma_hw_desc = desc->hw_desc; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u; 6278c2ecf20Sopenharmony_ci psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci *psgl = cpu_to_le32((u32)tmplow); 6308c2ecf20Sopenharmony_ci *psgu |= cpu_to_le32((u32)tmphi); 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 6338c2ecf20Sopenharmony_ci xor_hw_desc = desc->hw_desc; 6348c2ecf20Sopenharmony_ci xor_hw_desc->cbtal = addrl; 6358c2ecf20Sopenharmony_ci xor_hw_desc->cbtah |= addrh; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/** 6418c2ecf20Sopenharmony_ci * ppc440spe_desc_set_byte_count - set number of data bytes involved 6428c2ecf20Sopenharmony_ci * into the operation 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot *desc, 6458c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 6468c2ecf20Sopenharmony_ci u32 byte_count) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct dma_cdb *dma_hw_desc; 6498c2ecf20Sopenharmony_ci struct xor_cb *xor_hw_desc; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci switch (chan->device->id) { 6528c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 6538c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 6548c2ecf20Sopenharmony_ci dma_hw_desc = desc->hw_desc; 6558c2ecf20Sopenharmony_ci dma_hw_desc->cnt = cpu_to_le32(byte_count); 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 6588c2ecf20Sopenharmony_ci xor_hw_desc = desc->hw_desc; 6598c2ecf20Sopenharmony_ci xor_hw_desc->cbbc = byte_count; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/** 6658c2ecf20Sopenharmony_ci * ppc440spe_desc_set_rxor_block_size - set RXOR block size 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_cistatic inline void ppc440spe_desc_set_rxor_block_size(u32 byte_count) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci /* assume that byte_count is aligned on the 512-boundary; 6708c2ecf20Sopenharmony_ci * thus write it directly to the register (bits 23:31 are 6718c2ecf20Sopenharmony_ci * reserved there). 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/** 6778c2ecf20Sopenharmony_ci * ppc440spe_desc_set_dcheck - set CHECK pattern 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc, 6808c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, u8 *qword) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct dma_cdb *dma_hw_desc; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci switch (chan->device->id) { 6858c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 6868c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 6878c2ecf20Sopenharmony_ci dma_hw_desc = desc->hw_desc; 6888c2ecf20Sopenharmony_ci iowrite32(qword[0], &dma_hw_desc->sg3l); 6898c2ecf20Sopenharmony_ci iowrite32(qword[4], &dma_hw_desc->sg3u); 6908c2ecf20Sopenharmony_ci iowrite32(qword[8], &dma_hw_desc->sg2l); 6918c2ecf20Sopenharmony_ci iowrite32(qword[12], &dma_hw_desc->sg2u); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci default: 6948c2ecf20Sopenharmony_ci BUG(); 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/** 6998c2ecf20Sopenharmony_ci * ppc440spe_xor_set_link - set link address in xor CB 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_cistatic void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc, 7028c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *next_desc) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct xor_cb *xor_hw_desc = prev_desc->hw_desc; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (unlikely(!next_desc || !(next_desc->phys))) { 7078c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n", 7088c2ecf20Sopenharmony_ci __func__, next_desc, 7098c2ecf20Sopenharmony_ci next_desc ? next_desc->phys : 0); 7108c2ecf20Sopenharmony_ci BUG(); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci xor_hw_desc->cbs = 0; 7148c2ecf20Sopenharmony_ci xor_hw_desc->cblal = next_desc->phys; 7158c2ecf20Sopenharmony_ci xor_hw_desc->cblah = 0; 7168c2ecf20Sopenharmony_ci xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci/** 7208c2ecf20Sopenharmony_ci * ppc440spe_desc_set_link - set the address of descriptor following this 7218c2ecf20Sopenharmony_ci * descriptor in chain 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_cistatic void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan, 7248c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *prev_desc, 7258c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *next_desc) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci unsigned long flags; 7288c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *tail = next_desc; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (unlikely(!prev_desc || !next_desc || 7318c2ecf20Sopenharmony_ci (prev_desc->hw_next && prev_desc->hw_next != next_desc))) { 7328c2ecf20Sopenharmony_ci /* If previous next is overwritten something is wrong. 7338c2ecf20Sopenharmony_ci * though we may refetch from append to initiate list 7348c2ecf20Sopenharmony_ci * processing; in this case - it's ok. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; " 7378c2ecf20Sopenharmony_ci "prev->hw_next=0x%p\n", __func__, prev_desc, 7388c2ecf20Sopenharmony_ci next_desc, prev_desc ? prev_desc->hw_next : 0); 7398c2ecf20Sopenharmony_ci BUG(); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci local_irq_save(flags); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* do s/w chaining both for DMA and XOR descriptors */ 7458c2ecf20Sopenharmony_ci prev_desc->hw_next = next_desc; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci switch (chan->device->id) { 7488c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 7498c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 7528c2ecf20Sopenharmony_ci /* bind descriptor to the chain */ 7538c2ecf20Sopenharmony_ci while (tail->hw_next) 7548c2ecf20Sopenharmony_ci tail = tail->hw_next; 7558c2ecf20Sopenharmony_ci xor_last_linked = tail; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (prev_desc == xor_last_submit) 7588c2ecf20Sopenharmony_ci /* do not link to the last submitted CB */ 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci ppc440spe_xor_set_link(prev_desc, next_desc); 7618c2ecf20Sopenharmony_ci break; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci local_irq_restore(flags); 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/** 7688c2ecf20Sopenharmony_ci * ppc440spe_desc_get_link - get the address of the descriptor that 7698c2ecf20Sopenharmony_ci * follows this one 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cistatic inline u32 ppc440spe_desc_get_link(struct ppc440spe_adma_desc_slot *desc, 7728c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci if (!desc->hw_next) 7758c2ecf20Sopenharmony_ci return 0; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return desc->hw_next->phys; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/** 7818c2ecf20Sopenharmony_ci * ppc440spe_desc_is_aligned - check alignment 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_cistatic inline int ppc440spe_desc_is_aligned( 7848c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, int num_slots) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci return (desc->idx & (num_slots - 1)) ? 0 : 1; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * ppc440spe_chan_xor_slot_count - get the number of slots necessary for 7918c2ecf20Sopenharmony_ci * XOR operation 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_cistatic int ppc440spe_chan_xor_slot_count(size_t len, int src_cnt, 7948c2ecf20Sopenharmony_ci int *slots_per_op) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci int slot_cnt; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* each XOR descriptor provides up to 16 source operands */ 7998c2ecf20Sopenharmony_ci slot_cnt = *slots_per_op = (src_cnt + XOR_MAX_OPS - 1)/XOR_MAX_OPS; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (likely(len <= PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT)) 8028c2ecf20Sopenharmony_ci return slot_cnt; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: len %d > max %d !!\n", 8058c2ecf20Sopenharmony_ci __func__, len, PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT); 8068c2ecf20Sopenharmony_ci BUG(); 8078c2ecf20Sopenharmony_ci return slot_cnt; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/** 8118c2ecf20Sopenharmony_ci * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for 8128c2ecf20Sopenharmony_ci * DMA2 PQ operation 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_cistatic int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs, 8158c2ecf20Sopenharmony_ci int src_cnt, size_t len) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci signed long long order = 0; 8188c2ecf20Sopenharmony_ci int state = 0; 8198c2ecf20Sopenharmony_ci int addr_count = 0; 8208c2ecf20Sopenharmony_ci int i; 8218c2ecf20Sopenharmony_ci for (i = 1; i < src_cnt; i++) { 8228c2ecf20Sopenharmony_ci dma_addr_t cur_addr = srcs[i]; 8238c2ecf20Sopenharmony_ci dma_addr_t old_addr = srcs[i-1]; 8248c2ecf20Sopenharmony_ci switch (state) { 8258c2ecf20Sopenharmony_ci case 0: 8268c2ecf20Sopenharmony_ci if (cur_addr == old_addr + len) { 8278c2ecf20Sopenharmony_ci /* direct RXOR */ 8288c2ecf20Sopenharmony_ci order = 1; 8298c2ecf20Sopenharmony_ci state = 1; 8308c2ecf20Sopenharmony_ci if (i == src_cnt-1) 8318c2ecf20Sopenharmony_ci addr_count++; 8328c2ecf20Sopenharmony_ci } else if (old_addr == cur_addr + len) { 8338c2ecf20Sopenharmony_ci /* reverse RXOR */ 8348c2ecf20Sopenharmony_ci order = -1; 8358c2ecf20Sopenharmony_ci state = 1; 8368c2ecf20Sopenharmony_ci if (i == src_cnt-1) 8378c2ecf20Sopenharmony_ci addr_count++; 8388c2ecf20Sopenharmony_ci } else { 8398c2ecf20Sopenharmony_ci state = 3; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci break; 8428c2ecf20Sopenharmony_ci case 1: 8438c2ecf20Sopenharmony_ci if (i == src_cnt-2 || (order == -1 8448c2ecf20Sopenharmony_ci && cur_addr != old_addr - len)) { 8458c2ecf20Sopenharmony_ci order = 0; 8468c2ecf20Sopenharmony_ci state = 0; 8478c2ecf20Sopenharmony_ci addr_count++; 8488c2ecf20Sopenharmony_ci } else if (cur_addr == old_addr + len*order) { 8498c2ecf20Sopenharmony_ci state = 2; 8508c2ecf20Sopenharmony_ci if (i == src_cnt-1) 8518c2ecf20Sopenharmony_ci addr_count++; 8528c2ecf20Sopenharmony_ci } else if (cur_addr == old_addr + 2*len) { 8538c2ecf20Sopenharmony_ci state = 2; 8548c2ecf20Sopenharmony_ci if (i == src_cnt-1) 8558c2ecf20Sopenharmony_ci addr_count++; 8568c2ecf20Sopenharmony_ci } else if (cur_addr == old_addr + 3*len) { 8578c2ecf20Sopenharmony_ci state = 2; 8588c2ecf20Sopenharmony_ci if (i == src_cnt-1) 8598c2ecf20Sopenharmony_ci addr_count++; 8608c2ecf20Sopenharmony_ci } else { 8618c2ecf20Sopenharmony_ci order = 0; 8628c2ecf20Sopenharmony_ci state = 0; 8638c2ecf20Sopenharmony_ci addr_count++; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci case 2: 8678c2ecf20Sopenharmony_ci order = 0; 8688c2ecf20Sopenharmony_ci state = 0; 8698c2ecf20Sopenharmony_ci addr_count++; 8708c2ecf20Sopenharmony_ci break; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci if (state == 3) 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci if (src_cnt <= 1 || (state != 1 && state != 2)) { 8768c2ecf20Sopenharmony_ci pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n", 8778c2ecf20Sopenharmony_ci __func__, src_cnt, state, addr_count, order); 8788c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) 8798c2ecf20Sopenharmony_ci pr_err("\t[%d] 0x%llx \n", i, srcs[i]); 8808c2ecf20Sopenharmony_ci BUG(); 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci/****************************************************************************** 8888c2ecf20Sopenharmony_ci * ADMA channel low-level routines 8898c2ecf20Sopenharmony_ci ******************************************************************************/ 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic u32 8928c2ecf20Sopenharmony_cippc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan); 8938c2ecf20Sopenharmony_cistatic void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/** 8968c2ecf20Sopenharmony_ci * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_cistatic void ppc440spe_adma_device_clear_eot_status( 8998c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct dma_regs *dma_reg; 9028c2ecf20Sopenharmony_ci struct xor_regs *xor_reg; 9038c2ecf20Sopenharmony_ci u8 *p = chan->device->dma_desc_pool_virt; 9048c2ecf20Sopenharmony_ci struct dma_cdb *cdb; 9058c2ecf20Sopenharmony_ci u32 rv, i; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci switch (chan->device->id) { 9088c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 9098c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 9108c2ecf20Sopenharmony_ci /* read FIFO to ack */ 9118c2ecf20Sopenharmony_ci dma_reg = chan->device->dma_reg; 9128c2ecf20Sopenharmony_ci while ((rv = ioread32(&dma_reg->csfpl))) { 9138c2ecf20Sopenharmony_ci i = rv & DMA_CDB_ADDR_MSK; 9148c2ecf20Sopenharmony_ci cdb = (struct dma_cdb *)&p[i - 9158c2ecf20Sopenharmony_ci (u32)chan->device->dma_desc_pool]; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* Clear opcode to ack. This is necessary for 9188c2ecf20Sopenharmony_ci * ZeroSum operations only 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci cdb->opc = 0; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_RXOR_RUN, 9238c2ecf20Sopenharmony_ci &ppc440spe_rxor_state)) { 9248c2ecf20Sopenharmony_ci /* probably this is a completed RXOR op, 9258c2ecf20Sopenharmony_ci * get pointer to CDB using the fact that 9268c2ecf20Sopenharmony_ci * physical and virtual addresses of CDB 9278c2ecf20Sopenharmony_ci * in pools have the same offsets 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ci if (le32_to_cpu(cdb->sg1u) & 9308c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE) { 9318c2ecf20Sopenharmony_ci /* this is a RXOR */ 9328c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_RXOR_RUN, 9338c2ecf20Sopenharmony_ci &ppc440spe_rxor_state); 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (rv & DMA_CDB_STATUS_MSK) { 9388c2ecf20Sopenharmony_ci /* ZeroSum check failed 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 9418c2ecf20Sopenharmony_ci dma_addr_t phys = rv & ~DMA_CDB_MSK; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* 9448c2ecf20Sopenharmony_ci * Update the status of corresponding 9458c2ecf20Sopenharmony_ci * descriptor. 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_ci list_for_each_entry(iter, &chan->chain, 9488c2ecf20Sopenharmony_ci chain_node) { 9498c2ecf20Sopenharmony_ci if (iter->phys == phys) 9508c2ecf20Sopenharmony_ci break; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci /* 9538c2ecf20Sopenharmony_ci * if cannot find the corresponding 9548c2ecf20Sopenharmony_ci * slot it's a bug 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_ci BUG_ON(&iter->chain_node == &chan->chain); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (iter->xor_check_result) { 9598c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_PCHECK, 9608c2ecf20Sopenharmony_ci &iter->flags)) { 9618c2ecf20Sopenharmony_ci *iter->xor_check_result |= 9628c2ecf20Sopenharmony_ci SUM_CHECK_P_RESULT; 9638c2ecf20Sopenharmony_ci } else 9648c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_QCHECK, 9658c2ecf20Sopenharmony_ci &iter->flags)) { 9668c2ecf20Sopenharmony_ci *iter->xor_check_result |= 9678c2ecf20Sopenharmony_ci SUM_CHECK_Q_RESULT; 9688c2ecf20Sopenharmony_ci } else 9698c2ecf20Sopenharmony_ci BUG(); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci rv = ioread32(&dma_reg->dsts); 9758c2ecf20Sopenharmony_ci if (rv) { 9768c2ecf20Sopenharmony_ci pr_err("DMA%d err status: 0x%x\n", 9778c2ecf20Sopenharmony_ci chan->device->id, rv); 9788c2ecf20Sopenharmony_ci /* write back to clear */ 9798c2ecf20Sopenharmony_ci iowrite32(rv, &dma_reg->dsts); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci break; 9828c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 9838c2ecf20Sopenharmony_ci /* reset status bits to ack */ 9848c2ecf20Sopenharmony_ci xor_reg = chan->device->xor_reg; 9858c2ecf20Sopenharmony_ci rv = ioread32be(&xor_reg->sr); 9868c2ecf20Sopenharmony_ci iowrite32be(rv, &xor_reg->sr); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (rv & (XOR_IE_ICBIE_BIT|XOR_IE_ICIE_BIT|XOR_IE_RPTIE_BIT)) { 9898c2ecf20Sopenharmony_ci if (rv & XOR_IE_RPTIE_BIT) { 9908c2ecf20Sopenharmony_ci /* Read PLB Timeout Error. 9918c2ecf20Sopenharmony_ci * Try to resubmit the CB 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci u32 val = ioread32be(&xor_reg->ccbalr); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci iowrite32be(val, &xor_reg->cblalr); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci val = ioread32be(&xor_reg->crsr); 9988c2ecf20Sopenharmony_ci iowrite32be(val | XOR_CRSR_XAE_BIT, 9998c2ecf20Sopenharmony_ci &xor_reg->crsr); 10008c2ecf20Sopenharmony_ci } else 10018c2ecf20Sopenharmony_ci pr_err("XOR ERR 0x%x status\n", rv); 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* if the XORcore is idle, but there are unprocessed CBs 10068c2ecf20Sopenharmony_ci * then refetch the s/w chain here 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) && 10098c2ecf20Sopenharmony_ci do_xor_refetch) 10108c2ecf20Sopenharmony_ci ppc440spe_chan_append(chan); 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci/** 10168c2ecf20Sopenharmony_ci * ppc440spe_chan_is_busy - get the channel status 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_cistatic int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct dma_regs *dma_reg; 10218c2ecf20Sopenharmony_ci struct xor_regs *xor_reg; 10228c2ecf20Sopenharmony_ci int busy = 0; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci switch (chan->device->id) { 10258c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 10268c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 10278c2ecf20Sopenharmony_ci dma_reg = chan->device->dma_reg; 10288c2ecf20Sopenharmony_ci /* if command FIFO's head and tail pointers are equal and 10298c2ecf20Sopenharmony_ci * status tail is the same as command, then channel is free 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) || 10328c2ecf20Sopenharmony_ci ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp)) 10338c2ecf20Sopenharmony_ci busy = 1; 10348c2ecf20Sopenharmony_ci break; 10358c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 10368c2ecf20Sopenharmony_ci /* use the special status bit for the XORcore 10378c2ecf20Sopenharmony_ci */ 10388c2ecf20Sopenharmony_ci xor_reg = chan->device->xor_reg; 10398c2ecf20Sopenharmony_ci busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0; 10408c2ecf20Sopenharmony_ci break; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci return busy; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/** 10478c2ecf20Sopenharmony_ci * ppc440spe_chan_set_first_xor_descriptor - init XORcore chain 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_cistatic void ppc440spe_chan_set_first_xor_descriptor( 10508c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 10518c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *next_desc) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct xor_regs *xor_reg = chan->device->xor_reg; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) 10568c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Warn: XORcore is running " 10578c2ecf20Sopenharmony_ci "when try to set the first CDB!\n", 10588c2ecf20Sopenharmony_ci __func__); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci xor_last_submit = xor_last_linked = next_desc; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci iowrite32be(next_desc->phys, &xor_reg->cblalr); 10658c2ecf20Sopenharmony_ci iowrite32be(0, &xor_reg->cblahr); 10668c2ecf20Sopenharmony_ci iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT, 10678c2ecf20Sopenharmony_ci &xor_reg->cbcr); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci chan->hw_chain_inited = 1; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci/** 10738c2ecf20Sopenharmony_ci * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO. 10748c2ecf20Sopenharmony_ci * called with irqs disabled 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan, 10778c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci u32 pcdb; 10808c2ecf20Sopenharmony_ci struct dma_regs *dma_reg = chan->device->dma_reg; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci pcdb = desc->phys; 10838c2ecf20Sopenharmony_ci if (!test_bit(PPC440SPE_DESC_INT, &desc->flags)) 10848c2ecf20Sopenharmony_ci pcdb |= DMA_CDB_NO_INT; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci chan_last_sub[chan->device->id] = desc; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci ADMA_LL_DBG(print_cb(chan, desc->hw_desc)); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci iowrite32(pcdb, &dma_reg->cpfpl); 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci/** 10948c2ecf20Sopenharmony_ci * ppc440spe_chan_append - update the h/w chain in the channel 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_cistatic void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct xor_regs *xor_reg; 10998c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 11008c2ecf20Sopenharmony_ci struct xor_cb *xcb; 11018c2ecf20Sopenharmony_ci u32 cur_desc; 11028c2ecf20Sopenharmony_ci unsigned long flags; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci local_irq_save(flags); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci switch (chan->device->id) { 11078c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 11088c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 11098c2ecf20Sopenharmony_ci cur_desc = ppc440spe_chan_get_current_descriptor(chan); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (likely(cur_desc)) { 11128c2ecf20Sopenharmony_ci iter = chan_last_sub[chan->device->id]; 11138c2ecf20Sopenharmony_ci BUG_ON(!iter); 11148c2ecf20Sopenharmony_ci } else { 11158c2ecf20Sopenharmony_ci /* first peer */ 11168c2ecf20Sopenharmony_ci iter = chan_first_cdb[chan->device->id]; 11178c2ecf20Sopenharmony_ci BUG_ON(!iter); 11188c2ecf20Sopenharmony_ci ppc440spe_dma_put_desc(chan, iter); 11198c2ecf20Sopenharmony_ci chan->hw_chain_inited = 1; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* is there something new to append */ 11238c2ecf20Sopenharmony_ci if (!iter->hw_next) 11248c2ecf20Sopenharmony_ci break; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* flush descriptors from the s/w queue to fifo */ 11278c2ecf20Sopenharmony_ci list_for_each_entry_continue(iter, &chan->chain, chain_node) { 11288c2ecf20Sopenharmony_ci ppc440spe_dma_put_desc(chan, iter); 11298c2ecf20Sopenharmony_ci if (!iter->hw_next) 11308c2ecf20Sopenharmony_ci break; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci break; 11338c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 11348c2ecf20Sopenharmony_ci /* update h/w links and refetch */ 11358c2ecf20Sopenharmony_ci if (!xor_last_submit->hw_next) 11368c2ecf20Sopenharmony_ci break; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci xor_reg = chan->device->xor_reg; 11398c2ecf20Sopenharmony_ci /* the last linked CDB has to generate an interrupt 11408c2ecf20Sopenharmony_ci * that we'd be able to append the next lists to h/w 11418c2ecf20Sopenharmony_ci * regardless of the XOR engine state at the moment of 11428c2ecf20Sopenharmony_ci * appending of these next lists 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci xcb = xor_last_linked->hw_desc; 11458c2ecf20Sopenharmony_ci xcb->cbc |= XOR_CBCR_CBCE_BIT; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) { 11488c2ecf20Sopenharmony_ci /* XORcore is idle. Refetch now */ 11498c2ecf20Sopenharmony_ci do_xor_refetch = 0; 11508c2ecf20Sopenharmony_ci ppc440spe_xor_set_link(xor_last_submit, 11518c2ecf20Sopenharmony_ci xor_last_submit->hw_next); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ADMA_LL_DBG(print_cb_list(chan, 11548c2ecf20Sopenharmony_ci xor_last_submit->hw_next)); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci xor_last_submit = xor_last_linked; 11578c2ecf20Sopenharmony_ci iowrite32be(ioread32be(&xor_reg->crsr) | 11588c2ecf20Sopenharmony_ci XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT, 11598c2ecf20Sopenharmony_ci &xor_reg->crsr); 11608c2ecf20Sopenharmony_ci } else { 11618c2ecf20Sopenharmony_ci /* XORcore is running. Refetch later in the handler */ 11628c2ecf20Sopenharmony_ci do_xor_refetch = 1; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci break; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci local_irq_restore(flags); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/** 11728c2ecf20Sopenharmony_ci * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_cistatic u32 11758c2ecf20Sopenharmony_cippc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct dma_regs *dma_reg; 11788c2ecf20Sopenharmony_ci struct xor_regs *xor_reg; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (unlikely(!chan->hw_chain_inited)) 11818c2ecf20Sopenharmony_ci /* h/w descriptor chain is not initialized yet */ 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci switch (chan->device->id) { 11858c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 11868c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 11878c2ecf20Sopenharmony_ci dma_reg = chan->device->dma_reg; 11888c2ecf20Sopenharmony_ci return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK); 11898c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 11908c2ecf20Sopenharmony_ci xor_reg = chan->device->xor_reg; 11918c2ecf20Sopenharmony_ci return ioread32be(&xor_reg->ccbalr); 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci return 0; 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci/** 11978c2ecf20Sopenharmony_ci * ppc440spe_chan_run - enable the channel 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct xor_regs *xor_reg; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci switch (chan->device->id) { 12048c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 12058c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 12068c2ecf20Sopenharmony_ci /* DMAs are always enabled, do nothing */ 12078c2ecf20Sopenharmony_ci break; 12088c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 12098c2ecf20Sopenharmony_ci /* drain write buffer */ 12108c2ecf20Sopenharmony_ci xor_reg = chan->device->xor_reg; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* fetch descriptor pointed to in <link> */ 12138c2ecf20Sopenharmony_ci iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT, 12148c2ecf20Sopenharmony_ci &xor_reg->crsr); 12158c2ecf20Sopenharmony_ci break; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/****************************************************************************** 12208c2ecf20Sopenharmony_ci * ADMA device level 12218c2ecf20Sopenharmony_ci ******************************************************************************/ 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan); 12248c2ecf20Sopenharmony_cistatic int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic dma_cookie_t 12278c2ecf20Sopenharmony_cippc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *tx, 12308c2ecf20Sopenharmony_ci dma_addr_t addr, int index); 12318c2ecf20Sopenharmony_cistatic void 12328c2ecf20Sopenharmony_cippc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot *tx, 12338c2ecf20Sopenharmony_ci dma_addr_t addr, int index); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic void 12368c2ecf20Sopenharmony_cippc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *tx, 12378c2ecf20Sopenharmony_ci dma_addr_t *paddr, unsigned long flags); 12388c2ecf20Sopenharmony_cistatic void 12398c2ecf20Sopenharmony_cippc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *tx, 12408c2ecf20Sopenharmony_ci dma_addr_t addr, int index); 12418c2ecf20Sopenharmony_cistatic void 12428c2ecf20Sopenharmony_cippc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot *tx, 12438c2ecf20Sopenharmony_ci unsigned char mult, int index, int dst_pos); 12448c2ecf20Sopenharmony_cistatic void 12458c2ecf20Sopenharmony_cippc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot *tx, 12468c2ecf20Sopenharmony_ci dma_addr_t paddr, dma_addr_t qaddr); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic struct page *ppc440spe_rxor_srcs[32]; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci/** 12518c2ecf20Sopenharmony_ci * ppc440spe_can_rxor - check if the operands may be processed with RXOR 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_cistatic int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci int i, order = 0, state = 0; 12568c2ecf20Sopenharmony_ci int idx = 0; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (unlikely(!(src_cnt > 1))) 12598c2ecf20Sopenharmony_ci return 0; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs)); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* Skip holes in the source list before checking */ 12648c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) { 12658c2ecf20Sopenharmony_ci if (!srcs[i]) 12668c2ecf20Sopenharmony_ci continue; 12678c2ecf20Sopenharmony_ci ppc440spe_rxor_srcs[idx++] = srcs[i]; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci src_cnt = idx; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci for (i = 1; i < src_cnt; i++) { 12728c2ecf20Sopenharmony_ci char *cur_addr = page_address(ppc440spe_rxor_srcs[i]); 12738c2ecf20Sopenharmony_ci char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci switch (state) { 12768c2ecf20Sopenharmony_ci case 0: 12778c2ecf20Sopenharmony_ci if (cur_addr == old_addr + len) { 12788c2ecf20Sopenharmony_ci /* direct RXOR */ 12798c2ecf20Sopenharmony_ci order = 1; 12808c2ecf20Sopenharmony_ci state = 1; 12818c2ecf20Sopenharmony_ci } else if (old_addr == cur_addr + len) { 12828c2ecf20Sopenharmony_ci /* reverse RXOR */ 12838c2ecf20Sopenharmony_ci order = -1; 12848c2ecf20Sopenharmony_ci state = 1; 12858c2ecf20Sopenharmony_ci } else 12868c2ecf20Sopenharmony_ci goto out; 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci case 1: 12898c2ecf20Sopenharmony_ci if ((i == src_cnt - 2) || 12908c2ecf20Sopenharmony_ci (order == -1 && cur_addr != old_addr - len)) { 12918c2ecf20Sopenharmony_ci order = 0; 12928c2ecf20Sopenharmony_ci state = 0; 12938c2ecf20Sopenharmony_ci } else if ((cur_addr == old_addr + len * order) || 12948c2ecf20Sopenharmony_ci (cur_addr == old_addr + 2 * len) || 12958c2ecf20Sopenharmony_ci (cur_addr == old_addr + 3 * len)) { 12968c2ecf20Sopenharmony_ci state = 2; 12978c2ecf20Sopenharmony_ci } else { 12988c2ecf20Sopenharmony_ci order = 0; 12998c2ecf20Sopenharmony_ci state = 0; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci break; 13028c2ecf20Sopenharmony_ci case 2: 13038c2ecf20Sopenharmony_ci order = 0; 13048c2ecf20Sopenharmony_ci state = 0; 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ciout: 13108c2ecf20Sopenharmony_ci if (state == 1 || state == 2) 13118c2ecf20Sopenharmony_ci return 1; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci return 0; 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci/** 13178c2ecf20Sopenharmony_ci * ppc440spe_adma_device_estimate - estimate the efficiency of processing 13188c2ecf20Sopenharmony_ci * the operation given on this channel. It's assumed that 'chan' is 13198c2ecf20Sopenharmony_ci * capable to process 'cap' type of operation. 13208c2ecf20Sopenharmony_ci * @chan: channel to use 13218c2ecf20Sopenharmony_ci * @cap: type of transaction 13228c2ecf20Sopenharmony_ci * @dst_lst: array of destination pointers 13238c2ecf20Sopenharmony_ci * @dst_cnt: number of destination operands 13248c2ecf20Sopenharmony_ci * @src_lst: array of source pointers 13258c2ecf20Sopenharmony_ci * @src_cnt: number of source operands 13268c2ecf20Sopenharmony_ci * @src_sz: size of each source operand 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_cistatic int ppc440spe_adma_estimate(struct dma_chan *chan, 13298c2ecf20Sopenharmony_ci enum dma_transaction_type cap, struct page **dst_lst, int dst_cnt, 13308c2ecf20Sopenharmony_ci struct page **src_lst, int src_cnt, size_t src_sz) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci int ef = 1; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci if (cap == DMA_PQ || cap == DMA_PQ_VAL) { 13358c2ecf20Sopenharmony_ci /* If RAID-6 capabilities were not activated don't try 13368c2ecf20Sopenharmony_ci * to use them 13378c2ecf20Sopenharmony_ci */ 13388c2ecf20Sopenharmony_ci if (unlikely(!ppc440spe_r6_enabled)) 13398c2ecf20Sopenharmony_ci return -1; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci /* In the current implementation of ppc440spe ADMA driver it 13428c2ecf20Sopenharmony_ci * makes sense to pick out only pq case, because it may be 13438c2ecf20Sopenharmony_ci * processed: 13448c2ecf20Sopenharmony_ci * (1) either using Biskup method on DMA2; 13458c2ecf20Sopenharmony_ci * (2) or on DMA0/1. 13468c2ecf20Sopenharmony_ci * Thus we give a favour to (1) if the sources are suitable; 13478c2ecf20Sopenharmony_ci * else let it be processed on one of the DMA0/1 engines. 13488c2ecf20Sopenharmony_ci * In the sum_product case where destination is also the 13498c2ecf20Sopenharmony_ci * source process it on DMA0/1 only. 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_ci if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) { 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1]) 13548c2ecf20Sopenharmony_ci ef = 0; /* sum_product case, process on DMA0/1 */ 13558c2ecf20Sopenharmony_ci else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz)) 13568c2ecf20Sopenharmony_ci ef = 3; /* override (DMA0/1 + idle) */ 13578c2ecf20Sopenharmony_ci else 13588c2ecf20Sopenharmony_ci ef = 0; /* can't process on DMA2 if !rxor */ 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* channel idleness increases the priority */ 13628c2ecf20Sopenharmony_ci if (likely(ef) && 13638c2ecf20Sopenharmony_ci !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan))) 13648c2ecf20Sopenharmony_ci ef++; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci return ef; 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistruct dma_chan * 13708c2ecf20Sopenharmony_cippc440spe_async_tx_find_best_channel(enum dma_transaction_type cap, 13718c2ecf20Sopenharmony_ci struct page **dst_lst, int dst_cnt, struct page **src_lst, 13728c2ecf20Sopenharmony_ci int src_cnt, size_t src_sz) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci struct dma_chan *best_chan = NULL; 13758c2ecf20Sopenharmony_ci struct ppc_dma_chan_ref *ref; 13768c2ecf20Sopenharmony_ci int best_rank = -1; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (unlikely(!src_sz)) 13798c2ecf20Sopenharmony_ci return NULL; 13808c2ecf20Sopenharmony_ci if (src_sz > PAGE_SIZE) { 13818c2ecf20Sopenharmony_ci /* 13828c2ecf20Sopenharmony_ci * should a user of the api ever pass > PAGE_SIZE requests 13838c2ecf20Sopenharmony_ci * we sort out cases where temporary page-sized buffers 13848c2ecf20Sopenharmony_ci * are used. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_ci switch (cap) { 13878c2ecf20Sopenharmony_ci case DMA_PQ: 13888c2ecf20Sopenharmony_ci if (src_cnt == 1 && dst_lst[1] == src_lst[0]) 13898c2ecf20Sopenharmony_ci return NULL; 13908c2ecf20Sopenharmony_ci if (src_cnt == 2 && dst_lst[1] == src_lst[1]) 13918c2ecf20Sopenharmony_ci return NULL; 13928c2ecf20Sopenharmony_ci break; 13938c2ecf20Sopenharmony_ci case DMA_PQ_VAL: 13948c2ecf20Sopenharmony_ci case DMA_XOR_VAL: 13958c2ecf20Sopenharmony_ci return NULL; 13968c2ecf20Sopenharmony_ci default: 13978c2ecf20Sopenharmony_ci break; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) { 14028c2ecf20Sopenharmony_ci if (dma_has_cap(cap, ref->chan->device->cap_mask)) { 14038c2ecf20Sopenharmony_ci int rank; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst, 14068c2ecf20Sopenharmony_ci dst_cnt, src_lst, src_cnt, src_sz); 14078c2ecf20Sopenharmony_ci if (rank > best_rank) { 14088c2ecf20Sopenharmony_ci best_rank = rank; 14098c2ecf20Sopenharmony_ci best_chan = ref->chan; 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return best_chan; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci/** 14198c2ecf20Sopenharmony_ci * ppc440spe_get_group_entry - get group entry with index idx 14208c2ecf20Sopenharmony_ci * @tdesc: is the last allocated slot in the group. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot * 14238c2ecf20Sopenharmony_cippc440spe_get_group_entry(struct ppc440spe_adma_desc_slot *tdesc, u32 entry_idx) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter = tdesc->group_head; 14268c2ecf20Sopenharmony_ci int i = 0; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) { 14298c2ecf20Sopenharmony_ci printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n", 14308c2ecf20Sopenharmony_ci __func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt); 14318c2ecf20Sopenharmony_ci BUG(); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci list_for_each_entry(iter, &tdesc->group_list, chain_node) { 14358c2ecf20Sopenharmony_ci if (i++ == entry_idx) 14368c2ecf20Sopenharmony_ci break; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci return iter; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci/** 14428c2ecf20Sopenharmony_ci * ppc440spe_adma_free_slots - flags descriptor slots for reuse 14438c2ecf20Sopenharmony_ci * @slot: Slot to free 14448c2ecf20Sopenharmony_ci * Caller must hold &ppc440spe_chan->lock while calling this function 14458c2ecf20Sopenharmony_ci */ 14468c2ecf20Sopenharmony_cistatic void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot, 14478c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci int stride = slot->slots_per_op; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci while (stride--) { 14528c2ecf20Sopenharmony_ci slot->slots_per_op = 0; 14538c2ecf20Sopenharmony_ci slot = list_entry(slot->slot_node.next, 14548c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 14558c2ecf20Sopenharmony_ci slot_node); 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci/** 14608c2ecf20Sopenharmony_ci * ppc440spe_adma_run_tx_complete_actions - call functions to be called 14618c2ecf20Sopenharmony_ci * upon completion 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_cistatic dma_cookie_t ppc440spe_adma_run_tx_complete_actions( 14648c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 14658c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 14668c2ecf20Sopenharmony_ci dma_cookie_t cookie) 14678c2ecf20Sopenharmony_ci{ 14688c2ecf20Sopenharmony_ci BUG_ON(desc->async_tx.cookie < 0); 14698c2ecf20Sopenharmony_ci if (desc->async_tx.cookie > 0) { 14708c2ecf20Sopenharmony_ci cookie = desc->async_tx.cookie; 14718c2ecf20Sopenharmony_ci desc->async_tx.cookie = 0; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci dma_descriptor_unmap(&desc->async_tx); 14748c2ecf20Sopenharmony_ci /* call the callback (must not sleep or submit new 14758c2ecf20Sopenharmony_ci * operations to this channel) 14768c2ecf20Sopenharmony_ci */ 14778c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL); 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* run dependent operations */ 14818c2ecf20Sopenharmony_ci dma_run_dependencies(&desc->async_tx); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci return cookie; 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci/** 14878c2ecf20Sopenharmony_ci * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set) 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_cistatic int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc, 14908c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci /* the client is allowed to attach dependent operations 14938c2ecf20Sopenharmony_ci * until 'ack' is set 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_ci if (!async_tx_test_ack(&desc->async_tx)) 14968c2ecf20Sopenharmony_ci return 0; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci /* leave the last descriptor in the chain 14998c2ecf20Sopenharmony_ci * so we can append to it 15008c2ecf20Sopenharmony_ci */ 15018c2ecf20Sopenharmony_ci if (list_is_last(&desc->chain_node, &chan->chain) || 15028c2ecf20Sopenharmony_ci desc->phys == ppc440spe_chan_get_current_descriptor(chan)) 15038c2ecf20Sopenharmony_ci return 1; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (chan->device->id != PPC440SPE_XOR_ID) { 15068c2ecf20Sopenharmony_ci /* our DMA interrupt handler clears opc field of 15078c2ecf20Sopenharmony_ci * each processed descriptor. For all types of 15088c2ecf20Sopenharmony_ci * operations except for ZeroSum we do not actually 15098c2ecf20Sopenharmony_ci * need ack from the interrupt handler. ZeroSum is a 15108c2ecf20Sopenharmony_ci * special case since the result of this operation 15118c2ecf20Sopenharmony_ci * is available from the handler only, so if we see 15128c2ecf20Sopenharmony_ci * such type of descriptor (which is unprocessed yet) 15138c2ecf20Sopenharmony_ci * then leave it in chain. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_ci struct dma_cdb *cdb = desc->hw_desc; 15168c2ecf20Sopenharmony_ci if (cdb->opc == DMA_CDB_OPC_DCHECK128) 15178c2ecf20Sopenharmony_ci return 1; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n", 15218c2ecf20Sopenharmony_ci desc->phys, desc->idx, desc->slots_per_op); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci list_del(&desc->chain_node); 15248c2ecf20Sopenharmony_ci ppc440spe_adma_free_slots(desc, chan); 15258c2ecf20Sopenharmony_ci return 0; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci/** 15298c2ecf20Sopenharmony_ci * __ppc440spe_adma_slot_cleanup - this is the common clean-up routine 15308c2ecf20Sopenharmony_ci * which runs through the channel CDBs list until reach the descriptor 15318c2ecf20Sopenharmony_ci * currently processed. When routine determines that all CDBs of group 15328c2ecf20Sopenharmony_ci * are completed then corresponding callbacks (if any) are called and slots 15338c2ecf20Sopenharmony_ci * are freed. 15348c2ecf20Sopenharmony_ci */ 15358c2ecf20Sopenharmony_cistatic void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter, *_iter, *group_start = NULL; 15388c2ecf20Sopenharmony_ci dma_cookie_t cookie = 0; 15398c2ecf20Sopenharmony_ci u32 current_desc = ppc440spe_chan_get_current_descriptor(chan); 15408c2ecf20Sopenharmony_ci int busy = ppc440spe_chan_is_busy(chan); 15418c2ecf20Sopenharmony_ci int seen_current = 0, slot_cnt = 0, slots_per_op = 0; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, "ppc440spe adma%d: %s\n", 15448c2ecf20Sopenharmony_ci chan->device->id, __func__); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (!current_desc) { 15478c2ecf20Sopenharmony_ci /* There were no transactions yet, so 15488c2ecf20Sopenharmony_ci * nothing to clean 15498c2ecf20Sopenharmony_ci */ 15508c2ecf20Sopenharmony_ci return; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci /* free completed slots from the chain starting with 15548c2ecf20Sopenharmony_ci * the oldest descriptor 15558c2ecf20Sopenharmony_ci */ 15568c2ecf20Sopenharmony_ci list_for_each_entry_safe(iter, _iter, &chan->chain, 15578c2ecf20Sopenharmony_ci chain_node) { 15588c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, "\tcookie: %d slot: %d " 15598c2ecf20Sopenharmony_ci "busy: %d this_desc: %#llx next_desc: %#x " 15608c2ecf20Sopenharmony_ci "cur: %#x ack: %d\n", 15618c2ecf20Sopenharmony_ci iter->async_tx.cookie, iter->idx, busy, iter->phys, 15628c2ecf20Sopenharmony_ci ppc440spe_desc_get_link(iter, chan), current_desc, 15638c2ecf20Sopenharmony_ci async_tx_test_ack(&iter->async_tx)); 15648c2ecf20Sopenharmony_ci prefetch(_iter); 15658c2ecf20Sopenharmony_ci prefetch(&_iter->async_tx); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* do not advance past the current descriptor loaded into the 15688c2ecf20Sopenharmony_ci * hardware channel,subsequent descriptors are either in process 15698c2ecf20Sopenharmony_ci * or have not been submitted 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_ci if (seen_current) 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* stop the search if we reach the current descriptor and the 15758c2ecf20Sopenharmony_ci * channel is busy, or if it appears that the current descriptor 15768c2ecf20Sopenharmony_ci * needs to be re-read (i.e. has been appended to) 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ci if (iter->phys == current_desc) { 15798c2ecf20Sopenharmony_ci BUG_ON(seen_current++); 15808c2ecf20Sopenharmony_ci if (busy || ppc440spe_desc_get_link(iter, chan)) { 15818c2ecf20Sopenharmony_ci /* not all descriptors of the group have 15828c2ecf20Sopenharmony_ci * been completed; exit. 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_ci break; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* detect the start of a group transaction */ 15898c2ecf20Sopenharmony_ci if (!slot_cnt && !slots_per_op) { 15908c2ecf20Sopenharmony_ci slot_cnt = iter->slot_cnt; 15918c2ecf20Sopenharmony_ci slots_per_op = iter->slots_per_op; 15928c2ecf20Sopenharmony_ci if (slot_cnt <= slots_per_op) { 15938c2ecf20Sopenharmony_ci slot_cnt = 0; 15948c2ecf20Sopenharmony_ci slots_per_op = 0; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (slot_cnt) { 15998c2ecf20Sopenharmony_ci if (!group_start) 16008c2ecf20Sopenharmony_ci group_start = iter; 16018c2ecf20Sopenharmony_ci slot_cnt -= slots_per_op; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* all the members of a group are complete */ 16058c2ecf20Sopenharmony_ci if (slots_per_op != 0 && slot_cnt == 0) { 16068c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *grp_iter, *_grp_iter; 16078c2ecf20Sopenharmony_ci int end_of_chain = 0; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* clean up the group */ 16108c2ecf20Sopenharmony_ci slot_cnt = group_start->slot_cnt; 16118c2ecf20Sopenharmony_ci grp_iter = group_start; 16128c2ecf20Sopenharmony_ci list_for_each_entry_safe_from(grp_iter, _grp_iter, 16138c2ecf20Sopenharmony_ci &chan->chain, chain_node) { 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci cookie = ppc440spe_adma_run_tx_complete_actions( 16168c2ecf20Sopenharmony_ci grp_iter, chan, cookie); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci slot_cnt -= slots_per_op; 16198c2ecf20Sopenharmony_ci end_of_chain = ppc440spe_adma_clean_slot( 16208c2ecf20Sopenharmony_ci grp_iter, chan); 16218c2ecf20Sopenharmony_ci if (end_of_chain && slot_cnt) { 16228c2ecf20Sopenharmony_ci /* Should wait for ZeroSum completion */ 16238c2ecf20Sopenharmony_ci if (cookie > 0) 16248c2ecf20Sopenharmony_ci chan->common.completed_cookie = cookie; 16258c2ecf20Sopenharmony_ci return; 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci if (slot_cnt == 0 || end_of_chain) 16298c2ecf20Sopenharmony_ci break; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci /* the group should be complete at this point */ 16338c2ecf20Sopenharmony_ci BUG_ON(slot_cnt); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci slots_per_op = 0; 16368c2ecf20Sopenharmony_ci group_start = NULL; 16378c2ecf20Sopenharmony_ci if (end_of_chain) 16388c2ecf20Sopenharmony_ci break; 16398c2ecf20Sopenharmony_ci else 16408c2ecf20Sopenharmony_ci continue; 16418c2ecf20Sopenharmony_ci } else if (slots_per_op) /* wait for group completion */ 16428c2ecf20Sopenharmony_ci continue; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci cookie = ppc440spe_adma_run_tx_complete_actions(iter, chan, 16458c2ecf20Sopenharmony_ci cookie); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci if (ppc440spe_adma_clean_slot(iter, chan)) 16488c2ecf20Sopenharmony_ci break; 16498c2ecf20Sopenharmony_ci } 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci BUG_ON(!seen_current); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci if (cookie > 0) { 16548c2ecf20Sopenharmony_ci chan->common.completed_cookie = cookie; 16558c2ecf20Sopenharmony_ci pr_debug("\tcompleted cookie %d\n", cookie); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci} 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci/** 16618c2ecf20Sopenharmony_ci * ppc440spe_adma_tasklet - clean up watch-dog initiator 16628c2ecf20Sopenharmony_ci */ 16638c2ecf20Sopenharmony_cistatic void ppc440spe_adma_tasklet(struct tasklet_struct *t) 16648c2ecf20Sopenharmony_ci{ 16658c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan = from_tasklet(chan, t, irq_tasklet); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci spin_lock_nested(&chan->lock, SINGLE_DEPTH_NESTING); 16688c2ecf20Sopenharmony_ci __ppc440spe_adma_slot_cleanup(chan); 16698c2ecf20Sopenharmony_ci spin_unlock(&chan->lock); 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci/** 16738c2ecf20Sopenharmony_ci * ppc440spe_adma_slot_cleanup - clean up scheduled initiator 16748c2ecf20Sopenharmony_ci */ 16758c2ecf20Sopenharmony_cistatic void ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci spin_lock_bh(&chan->lock); 16788c2ecf20Sopenharmony_ci __ppc440spe_adma_slot_cleanup(chan); 16798c2ecf20Sopenharmony_ci spin_unlock_bh(&chan->lock); 16808c2ecf20Sopenharmony_ci} 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci/** 16838c2ecf20Sopenharmony_ci * ppc440spe_adma_alloc_slots - allocate free slots (if any) 16848c2ecf20Sopenharmony_ci */ 16858c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots( 16868c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, int num_slots, 16878c2ecf20Sopenharmony_ci int slots_per_op) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter = NULL, *_iter; 16908c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *alloc_start = NULL; 16918c2ecf20Sopenharmony_ci struct list_head chain = LIST_HEAD_INIT(chain); 16928c2ecf20Sopenharmony_ci int slots_found, retry = 0; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci BUG_ON(!num_slots || !slots_per_op); 16968c2ecf20Sopenharmony_ci /* start search from the last allocated descrtiptor 16978c2ecf20Sopenharmony_ci * if a contiguous allocation can not be found start searching 16988c2ecf20Sopenharmony_ci * from the beginning of the list 16998c2ecf20Sopenharmony_ci */ 17008c2ecf20Sopenharmony_ciretry: 17018c2ecf20Sopenharmony_ci slots_found = 0; 17028c2ecf20Sopenharmony_ci if (retry == 0) 17038c2ecf20Sopenharmony_ci iter = chan->last_used; 17048c2ecf20Sopenharmony_ci else 17058c2ecf20Sopenharmony_ci iter = list_entry(&chan->all_slots, 17068c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 17078c2ecf20Sopenharmony_ci slot_node); 17088c2ecf20Sopenharmony_ci list_for_each_entry_safe_continue(iter, _iter, &chan->all_slots, 17098c2ecf20Sopenharmony_ci slot_node) { 17108c2ecf20Sopenharmony_ci prefetch(_iter); 17118c2ecf20Sopenharmony_ci prefetch(&_iter->async_tx); 17128c2ecf20Sopenharmony_ci if (iter->slots_per_op) { 17138c2ecf20Sopenharmony_ci slots_found = 0; 17148c2ecf20Sopenharmony_ci continue; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci /* start the allocation if the slot is correctly aligned */ 17188c2ecf20Sopenharmony_ci if (!slots_found++) 17198c2ecf20Sopenharmony_ci alloc_start = iter; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if (slots_found == num_slots) { 17228c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *alloc_tail = NULL; 17238c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *last_used = NULL; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci iter = alloc_start; 17268c2ecf20Sopenharmony_ci while (num_slots) { 17278c2ecf20Sopenharmony_ci int i; 17288c2ecf20Sopenharmony_ci /* pre-ack all but the last descriptor */ 17298c2ecf20Sopenharmony_ci if (num_slots != slots_per_op) 17308c2ecf20Sopenharmony_ci async_tx_ack(&iter->async_tx); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci list_add_tail(&iter->chain_node, &chain); 17338c2ecf20Sopenharmony_ci alloc_tail = iter; 17348c2ecf20Sopenharmony_ci iter->async_tx.cookie = 0; 17358c2ecf20Sopenharmony_ci iter->hw_next = NULL; 17368c2ecf20Sopenharmony_ci iter->flags = 0; 17378c2ecf20Sopenharmony_ci iter->slot_cnt = num_slots; 17388c2ecf20Sopenharmony_ci iter->xor_check_result = NULL; 17398c2ecf20Sopenharmony_ci for (i = 0; i < slots_per_op; i++) { 17408c2ecf20Sopenharmony_ci iter->slots_per_op = slots_per_op - i; 17418c2ecf20Sopenharmony_ci last_used = iter; 17428c2ecf20Sopenharmony_ci iter = list_entry(iter->slot_node.next, 17438c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 17448c2ecf20Sopenharmony_ci slot_node); 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci num_slots -= slots_per_op; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci alloc_tail->group_head = alloc_start; 17498c2ecf20Sopenharmony_ci alloc_tail->async_tx.cookie = -EBUSY; 17508c2ecf20Sopenharmony_ci list_splice(&chain, &alloc_tail->group_list); 17518c2ecf20Sopenharmony_ci chan->last_used = last_used; 17528c2ecf20Sopenharmony_ci return alloc_tail; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci if (!retry++) 17568c2ecf20Sopenharmony_ci goto retry; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci /* try to free some slots if the allocation fails */ 17598c2ecf20Sopenharmony_ci tasklet_schedule(&chan->irq_tasklet); 17608c2ecf20Sopenharmony_ci return NULL; 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci/** 17648c2ecf20Sopenharmony_ci * ppc440spe_adma_alloc_chan_resources - allocate pools for CDB slots 17658c2ecf20Sopenharmony_ci */ 17668c2ecf20Sopenharmony_cistatic int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 17698c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *slot = NULL; 17708c2ecf20Sopenharmony_ci char *hw_desc; 17718c2ecf20Sopenharmony_ci int i, db_sz; 17728c2ecf20Sopenharmony_ci int init; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 17758c2ecf20Sopenharmony_ci init = ppc440spe_chan->slots_allocated ? 0 : 1; 17768c2ecf20Sopenharmony_ci chan->chan_id = ppc440spe_chan->device->id; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* Allocate descriptor slots */ 17798c2ecf20Sopenharmony_ci i = ppc440spe_chan->slots_allocated; 17808c2ecf20Sopenharmony_ci if (ppc440spe_chan->device->id != PPC440SPE_XOR_ID) 17818c2ecf20Sopenharmony_ci db_sz = sizeof(struct dma_cdb); 17828c2ecf20Sopenharmony_ci else 17838c2ecf20Sopenharmony_ci db_sz = sizeof(struct xor_cb); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci for (; i < (ppc440spe_chan->device->pool_size / db_sz); i++) { 17868c2ecf20Sopenharmony_ci slot = kzalloc(sizeof(struct ppc440spe_adma_desc_slot), 17878c2ecf20Sopenharmony_ci GFP_KERNEL); 17888c2ecf20Sopenharmony_ci if (!slot) { 17898c2ecf20Sopenharmony_ci printk(KERN_INFO "SPE ADMA Channel only initialized" 17908c2ecf20Sopenharmony_ci " %d descriptor slots", i--); 17918c2ecf20Sopenharmony_ci break; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci hw_desc = (char *) ppc440spe_chan->device->dma_desc_pool_virt; 17958c2ecf20Sopenharmony_ci slot->hw_desc = (void *) &hw_desc[i * db_sz]; 17968c2ecf20Sopenharmony_ci dma_async_tx_descriptor_init(&slot->async_tx, chan); 17978c2ecf20Sopenharmony_ci slot->async_tx.tx_submit = ppc440spe_adma_tx_submit; 17988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&slot->chain_node); 17998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&slot->slot_node); 18008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&slot->group_list); 18018c2ecf20Sopenharmony_ci slot->phys = ppc440spe_chan->device->dma_desc_pool + i * db_sz; 18028c2ecf20Sopenharmony_ci slot->idx = i; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 18058c2ecf20Sopenharmony_ci ppc440spe_chan->slots_allocated++; 18068c2ecf20Sopenharmony_ci list_add_tail(&slot->slot_node, &ppc440spe_chan->all_slots); 18078c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (i && !ppc440spe_chan->last_used) { 18118c2ecf20Sopenharmony_ci ppc440spe_chan->last_used = 18128c2ecf20Sopenharmony_ci list_entry(ppc440spe_chan->all_slots.next, 18138c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 18148c2ecf20Sopenharmony_ci slot_node); 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 18188c2ecf20Sopenharmony_ci "ppc440spe adma%d: allocated %d descriptor slots\n", 18198c2ecf20Sopenharmony_ci ppc440spe_chan->device->id, i); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* initialize the channel and the chain with a null operation */ 18228c2ecf20Sopenharmony_ci if (init) { 18238c2ecf20Sopenharmony_ci switch (ppc440spe_chan->device->id) { 18248c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 18258c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 18268c2ecf20Sopenharmony_ci ppc440spe_chan->hw_chain_inited = 0; 18278c2ecf20Sopenharmony_ci /* Use WXOR for self-testing */ 18288c2ecf20Sopenharmony_ci if (!ppc440spe_r6_tchan) 18298c2ecf20Sopenharmony_ci ppc440spe_r6_tchan = ppc440spe_chan; 18308c2ecf20Sopenharmony_ci break; 18318c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 18328c2ecf20Sopenharmony_ci ppc440spe_chan_start_null_xor(ppc440spe_chan); 18338c2ecf20Sopenharmony_ci break; 18348c2ecf20Sopenharmony_ci default: 18358c2ecf20Sopenharmony_ci BUG(); 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci ppc440spe_chan->needs_unmap = 1; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci return (i > 0) ? i : -ENOMEM; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/** 18448c2ecf20Sopenharmony_ci * ppc440spe_rxor_set_region_data - 18458c2ecf20Sopenharmony_ci */ 18468c2ecf20Sopenharmony_cistatic void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc, 18478c2ecf20Sopenharmony_ci u8 xor_arg_no, u32 mask) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct xor_cb *xcb = desc->hw_desc; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci xcb->ops[xor_arg_no].h |= mask; 18528c2ecf20Sopenharmony_ci} 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci/** 18558c2ecf20Sopenharmony_ci * ppc440spe_rxor_set_src - 18568c2ecf20Sopenharmony_ci */ 18578c2ecf20Sopenharmony_cistatic void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc, 18588c2ecf20Sopenharmony_ci u8 xor_arg_no, dma_addr_t addr) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci struct xor_cb *xcb = desc->hw_desc; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE; 18638c2ecf20Sopenharmony_ci xcb->ops[xor_arg_no].l = addr; 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci/** 18678c2ecf20Sopenharmony_ci * ppc440spe_rxor_set_mult - 18688c2ecf20Sopenharmony_ci */ 18698c2ecf20Sopenharmony_cistatic void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc, 18708c2ecf20Sopenharmony_ci u8 xor_arg_no, u8 idx, u8 mult) 18718c2ecf20Sopenharmony_ci{ 18728c2ecf20Sopenharmony_ci struct xor_cb *xcb = desc->hw_desc; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8); 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci/** 18788c2ecf20Sopenharmony_ci * ppc440spe_adma_check_threshold - append CDBs to h/w chain if threshold 18798c2ecf20Sopenharmony_ci * has been achieved 18808c2ecf20Sopenharmony_ci */ 18818c2ecf20Sopenharmony_cistatic void ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan *chan) 18828c2ecf20Sopenharmony_ci{ 18838c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, "ppc440spe adma%d: pending: %d\n", 18848c2ecf20Sopenharmony_ci chan->device->id, chan->pending); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (chan->pending >= PPC440SPE_ADMA_THRESHOLD) { 18878c2ecf20Sopenharmony_ci chan->pending = 0; 18888c2ecf20Sopenharmony_ci ppc440spe_chan_append(chan); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci} 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci/** 18938c2ecf20Sopenharmony_ci * ppc440spe_adma_tx_submit - submit new descriptor group to the channel 18948c2ecf20Sopenharmony_ci * (it's not necessary that descriptors will be submitted to the h/w 18958c2ecf20Sopenharmony_ci * chains too right now) 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_cistatic dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx) 18988c2ecf20Sopenharmony_ci{ 18998c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc; 19008c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(tx->chan); 19018c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *group_start, *old_chain_tail; 19028c2ecf20Sopenharmony_ci int slot_cnt; 19038c2ecf20Sopenharmony_ci int slots_per_op; 19048c2ecf20Sopenharmony_ci dma_cookie_t cookie; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci sw_desc = tx_to_ppc440spe_adma_slot(tx); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci group_start = sw_desc->group_head; 19098c2ecf20Sopenharmony_ci slot_cnt = group_start->slot_cnt; 19108c2ecf20Sopenharmony_ci slots_per_op = group_start->slots_per_op; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci spin_lock_bh(&chan->lock); 19138c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(tx); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if (unlikely(list_empty(&chan->chain))) { 19168c2ecf20Sopenharmony_ci /* first peer */ 19178c2ecf20Sopenharmony_ci list_splice_init(&sw_desc->group_list, &chan->chain); 19188c2ecf20Sopenharmony_ci chan_first_cdb[chan->device->id] = group_start; 19198c2ecf20Sopenharmony_ci } else { 19208c2ecf20Sopenharmony_ci /* isn't first peer, bind CDBs to chain */ 19218c2ecf20Sopenharmony_ci old_chain_tail = list_entry(chan->chain.prev, 19228c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 19238c2ecf20Sopenharmony_ci chain_node); 19248c2ecf20Sopenharmony_ci list_splice_init(&sw_desc->group_list, 19258c2ecf20Sopenharmony_ci &old_chain_tail->chain_node); 19268c2ecf20Sopenharmony_ci /* fix up the hardware chain */ 19278c2ecf20Sopenharmony_ci ppc440spe_desc_set_link(chan, old_chain_tail, group_start); 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* increment the pending count by the number of operations */ 19318c2ecf20Sopenharmony_ci chan->pending += slot_cnt / slots_per_op; 19328c2ecf20Sopenharmony_ci ppc440spe_adma_check_threshold(chan); 19338c2ecf20Sopenharmony_ci spin_unlock_bh(&chan->lock); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, 19368c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s cookie: %d slot: %d tx %p\n", 19378c2ecf20Sopenharmony_ci chan->device->id, __func__, 19388c2ecf20Sopenharmony_ci sw_desc->async_tx.cookie, sw_desc->idx, sw_desc); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci return cookie; 19418c2ecf20Sopenharmony_ci} 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci/** 19448c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_interrupt - prepare CDB for a pseudo DMA operation 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_interrupt( 19478c2ecf20Sopenharmony_ci struct dma_chan *chan, unsigned long flags) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 19508c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *group_start; 19518c2ecf20Sopenharmony_ci int slot_cnt, slots_per_op; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 19568c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s\n", ppc440spe_chan->device->id, 19578c2ecf20Sopenharmony_ci __func__); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 19608c2ecf20Sopenharmony_ci slot_cnt = slots_per_op = 1; 19618c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 19628c2ecf20Sopenharmony_ci slots_per_op); 19638c2ecf20Sopenharmony_ci if (sw_desc) { 19648c2ecf20Sopenharmony_ci group_start = sw_desc->group_head; 19658c2ecf20Sopenharmony_ci ppc440spe_desc_init_interrupt(group_start, ppc440spe_chan); 19668c2ecf20Sopenharmony_ci group_start->unmap_len = 0; 19678c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci/** 19758c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_memcpy - prepare CDB for a MEMCPY operation 19768c2ecf20Sopenharmony_ci */ 19778c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy( 19788c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t dma_dest, 19798c2ecf20Sopenharmony_ci dma_addr_t dma_src, size_t len, unsigned long flags) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 19828c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *group_start; 19838c2ecf20Sopenharmony_ci int slot_cnt, slots_per_op; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci if (unlikely(!len)) 19888c2ecf20Sopenharmony_ci return NULL; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 19958c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s len: %u int_en %d\n", 19968c2ecf20Sopenharmony_ci ppc440spe_chan->device->id, __func__, len, 19978c2ecf20Sopenharmony_ci flags & DMA_PREP_INTERRUPT ? 1 : 0); 19988c2ecf20Sopenharmony_ci slot_cnt = slots_per_op = 1; 19998c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 20008c2ecf20Sopenharmony_ci slots_per_op); 20018c2ecf20Sopenharmony_ci if (sw_desc) { 20028c2ecf20Sopenharmony_ci group_start = sw_desc->group_head; 20038c2ecf20Sopenharmony_ci ppc440spe_desc_init_memcpy(group_start, flags); 20048c2ecf20Sopenharmony_ci ppc440spe_adma_set_dest(group_start, dma_dest, 0); 20058c2ecf20Sopenharmony_ci ppc440spe_adma_memcpy_xor_set_src(group_start, dma_src, 0); 20068c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len); 20078c2ecf20Sopenharmony_ci sw_desc->unmap_len = len; 20088c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 20138c2ecf20Sopenharmony_ci} 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci/** 20168c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_xor - prepare CDB for a XOR operation 20178c2ecf20Sopenharmony_ci */ 20188c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor( 20198c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t dma_dest, 20208c2ecf20Sopenharmony_ci dma_addr_t *dma_src, u32 src_cnt, size_t len, 20218c2ecf20Sopenharmony_ci unsigned long flags) 20228c2ecf20Sopenharmony_ci{ 20238c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 20248c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *group_start; 20258c2ecf20Sopenharmony_ci int slot_cnt, slots_per_op; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci ADMA_LL_DBG(prep_dma_xor_dbg(ppc440spe_chan->device->id, 20308c2ecf20Sopenharmony_ci dma_dest, dma_src, src_cnt)); 20318c2ecf20Sopenharmony_ci if (unlikely(!len)) 20328c2ecf20Sopenharmony_ci return NULL; 20338c2ecf20Sopenharmony_ci BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 20368c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n", 20378c2ecf20Sopenharmony_ci ppc440spe_chan->device->id, __func__, src_cnt, len, 20388c2ecf20Sopenharmony_ci flags & DMA_PREP_INTERRUPT ? 1 : 0); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 20418c2ecf20Sopenharmony_ci slot_cnt = ppc440spe_chan_xor_slot_count(len, src_cnt, &slots_per_op); 20428c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 20438c2ecf20Sopenharmony_ci slots_per_op); 20448c2ecf20Sopenharmony_ci if (sw_desc) { 20458c2ecf20Sopenharmony_ci group_start = sw_desc->group_head; 20468c2ecf20Sopenharmony_ci ppc440spe_desc_init_xor(group_start, src_cnt, flags); 20478c2ecf20Sopenharmony_ci ppc440spe_adma_set_dest(group_start, dma_dest, 0); 20488c2ecf20Sopenharmony_ci while (src_cnt--) 20498c2ecf20Sopenharmony_ci ppc440spe_adma_memcpy_xor_set_src(group_start, 20508c2ecf20Sopenharmony_ci dma_src[src_cnt], src_cnt); 20518c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len); 20528c2ecf20Sopenharmony_ci sw_desc->unmap_len = len; 20538c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 20588c2ecf20Sopenharmony_ci} 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_cistatic inline void 20618c2ecf20Sopenharmony_cippc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot *desc, 20628c2ecf20Sopenharmony_ci int src_cnt); 20638c2ecf20Sopenharmony_cistatic void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci/** 20668c2ecf20Sopenharmony_ci * ppc440spe_adma_init_dma2rxor_slot - 20678c2ecf20Sopenharmony_ci */ 20688c2ecf20Sopenharmony_cistatic void ppc440spe_adma_init_dma2rxor_slot( 20698c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 20708c2ecf20Sopenharmony_ci dma_addr_t *src, int src_cnt) 20718c2ecf20Sopenharmony_ci{ 20728c2ecf20Sopenharmony_ci int i; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci /* initialize CDB */ 20758c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) { 20768c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i, 20778c2ecf20Sopenharmony_ci desc->src_cnt, (u32)src[i]); 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci} 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci/** 20828c2ecf20Sopenharmony_ci * ppc440spe_dma01_prep_mult - 20838c2ecf20Sopenharmony_ci * for Q operation where destination is also the source 20848c2ecf20Sopenharmony_ci */ 20858c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_mult( 20868c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan, 20878c2ecf20Sopenharmony_ci dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt, 20888c2ecf20Sopenharmony_ci const unsigned char *scf, size_t len, unsigned long flags) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc = NULL; 20918c2ecf20Sopenharmony_ci unsigned long op = 0; 20928c2ecf20Sopenharmony_ci int slot_cnt; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_WXOR, &op); 20958c2ecf20Sopenharmony_ci slot_cnt = 2; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci /* use WXOR, each descriptor occupies one slot */ 21008c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1); 21018c2ecf20Sopenharmony_ci if (sw_desc) { 21028c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 21038c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 21048c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 21078c2ecf20Sopenharmony_ci set_bits(op, &sw_desc->flags); 21088c2ecf20Sopenharmony_ci sw_desc->src_cnt = src_cnt; 21098c2ecf20Sopenharmony_ci sw_desc->dst_cnt = dst_cnt; 21108c2ecf20Sopenharmony_ci /* First descriptor, zero data in the destination and copy it 21118c2ecf20Sopenharmony_ci * to q page using MULTICAST transfer. 21128c2ecf20Sopenharmony_ci */ 21138c2ecf20Sopenharmony_ci iter = list_first_entry(&sw_desc->group_list, 21148c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 21158c2ecf20Sopenharmony_ci chain_node); 21168c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 21178c2ecf20Sopenharmony_ci /* set 'next' pointer */ 21188c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 21198c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 21208c2ecf20Sopenharmony_ci chain_node); 21218c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 21228c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 21238c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MULTICAST; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 21268c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, dst[0], 0); 21278c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1); 21288c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, 21298c2ecf20Sopenharmony_ci src[0]); 21308c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len); 21318c2ecf20Sopenharmony_ci iter->unmap_len = len; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci /* 21348c2ecf20Sopenharmony_ci * Second descriptor, multiply data from the q page 21358c2ecf20Sopenharmony_ci * and store the result in real destination. 21368c2ecf20Sopenharmony_ci */ 21378c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 21388c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 21398c2ecf20Sopenharmony_ci chain_node); 21408c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 21418c2ecf20Sopenharmony_ci iter->hw_next = NULL; 21428c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 21438c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &iter->flags); 21448c2ecf20Sopenharmony_ci else 21458c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 21488c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 21498c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, 21508c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB, dst[1]); 21518c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 21528c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, dst[0], 0); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF, 21558c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1, scf[0]); 21568c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len); 21578c2ecf20Sopenharmony_ci iter->unmap_len = len; 21588c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci return sw_desc; 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci/** 21678c2ecf20Sopenharmony_ci * ppc440spe_dma01_prep_sum_product - 21688c2ecf20Sopenharmony_ci * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also 21698c2ecf20Sopenharmony_ci * the source. 21708c2ecf20Sopenharmony_ci */ 21718c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_sum_product( 21728c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan, 21738c2ecf20Sopenharmony_ci dma_addr_t *dst, dma_addr_t *src, int src_cnt, 21748c2ecf20Sopenharmony_ci const unsigned char *scf, size_t len, unsigned long flags) 21758c2ecf20Sopenharmony_ci{ 21768c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc = NULL; 21778c2ecf20Sopenharmony_ci unsigned long op = 0; 21788c2ecf20Sopenharmony_ci int slot_cnt; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_WXOR, &op); 21818c2ecf20Sopenharmony_ci slot_cnt = 3; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci /* WXOR, each descriptor occupies one slot */ 21868c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1); 21878c2ecf20Sopenharmony_ci if (sw_desc) { 21888c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 21898c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 21908c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 21938c2ecf20Sopenharmony_ci set_bits(op, &sw_desc->flags); 21948c2ecf20Sopenharmony_ci sw_desc->src_cnt = src_cnt; 21958c2ecf20Sopenharmony_ci sw_desc->dst_cnt = 1; 21968c2ecf20Sopenharmony_ci /* 1st descriptor, src[1] data to q page and zero destination */ 21978c2ecf20Sopenharmony_ci iter = list_first_entry(&sw_desc->group_list, 21988c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 21998c2ecf20Sopenharmony_ci chain_node); 22008c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 22018c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 22028c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 22038c2ecf20Sopenharmony_ci chain_node); 22048c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 22058c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 22068c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MULTICAST; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, 22098c2ecf20Sopenharmony_ci *dst, 0); 22108c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 0, 22118c2ecf20Sopenharmony_ci ppc440spe_chan->qdest, 1); 22128c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, 22138c2ecf20Sopenharmony_ci src[1]); 22148c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len); 22158c2ecf20Sopenharmony_ci iter->unmap_len = len; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci /* 2nd descriptor, multiply src[1] data and store the 22188c2ecf20Sopenharmony_ci * result in destination */ 22198c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 22208c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 22218c2ecf20Sopenharmony_ci chain_node); 22228c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 22238c2ecf20Sopenharmony_ci /* set 'next' pointer */ 22248c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 22258c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 22268c2ecf20Sopenharmony_ci chain_node); 22278c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 22288c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &iter->flags); 22298c2ecf20Sopenharmony_ci else 22308c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 22338c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 22348c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, 22358c2ecf20Sopenharmony_ci ppc440spe_chan->qdest); 22368c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, 22378c2ecf20Sopenharmony_ci *dst, 0); 22388c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF, 22398c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1, scf[1]); 22408c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len); 22418c2ecf20Sopenharmony_ci iter->unmap_len = len; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci /* 22448c2ecf20Sopenharmony_ci * 3rd descriptor, multiply src[0] data and xor it 22458c2ecf20Sopenharmony_ci * with destination 22468c2ecf20Sopenharmony_ci */ 22478c2ecf20Sopenharmony_ci iter = list_first_entry(&iter->chain_node, 22488c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 22498c2ecf20Sopenharmony_ci chain_node); 22508c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 22518c2ecf20Sopenharmony_ci iter->hw_next = NULL; 22528c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 22538c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_INT, &iter->flags); 22548c2ecf20Sopenharmony_ci else 22558c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_INT, &iter->flags); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 22588c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 22598c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, 22608c2ecf20Sopenharmony_ci src[0]); 22618c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, 22628c2ecf20Sopenharmony_ci *dst, 0); 22638c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF, 22648c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1, scf[0]); 22658c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len); 22668c2ecf20Sopenharmony_ci iter->unmap_len = len; 22678c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci return sw_desc; 22738c2ecf20Sopenharmony_ci} 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq( 22768c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan, 22778c2ecf20Sopenharmony_ci dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt, 22788c2ecf20Sopenharmony_ci const unsigned char *scf, size_t len, unsigned long flags) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci int slot_cnt; 22818c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter; 22828c2ecf20Sopenharmony_ci unsigned long op = 0; 22838c2ecf20Sopenharmony_ci unsigned char mult = 1; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n", 22868c2ecf20Sopenharmony_ci __func__, dst_cnt, src_cnt, len); 22878c2ecf20Sopenharmony_ci /* select operations WXOR/RXOR depending on the 22888c2ecf20Sopenharmony_ci * source addresses of operators and the number 22898c2ecf20Sopenharmony_ci * of destinations (RXOR support only Q-parity calculations) 22908c2ecf20Sopenharmony_ci */ 22918c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_WXOR, &op); 22928c2ecf20Sopenharmony_ci if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) { 22938c2ecf20Sopenharmony_ci /* no active RXOR; 22948c2ecf20Sopenharmony_ci * do RXOR if: 22958c2ecf20Sopenharmony_ci * - there are more than 1 source, 22968c2ecf20Sopenharmony_ci * - len is aligned on 512-byte boundary, 22978c2ecf20Sopenharmony_ci * - source addresses fit to one of 4 possible regions. 22988c2ecf20Sopenharmony_ci */ 22998c2ecf20Sopenharmony_ci if (src_cnt > 1 && 23008c2ecf20Sopenharmony_ci !(len & MQ0_CF2H_RXOR_BS_MASK) && 23018c2ecf20Sopenharmony_ci (src[0] + len) == src[1]) { 23028c2ecf20Sopenharmony_ci /* may do RXOR R1 R2 */ 23038c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR, &op); 23048c2ecf20Sopenharmony_ci if (src_cnt != 2) { 23058c2ecf20Sopenharmony_ci /* may try to enhance region of RXOR */ 23068c2ecf20Sopenharmony_ci if ((src[1] + len) == src[2]) { 23078c2ecf20Sopenharmony_ci /* do RXOR R1 R2 R3 */ 23088c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR123, 23098c2ecf20Sopenharmony_ci &op); 23108c2ecf20Sopenharmony_ci } else if ((src[1] + len * 2) == src[2]) { 23118c2ecf20Sopenharmony_ci /* do RXOR R1 R2 R4 */ 23128c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR124, &op); 23138c2ecf20Sopenharmony_ci } else if ((src[1] + len * 3) == src[2]) { 23148c2ecf20Sopenharmony_ci /* do RXOR R1 R2 R5 */ 23158c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR125, 23168c2ecf20Sopenharmony_ci &op); 23178c2ecf20Sopenharmony_ci } else { 23188c2ecf20Sopenharmony_ci /* do RXOR R1 R2 */ 23198c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR12, 23208c2ecf20Sopenharmony_ci &op); 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci } else { 23238c2ecf20Sopenharmony_ci /* do RXOR R1 R2 */ 23248c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_RXOR12, &op); 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci if (!test_bit(PPC440SPE_DESC_RXOR, &op)) { 23298c2ecf20Sopenharmony_ci /* can not do this operation with RXOR */ 23308c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_RXOR_RUN, 23318c2ecf20Sopenharmony_ci &ppc440spe_rxor_state); 23328c2ecf20Sopenharmony_ci } else { 23338c2ecf20Sopenharmony_ci /* can do; set block size right now */ 23348c2ecf20Sopenharmony_ci ppc440spe_desc_set_rxor_block_size(len); 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci /* Number of necessary slots depends on operation type selected */ 23398c2ecf20Sopenharmony_ci if (!test_bit(PPC440SPE_DESC_RXOR, &op)) { 23408c2ecf20Sopenharmony_ci /* This is a WXOR only chain. Need descriptors for each 23418c2ecf20Sopenharmony_ci * source to GF-XOR them with WXOR, and need descriptors 23428c2ecf20Sopenharmony_ci * for each destination to zero them with WXOR 23438c2ecf20Sopenharmony_ci */ 23448c2ecf20Sopenharmony_ci slot_cnt = src_cnt; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_P) { 23478c2ecf20Sopenharmony_ci slot_cnt++; 23488c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_P, &op); 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_Q) { 23518c2ecf20Sopenharmony_ci slot_cnt++; 23528c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_Q, &op); 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci } else { 23558c2ecf20Sopenharmony_ci /* Need 1/2 descriptor for RXOR operation, and 23568c2ecf20Sopenharmony_ci * need (src_cnt - (2 or 3)) for WXOR of sources 23578c2ecf20Sopenharmony_ci * remained (if any) 23588c2ecf20Sopenharmony_ci */ 23598c2ecf20Sopenharmony_ci slot_cnt = dst_cnt; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_P) 23628c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_P, &op); 23638c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_Q) 23648c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_Q, &op); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_RXOR12, &op)) 23678c2ecf20Sopenharmony_ci slot_cnt += src_cnt - 2; 23688c2ecf20Sopenharmony_ci else 23698c2ecf20Sopenharmony_ci slot_cnt += src_cnt - 3; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci /* Thus we have either RXOR only chain or 23728c2ecf20Sopenharmony_ci * mixed RXOR/WXOR 23738c2ecf20Sopenharmony_ci */ 23748c2ecf20Sopenharmony_ci if (slot_cnt == dst_cnt) 23758c2ecf20Sopenharmony_ci /* RXOR only chain */ 23768c2ecf20Sopenharmony_ci clear_bit(PPC440SPE_DESC_WXOR, &op); 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 23808c2ecf20Sopenharmony_ci /* for both RXOR/WXOR each descriptor occupies one slot */ 23818c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1); 23828c2ecf20Sopenharmony_ci if (sw_desc) { 23838c2ecf20Sopenharmony_ci ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt, 23848c2ecf20Sopenharmony_ci flags, op); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci /* setup dst/src/mult */ 23878c2ecf20Sopenharmony_ci pr_debug("%s: set dst descriptor 0, 1: 0x%016llx, 0x%016llx\n", 23888c2ecf20Sopenharmony_ci __func__, dst[0], dst[1]); 23898c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_dest(sw_desc, dst, flags); 23908c2ecf20Sopenharmony_ci while (src_cnt--) { 23918c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt], 23928c2ecf20Sopenharmony_ci src_cnt); 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci /* NOTE: "Multi = 0 is equivalent to = 1" as it 23958c2ecf20Sopenharmony_ci * stated in 440SPSPe_RAID6_Addendum_UM_1_17.pdf 23968c2ecf20Sopenharmony_ci * doesn't work for RXOR with DMA0/1! Instead, multi=0 23978c2ecf20Sopenharmony_ci * leads to zeroing source data after RXOR. 23988c2ecf20Sopenharmony_ci * So, for P case set-up mult=1 explicitly. 23998c2ecf20Sopenharmony_ci */ 24008c2ecf20Sopenharmony_ci if (!(flags & DMA_PREP_PQ_DISABLE_Q)) 24018c2ecf20Sopenharmony_ci mult = scf[src_cnt]; 24028c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src_mult(sw_desc, 24038c2ecf20Sopenharmony_ci mult, src_cnt, dst_cnt - 1); 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci /* Setup byte count foreach slot just allocated */ 24078c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 24088c2ecf20Sopenharmony_ci list_for_each_entry(iter, &sw_desc->group_list, 24098c2ecf20Sopenharmony_ci chain_node) { 24108c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, 24118c2ecf20Sopenharmony_ci ppc440spe_chan, len); 24128c2ecf20Sopenharmony_ci iter->unmap_len = len; 24138c2ecf20Sopenharmony_ci } 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci return sw_desc; 24188c2ecf20Sopenharmony_ci} 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_cistatic struct ppc440spe_adma_desc_slot *ppc440spe_dma2_prep_pq( 24218c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan, 24228c2ecf20Sopenharmony_ci dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt, 24238c2ecf20Sopenharmony_ci const unsigned char *scf, size_t len, unsigned long flags) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci int slot_cnt, descs_per_op; 24268c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter; 24278c2ecf20Sopenharmony_ci unsigned long op = 0; 24288c2ecf20Sopenharmony_ci unsigned char mult = 1; 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci BUG_ON(!dst_cnt); 24318c2ecf20Sopenharmony_ci /*pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n", 24328c2ecf20Sopenharmony_ci __func__, dst_cnt, src_cnt, len);*/ 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 24358c2ecf20Sopenharmony_ci descs_per_op = ppc440spe_dma2_pq_slot_count(src, src_cnt, len); 24368c2ecf20Sopenharmony_ci if (descs_per_op < 0) { 24378c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 24388c2ecf20Sopenharmony_ci return NULL; 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* depending on number of sources we have 1 or 2 RXOR chains */ 24428c2ecf20Sopenharmony_ci slot_cnt = descs_per_op * dst_cnt; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1); 24458c2ecf20Sopenharmony_ci if (sw_desc) { 24468c2ecf20Sopenharmony_ci op = slot_cnt; 24478c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 24488c2ecf20Sopenharmony_ci list_for_each_entry(iter, &sw_desc->group_list, chain_node) { 24498c2ecf20Sopenharmony_ci ppc440spe_desc_init_dma2pq(iter, dst_cnt, src_cnt, 24508c2ecf20Sopenharmony_ci --op ? 0 : flags); 24518c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, 24528c2ecf20Sopenharmony_ci len); 24538c2ecf20Sopenharmony_ci iter->unmap_len = len; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci ppc440spe_init_rxor_cursor(&(iter->rxor_cursor)); 24568c2ecf20Sopenharmony_ci iter->rxor_cursor.len = len; 24578c2ecf20Sopenharmony_ci iter->descs_per_op = descs_per_op; 24588c2ecf20Sopenharmony_ci } 24598c2ecf20Sopenharmony_ci op = 0; 24608c2ecf20Sopenharmony_ci list_for_each_entry(iter, &sw_desc->group_list, chain_node) { 24618c2ecf20Sopenharmony_ci op++; 24628c2ecf20Sopenharmony_ci if (op % descs_per_op == 0) 24638c2ecf20Sopenharmony_ci ppc440spe_adma_init_dma2rxor_slot(iter, src, 24648c2ecf20Sopenharmony_ci src_cnt); 24658c2ecf20Sopenharmony_ci if (likely(!list_is_last(&iter->chain_node, 24668c2ecf20Sopenharmony_ci &sw_desc->group_list))) { 24678c2ecf20Sopenharmony_ci /* set 'next' pointer */ 24688c2ecf20Sopenharmony_ci iter->hw_next = 24698c2ecf20Sopenharmony_ci list_entry(iter->chain_node.next, 24708c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 24718c2ecf20Sopenharmony_ci chain_node); 24728c2ecf20Sopenharmony_ci ppc440spe_xor_set_link(iter, iter->hw_next); 24738c2ecf20Sopenharmony_ci } else { 24748c2ecf20Sopenharmony_ci /* this is the last descriptor. */ 24758c2ecf20Sopenharmony_ci iter->hw_next = NULL; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci /* fixup head descriptor */ 24808c2ecf20Sopenharmony_ci sw_desc->dst_cnt = dst_cnt; 24818c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_P) 24828c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_P, &sw_desc->flags); 24838c2ecf20Sopenharmony_ci if (flags & DMA_PREP_ZERO_Q) 24848c2ecf20Sopenharmony_ci set_bit(PPC440SPE_ZERO_Q, &sw_desc->flags); 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci /* setup dst/src/mult */ 24878c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_dest(sw_desc, dst, flags); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci while (src_cnt--) { 24908c2ecf20Sopenharmony_ci /* handle descriptors (if dst_cnt == 2) inside 24918c2ecf20Sopenharmony_ci * the ppc440spe_adma_pq_set_srcxxx() functions 24928c2ecf20Sopenharmony_ci */ 24938c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt], 24948c2ecf20Sopenharmony_ci src_cnt); 24958c2ecf20Sopenharmony_ci if (!(flags & DMA_PREP_PQ_DISABLE_Q)) 24968c2ecf20Sopenharmony_ci mult = scf[src_cnt]; 24978c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src_mult(sw_desc, 24988c2ecf20Sopenharmony_ci mult, src_cnt, dst_cnt - 1); 24998c2ecf20Sopenharmony_ci } 25008c2ecf20Sopenharmony_ci } 25018c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 25028c2ecf20Sopenharmony_ci ppc440spe_desc_set_rxor_block_size(len); 25038c2ecf20Sopenharmony_ci return sw_desc; 25048c2ecf20Sopenharmony_ci} 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci/** 25078c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_pq - prepare CDB (group) for a GF-XOR operation 25088c2ecf20Sopenharmony_ci */ 25098c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq( 25108c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src, 25118c2ecf20Sopenharmony_ci unsigned int src_cnt, const unsigned char *scf, 25128c2ecf20Sopenharmony_ci size_t len, unsigned long flags) 25138c2ecf20Sopenharmony_ci{ 25148c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 25158c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc = NULL; 25168c2ecf20Sopenharmony_ci int dst_cnt = 0; 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id, 25218c2ecf20Sopenharmony_ci dst, src, src_cnt)); 25228c2ecf20Sopenharmony_ci BUG_ON(!len); 25238c2ecf20Sopenharmony_ci BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT); 25248c2ecf20Sopenharmony_ci BUG_ON(!src_cnt); 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci if (src_cnt == 1 && dst[1] == src[0]) { 25278c2ecf20Sopenharmony_ci dma_addr_t dest[2]; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* dst[1] is real destination (Q) */ 25308c2ecf20Sopenharmony_ci dest[0] = dst[1]; 25318c2ecf20Sopenharmony_ci /* this is the page to multicast source data to */ 25328c2ecf20Sopenharmony_ci dest[1] = ppc440spe_chan->qdest; 25338c2ecf20Sopenharmony_ci sw_desc = ppc440spe_dma01_prep_mult(ppc440spe_chan, 25348c2ecf20Sopenharmony_ci dest, 2, src, src_cnt, scf, len, flags); 25358c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci if (src_cnt == 2 && dst[1] == src[1]) { 25398c2ecf20Sopenharmony_ci sw_desc = ppc440spe_dma01_prep_sum_product(ppc440spe_chan, 25408c2ecf20Sopenharmony_ci &dst[1], src, 2, scf, len, flags); 25418c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 25428c2ecf20Sopenharmony_ci } 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (!(flags & DMA_PREP_PQ_DISABLE_P)) { 25458c2ecf20Sopenharmony_ci BUG_ON(!dst[0]); 25468c2ecf20Sopenharmony_ci dst_cnt++; 25478c2ecf20Sopenharmony_ci flags |= DMA_PREP_ZERO_P; 25488c2ecf20Sopenharmony_ci } 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci if (!(flags & DMA_PREP_PQ_DISABLE_Q)) { 25518c2ecf20Sopenharmony_ci BUG_ON(!dst[1]); 25528c2ecf20Sopenharmony_ci dst_cnt++; 25538c2ecf20Sopenharmony_ci flags |= DMA_PREP_ZERO_Q; 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci BUG_ON(!dst_cnt); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 25598c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n", 25608c2ecf20Sopenharmony_ci ppc440spe_chan->device->id, __func__, src_cnt, len, 25618c2ecf20Sopenharmony_ci flags & DMA_PREP_INTERRUPT ? 1 : 0); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci switch (ppc440spe_chan->device->id) { 25648c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 25658c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 25668c2ecf20Sopenharmony_ci sw_desc = ppc440spe_dma01_prep_pq(ppc440spe_chan, 25678c2ecf20Sopenharmony_ci dst, dst_cnt, src, src_cnt, scf, 25688c2ecf20Sopenharmony_ci len, flags); 25698c2ecf20Sopenharmony_ci break; 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 25728c2ecf20Sopenharmony_ci sw_desc = ppc440spe_dma2_prep_pq(ppc440spe_chan, 25738c2ecf20Sopenharmony_ci dst, dst_cnt, src, src_cnt, scf, 25748c2ecf20Sopenharmony_ci len, flags); 25758c2ecf20Sopenharmony_ci break; 25768c2ecf20Sopenharmony_ci } 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 25798c2ecf20Sopenharmony_ci} 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci/** 25828c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_pqzero_sum - prepare CDB group for 25838c2ecf20Sopenharmony_ci * a PQ_ZERO_SUM operation 25848c2ecf20Sopenharmony_ci */ 25858c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pqzero_sum( 25868c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src, 25878c2ecf20Sopenharmony_ci unsigned int src_cnt, const unsigned char *scf, size_t len, 25888c2ecf20Sopenharmony_ci enum sum_check_flags *pqres, unsigned long flags) 25898c2ecf20Sopenharmony_ci{ 25908c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 25918c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *iter; 25928c2ecf20Sopenharmony_ci dma_addr_t pdest, qdest; 25938c2ecf20Sopenharmony_ci int slot_cnt, slots_per_op, idst, dst_cnt; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (flags & DMA_PREP_PQ_DISABLE_P) 25988c2ecf20Sopenharmony_ci pdest = 0; 25998c2ecf20Sopenharmony_ci else 26008c2ecf20Sopenharmony_ci pdest = pq[0]; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci if (flags & DMA_PREP_PQ_DISABLE_Q) 26038c2ecf20Sopenharmony_ci qdest = 0; 26048c2ecf20Sopenharmony_ci else 26058c2ecf20Sopenharmony_ci qdest = pq[1]; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci ADMA_LL_DBG(prep_dma_pqzero_sum_dbg(ppc440spe_chan->device->id, 26088c2ecf20Sopenharmony_ci src, src_cnt, scf)); 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci /* Always use WXOR for P/Q calculations (two destinations). 26118c2ecf20Sopenharmony_ci * Need 1 or 2 extra slots to verify results are zero. 26128c2ecf20Sopenharmony_ci */ 26138c2ecf20Sopenharmony_ci idst = dst_cnt = (pdest && qdest) ? 2 : 1; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci /* One additional slot per destination to clone P/Q 26168c2ecf20Sopenharmony_ci * before calculation (we have to preserve destinations). 26178c2ecf20Sopenharmony_ci */ 26188c2ecf20Sopenharmony_ci slot_cnt = src_cnt + dst_cnt * 2; 26198c2ecf20Sopenharmony_ci slots_per_op = 1; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 26228c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 26238c2ecf20Sopenharmony_ci slots_per_op); 26248c2ecf20Sopenharmony_ci if (sw_desc) { 26258c2ecf20Sopenharmony_ci ppc440spe_desc_init_dma01pqzero_sum(sw_desc, dst_cnt, src_cnt); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci /* Setup byte count for each slot just allocated */ 26288c2ecf20Sopenharmony_ci sw_desc->async_tx.flags = flags; 26298c2ecf20Sopenharmony_ci list_for_each_entry(iter, &sw_desc->group_list, chain_node) { 26308c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, 26318c2ecf20Sopenharmony_ci len); 26328c2ecf20Sopenharmony_ci iter->unmap_len = len; 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (pdest) { 26368c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 26378c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci iter = sw_desc->group_head; 26408c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(iter->async_tx.chan); 26418c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 26428c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 26438c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 26448c2ecf20Sopenharmony_ci chain_node); 26458c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 26468c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 26478c2ecf20Sopenharmony_ci iter->src_cnt = 0; 26488c2ecf20Sopenharmony_ci iter->dst_cnt = 0; 26498c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 0, 26508c2ecf20Sopenharmony_ci ppc440spe_chan->pdest, 0); 26518c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, 0, pdest); 26528c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, 26538c2ecf20Sopenharmony_ci len); 26548c2ecf20Sopenharmony_ci iter->unmap_len = 0; 26558c2ecf20Sopenharmony_ci /* override pdest to preserve original P */ 26568c2ecf20Sopenharmony_ci pdest = ppc440spe_chan->pdest; 26578c2ecf20Sopenharmony_ci } 26588c2ecf20Sopenharmony_ci if (qdest) { 26598c2ecf20Sopenharmony_ci struct dma_cdb *hw_desc; 26608c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci iter = list_first_entry(&sw_desc->group_list, 26638c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 26648c2ecf20Sopenharmony_ci chain_node); 26658c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(iter->async_tx.chan); 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci if (pdest) { 26688c2ecf20Sopenharmony_ci iter = list_entry(iter->chain_node.next, 26698c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 26708c2ecf20Sopenharmony_ci chain_node); 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci memset(iter->hw_desc, 0, sizeof(struct dma_cdb)); 26748c2ecf20Sopenharmony_ci iter->hw_next = list_entry(iter->chain_node.next, 26758c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 26768c2ecf20Sopenharmony_ci chain_node); 26778c2ecf20Sopenharmony_ci hw_desc = iter->hw_desc; 26788c2ecf20Sopenharmony_ci hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2; 26798c2ecf20Sopenharmony_ci iter->src_cnt = 0; 26808c2ecf20Sopenharmony_ci iter->dst_cnt = 0; 26818c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 0, 26828c2ecf20Sopenharmony_ci ppc440spe_chan->qdest, 0); 26838c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, 0, qdest); 26848c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, 26858c2ecf20Sopenharmony_ci len); 26868c2ecf20Sopenharmony_ci iter->unmap_len = 0; 26878c2ecf20Sopenharmony_ci /* override qdest to preserve original Q */ 26888c2ecf20Sopenharmony_ci qdest = ppc440spe_chan->qdest; 26898c2ecf20Sopenharmony_ci } 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci /* Setup destinations for P/Q ops */ 26928c2ecf20Sopenharmony_ci ppc440spe_adma_pqzero_sum_set_dest(sw_desc, pdest, qdest); 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci /* Setup zero QWORDs into DCHECK CDBs */ 26958c2ecf20Sopenharmony_ci idst = dst_cnt; 26968c2ecf20Sopenharmony_ci list_for_each_entry_reverse(iter, &sw_desc->group_list, 26978c2ecf20Sopenharmony_ci chain_node) { 26988c2ecf20Sopenharmony_ci /* 26998c2ecf20Sopenharmony_ci * The last CDB corresponds to Q-parity check, 27008c2ecf20Sopenharmony_ci * the one before last CDB corresponds 27018c2ecf20Sopenharmony_ci * P-parity check 27028c2ecf20Sopenharmony_ci */ 27038c2ecf20Sopenharmony_ci if (idst == DMA_DEST_MAX_NUM) { 27048c2ecf20Sopenharmony_ci if (idst == dst_cnt) { 27058c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_QCHECK, 27068c2ecf20Sopenharmony_ci &iter->flags); 27078c2ecf20Sopenharmony_ci } else { 27088c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_PCHECK, 27098c2ecf20Sopenharmony_ci &iter->flags); 27108c2ecf20Sopenharmony_ci } 27118c2ecf20Sopenharmony_ci } else { 27128c2ecf20Sopenharmony_ci if (qdest) { 27138c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_QCHECK, 27148c2ecf20Sopenharmony_ci &iter->flags); 27158c2ecf20Sopenharmony_ci } else { 27168c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_PCHECK, 27178c2ecf20Sopenharmony_ci &iter->flags); 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci iter->xor_check_result = pqres; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci /* 27238c2ecf20Sopenharmony_ci * set it to zero, if check fail then result will 27248c2ecf20Sopenharmony_ci * be updated 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci *iter->xor_check_result = 0; 27278c2ecf20Sopenharmony_ci ppc440spe_desc_set_dcheck(iter, ppc440spe_chan, 27288c2ecf20Sopenharmony_ci ppc440spe_qword); 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci if (!(--dst_cnt)) 27318c2ecf20Sopenharmony_ci break; 27328c2ecf20Sopenharmony_ci } 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci /* Setup sources and mults for P/Q ops */ 27358c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(iter, &sw_desc->group_list, 27368c2ecf20Sopenharmony_ci chain_node) { 27378c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 27388c2ecf20Sopenharmony_ci u32 mult_dst; 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(iter->async_tx.chan); 27418c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, 27428c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB, 27438c2ecf20Sopenharmony_ci src[src_cnt - 1]); 27448c2ecf20Sopenharmony_ci if (qdest) { 27458c2ecf20Sopenharmony_ci mult_dst = (dst_cnt - 1) ? DMA_CDB_SG_DST2 : 27468c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1; 27478c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, 27488c2ecf20Sopenharmony_ci DMA_CUED_MULT1_OFF, 27498c2ecf20Sopenharmony_ci mult_dst, 27508c2ecf20Sopenharmony_ci scf[src_cnt - 1]); 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci if (!(--src_cnt)) 27538c2ecf20Sopenharmony_ci break; 27548c2ecf20Sopenharmony_ci } 27558c2ecf20Sopenharmony_ci } 27568c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 27578c2ecf20Sopenharmony_ci return sw_desc ? &sw_desc->async_tx : NULL; 27588c2ecf20Sopenharmony_ci} 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci/** 27618c2ecf20Sopenharmony_ci * ppc440spe_adma_prep_dma_xor_zero_sum - prepare CDB group for 27628c2ecf20Sopenharmony_ci * XOR ZERO_SUM operation 27638c2ecf20Sopenharmony_ci */ 27648c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor_zero_sum( 27658c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt, 27668c2ecf20Sopenharmony_ci size_t len, enum sum_check_flags *result, unsigned long flags) 27678c2ecf20Sopenharmony_ci{ 27688c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 27698c2ecf20Sopenharmony_ci dma_addr_t pq[2]; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci /* validate P, disable Q */ 27728c2ecf20Sopenharmony_ci pq[0] = src[0]; 27738c2ecf20Sopenharmony_ci pq[1] = 0; 27748c2ecf20Sopenharmony_ci flags |= DMA_PREP_PQ_DISABLE_Q; 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci tx = ppc440spe_adma_prep_dma_pqzero_sum(chan, pq, &src[1], 27778c2ecf20Sopenharmony_ci src_cnt - 1, 0, len, 27788c2ecf20Sopenharmony_ci result, flags); 27798c2ecf20Sopenharmony_ci return tx; 27808c2ecf20Sopenharmony_ci} 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci/** 27838c2ecf20Sopenharmony_ci * ppc440spe_adma_set_dest - set destination address into descriptor 27848c2ecf20Sopenharmony_ci */ 27858c2ecf20Sopenharmony_cistatic void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *sw_desc, 27868c2ecf20Sopenharmony_ci dma_addr_t addr, int index) 27878c2ecf20Sopenharmony_ci{ 27888c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci BUG_ON(index >= sw_desc->dst_cnt); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci switch (chan->device->id) { 27958c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 27968c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 27978c2ecf20Sopenharmony_ci /* to do: support transfers lengths > 27988c2ecf20Sopenharmony_ci * PPC440SPE_ADMA_DMA/XOR_MAX_BYTE_COUNT 27998c2ecf20Sopenharmony_ci */ 28008c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(sw_desc->group_head, 28018c2ecf20Sopenharmony_ci chan, 0, addr, index); 28028c2ecf20Sopenharmony_ci break; 28038c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 28048c2ecf20Sopenharmony_ci sw_desc = ppc440spe_get_group_entry(sw_desc, index); 28058c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(sw_desc, 28068c2ecf20Sopenharmony_ci chan, 0, addr, index); 28078c2ecf20Sopenharmony_ci break; 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci} 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_cistatic void ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot *iter, 28128c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, dma_addr_t addr) 28138c2ecf20Sopenharmony_ci{ 28148c2ecf20Sopenharmony_ci /* To clear destinations update the descriptor 28158c2ecf20Sopenharmony_ci * (P or Q depending on index) as follows: 28168c2ecf20Sopenharmony_ci * addr is destination (0 corresponds to SG2): 28178c2ecf20Sopenharmony_ci */ 28188c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, addr, 0); 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci /* ... and the addr is source: */ 28218c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, addr); 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci /* addr is always SG2 then the mult is always DST1 */ 28248c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF, 28258c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1, 1); 28268c2ecf20Sopenharmony_ci} 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci/** 28298c2ecf20Sopenharmony_ci * ppc440spe_adma_pq_set_dest - set destination address into descriptor 28308c2ecf20Sopenharmony_ci * for the PQXOR operation 28318c2ecf20Sopenharmony_ci */ 28328c2ecf20Sopenharmony_cistatic void ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *sw_desc, 28338c2ecf20Sopenharmony_ci dma_addr_t *addrs, unsigned long flags) 28348c2ecf20Sopenharmony_ci{ 28358c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter; 28368c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 28378c2ecf20Sopenharmony_ci dma_addr_t paddr, qaddr; 28388c2ecf20Sopenharmony_ci dma_addr_t addr = 0, ppath, qpath; 28398c2ecf20Sopenharmony_ci int index = 0, i; 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (flags & DMA_PREP_PQ_DISABLE_P) 28448c2ecf20Sopenharmony_ci paddr = 0; 28458c2ecf20Sopenharmony_ci else 28468c2ecf20Sopenharmony_ci paddr = addrs[0]; 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci if (flags & DMA_PREP_PQ_DISABLE_Q) 28498c2ecf20Sopenharmony_ci qaddr = 0; 28508c2ecf20Sopenharmony_ci else 28518c2ecf20Sopenharmony_ci qaddr = addrs[1]; 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci if (!paddr || !qaddr) 28548c2ecf20Sopenharmony_ci addr = paddr ? paddr : qaddr; 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci switch (chan->device->id) { 28578c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 28588c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 28598c2ecf20Sopenharmony_ci /* walk through the WXOR source list and set P/Q-destinations 28608c2ecf20Sopenharmony_ci * for each slot: 28618c2ecf20Sopenharmony_ci */ 28628c2ecf20Sopenharmony_ci if (!test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) { 28638c2ecf20Sopenharmony_ci /* This is WXOR-only chain; may have 1/2 zero descs */ 28648c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags)) 28658c2ecf20Sopenharmony_ci index++; 28668c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags)) 28678c2ecf20Sopenharmony_ci index++; 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, index); 28708c2ecf20Sopenharmony_ci if (addr) { 28718c2ecf20Sopenharmony_ci /* one destination */ 28728c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, 28738c2ecf20Sopenharmony_ci &sw_desc->group_list, chain_node) 28748c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 28758c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, addr, 0); 28768c2ecf20Sopenharmony_ci } else { 28778c2ecf20Sopenharmony_ci /* two destinations */ 28788c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, 28798c2ecf20Sopenharmony_ci &sw_desc->group_list, chain_node) { 28808c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 28818c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, paddr, 0); 28828c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 28838c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, qaddr, 1); 28848c2ecf20Sopenharmony_ci } 28858c2ecf20Sopenharmony_ci } 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci if (index) { 28888c2ecf20Sopenharmony_ci /* To clear destinations update the descriptor 28898c2ecf20Sopenharmony_ci * (1st,2nd, or both depending on flags) 28908c2ecf20Sopenharmony_ci */ 28918c2ecf20Sopenharmony_ci index = 0; 28928c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_P, 28938c2ecf20Sopenharmony_ci &sw_desc->flags)) { 28948c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry( 28958c2ecf20Sopenharmony_ci sw_desc, index++); 28968c2ecf20Sopenharmony_ci ppc440spe_adma_pq_zero_op(iter, chan, 28978c2ecf20Sopenharmony_ci paddr); 28988c2ecf20Sopenharmony_ci } 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_Q, 29018c2ecf20Sopenharmony_ci &sw_desc->flags)) { 29028c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry( 29038c2ecf20Sopenharmony_ci sw_desc, index++); 29048c2ecf20Sopenharmony_ci ppc440spe_adma_pq_zero_op(iter, chan, 29058c2ecf20Sopenharmony_ci qaddr); 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci return; 29098c2ecf20Sopenharmony_ci } 29108c2ecf20Sopenharmony_ci } else { 29118c2ecf20Sopenharmony_ci /* This is RXOR-only or RXOR/WXOR mixed chain */ 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci /* If we want to include destination into calculations, 29148c2ecf20Sopenharmony_ci * then make dest addresses cued with mult=1 (XOR). 29158c2ecf20Sopenharmony_ci */ 29168c2ecf20Sopenharmony_ci ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ? 29178c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB : 29188c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE | 29198c2ecf20Sopenharmony_ci (1 << DMA_CUED_MULT1_OFF); 29208c2ecf20Sopenharmony_ci qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ? 29218c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB : 29228c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE | 29238c2ecf20Sopenharmony_ci (1 << DMA_CUED_MULT1_OFF); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci /* Setup destination(s) in RXOR slot(s) */ 29268c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, index++); 29278c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 29288c2ecf20Sopenharmony_ci paddr ? ppath : qpath, 29298c2ecf20Sopenharmony_ci paddr ? paddr : qaddr, 0); 29308c2ecf20Sopenharmony_ci if (!addr) { 29318c2ecf20Sopenharmony_ci /* two destinations */ 29328c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 29338c2ecf20Sopenharmony_ci index++); 29348c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 29358c2ecf20Sopenharmony_ci qpath, qaddr, 0); 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_WXOR, &sw_desc->flags)) { 29398c2ecf20Sopenharmony_ci /* Setup destination(s) in remaining WXOR 29408c2ecf20Sopenharmony_ci * slots 29418c2ecf20Sopenharmony_ci */ 29428c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 29438c2ecf20Sopenharmony_ci index); 29448c2ecf20Sopenharmony_ci if (addr) { 29458c2ecf20Sopenharmony_ci /* one destination */ 29468c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, 29478c2ecf20Sopenharmony_ci &sw_desc->group_list, 29488c2ecf20Sopenharmony_ci chain_node) 29498c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr( 29508c2ecf20Sopenharmony_ci iter, chan, 29518c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, 29528c2ecf20Sopenharmony_ci addr, 0); 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci } else { 29558c2ecf20Sopenharmony_ci /* two destinations */ 29568c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, 29578c2ecf20Sopenharmony_ci &sw_desc->group_list, 29588c2ecf20Sopenharmony_ci chain_node) { 29598c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr( 29608c2ecf20Sopenharmony_ci iter, chan, 29618c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, 29628c2ecf20Sopenharmony_ci paddr, 0); 29638c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr( 29648c2ecf20Sopenharmony_ci iter, chan, 29658c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, 29668c2ecf20Sopenharmony_ci qaddr, 1); 29678c2ecf20Sopenharmony_ci } 29688c2ecf20Sopenharmony_ci } 29698c2ecf20Sopenharmony_ci } 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci break; 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 29758c2ecf20Sopenharmony_ci /* DMA2 descriptors have only 1 destination, so there are 29768c2ecf20Sopenharmony_ci * two chains - one for each dest. 29778c2ecf20Sopenharmony_ci * If we want to include destination into calculations, 29788c2ecf20Sopenharmony_ci * then make dest addresses cued with mult=1 (XOR). 29798c2ecf20Sopenharmony_ci */ 29808c2ecf20Sopenharmony_ci ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ? 29818c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB : 29828c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE | 29838c2ecf20Sopenharmony_ci (1 << DMA_CUED_MULT1_OFF); 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ? 29868c2ecf20Sopenharmony_ci DMA_CUED_XOR_HB : 29878c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE | 29888c2ecf20Sopenharmony_ci (1 << DMA_CUED_MULT1_OFF); 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 0); 29918c2ecf20Sopenharmony_ci for (i = 0; i < sw_desc->descs_per_op; i++) { 29928c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 29938c2ecf20Sopenharmony_ci paddr ? ppath : qpath, 29948c2ecf20Sopenharmony_ci paddr ? paddr : qaddr, 0); 29958c2ecf20Sopenharmony_ci iter = list_entry(iter->chain_node.next, 29968c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 29978c2ecf20Sopenharmony_ci chain_node); 29988c2ecf20Sopenharmony_ci } 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci if (!addr) { 30018c2ecf20Sopenharmony_ci /* Two destinations; setup Q here */ 30028c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 30038c2ecf20Sopenharmony_ci sw_desc->descs_per_op); 30048c2ecf20Sopenharmony_ci for (i = 0; i < sw_desc->descs_per_op; i++) { 30058c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, 30068c2ecf20Sopenharmony_ci chan, qpath, qaddr, 0); 30078c2ecf20Sopenharmony_ci iter = list_entry(iter->chain_node.next, 30088c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 30098c2ecf20Sopenharmony_ci chain_node); 30108c2ecf20Sopenharmony_ci } 30118c2ecf20Sopenharmony_ci } 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci break; 30148c2ecf20Sopenharmony_ci } 30158c2ecf20Sopenharmony_ci} 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci/** 30188c2ecf20Sopenharmony_ci * ppc440spe_adma_pq_zero_sum_set_dest - set destination address into descriptor 30198c2ecf20Sopenharmony_ci * for the PQ_ZERO_SUM operation 30208c2ecf20Sopenharmony_ci */ 30218c2ecf20Sopenharmony_cistatic void ppc440spe_adma_pqzero_sum_set_dest( 30228c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, 30238c2ecf20Sopenharmony_ci dma_addr_t paddr, dma_addr_t qaddr) 30248c2ecf20Sopenharmony_ci{ 30258c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter, *end; 30268c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 30278c2ecf20Sopenharmony_ci dma_addr_t addr = 0; 30288c2ecf20Sopenharmony_ci int idx; 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci /* walk through the WXOR source list and set P/Q-destinations 30338c2ecf20Sopenharmony_ci * for each slot 30348c2ecf20Sopenharmony_ci */ 30358c2ecf20Sopenharmony_ci idx = (paddr && qaddr) ? 2 : 1; 30368c2ecf20Sopenharmony_ci /* set end */ 30378c2ecf20Sopenharmony_ci list_for_each_entry_reverse(end, &sw_desc->group_list, 30388c2ecf20Sopenharmony_ci chain_node) { 30398c2ecf20Sopenharmony_ci if (!(--idx)) 30408c2ecf20Sopenharmony_ci break; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci /* set start */ 30438c2ecf20Sopenharmony_ci idx = (paddr && qaddr) ? 2 : 1; 30448c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, idx); 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci if (paddr && qaddr) { 30478c2ecf20Sopenharmony_ci /* two destinations */ 30488c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &sw_desc->group_list, 30498c2ecf20Sopenharmony_ci chain_node) { 30508c2ecf20Sopenharmony_ci if (unlikely(iter == end)) 30518c2ecf20Sopenharmony_ci break; 30528c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 30538c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, paddr, 0); 30548c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 30558c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, qaddr, 1); 30568c2ecf20Sopenharmony_ci } 30578c2ecf20Sopenharmony_ci } else { 30588c2ecf20Sopenharmony_ci /* one destination */ 30598c2ecf20Sopenharmony_ci addr = paddr ? paddr : qaddr; 30608c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &sw_desc->group_list, 30618c2ecf20Sopenharmony_ci chain_node) { 30628c2ecf20Sopenharmony_ci if (unlikely(iter == end)) 30638c2ecf20Sopenharmony_ci break; 30648c2ecf20Sopenharmony_ci ppc440spe_desc_set_dest_addr(iter, chan, 30658c2ecf20Sopenharmony_ci DMA_CUED_XOR_BASE, addr, 0); 30668c2ecf20Sopenharmony_ci } 30678c2ecf20Sopenharmony_ci } 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci /* The remaining descriptors are DATACHECK. These have no need in 30708c2ecf20Sopenharmony_ci * destination. Actually, these destinations are used there 30718c2ecf20Sopenharmony_ci * as sources for check operation. So, set addr as source. 30728c2ecf20Sopenharmony_ci */ 30738c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(end, chan, 0, 0, addr ? addr : paddr); 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci if (!addr) { 30768c2ecf20Sopenharmony_ci end = list_entry(end->chain_node.next, 30778c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 30788c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(end, chan, 0, 0, qaddr); 30798c2ecf20Sopenharmony_ci } 30808c2ecf20Sopenharmony_ci} 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci/** 30838c2ecf20Sopenharmony_ci * ppc440spe_desc_set_xor_src_cnt - set source count into descriptor 30848c2ecf20Sopenharmony_ci */ 30858c2ecf20Sopenharmony_cistatic inline void ppc440spe_desc_set_xor_src_cnt( 30868c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 30878c2ecf20Sopenharmony_ci int src_cnt) 30888c2ecf20Sopenharmony_ci{ 30898c2ecf20Sopenharmony_ci struct xor_cb *hw_desc = desc->hw_desc; 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci hw_desc->cbc &= ~XOR_CDCR_OAC_MSK; 30928c2ecf20Sopenharmony_ci hw_desc->cbc |= src_cnt; 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci/** 30968c2ecf20Sopenharmony_ci * ppc440spe_adma_pq_set_src - set source address into descriptor 30978c2ecf20Sopenharmony_ci */ 30988c2ecf20Sopenharmony_cistatic void ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *sw_desc, 30998c2ecf20Sopenharmony_ci dma_addr_t addr, int index) 31008c2ecf20Sopenharmony_ci{ 31018c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 31028c2ecf20Sopenharmony_ci dma_addr_t haddr = 0; 31038c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter = NULL; 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci switch (chan->device->id) { 31088c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 31098c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 31108c2ecf20Sopenharmony_ci /* DMA0,1 may do: WXOR, RXOR, RXOR+WXORs chain 31118c2ecf20Sopenharmony_ci */ 31128c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) { 31138c2ecf20Sopenharmony_ci /* RXOR-only or RXOR/WXOR operation */ 31148c2ecf20Sopenharmony_ci int iskip = test_bit(PPC440SPE_DESC_RXOR12, 31158c2ecf20Sopenharmony_ci &sw_desc->flags) ? 2 : 3; 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_ci if (index == 0) { 31188c2ecf20Sopenharmony_ci /* 1st slot (RXOR) */ 31198c2ecf20Sopenharmony_ci /* setup sources region (R1-2-3, R1-2-4, 31208c2ecf20Sopenharmony_ci * or R1-2-5) 31218c2ecf20Sopenharmony_ci */ 31228c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_RXOR12, 31238c2ecf20Sopenharmony_ci &sw_desc->flags)) 31248c2ecf20Sopenharmony_ci haddr = DMA_RXOR12 << 31258c2ecf20Sopenharmony_ci DMA_CUED_REGION_OFF; 31268c2ecf20Sopenharmony_ci else if (test_bit(PPC440SPE_DESC_RXOR123, 31278c2ecf20Sopenharmony_ci &sw_desc->flags)) 31288c2ecf20Sopenharmony_ci haddr = DMA_RXOR123 << 31298c2ecf20Sopenharmony_ci DMA_CUED_REGION_OFF; 31308c2ecf20Sopenharmony_ci else if (test_bit(PPC440SPE_DESC_RXOR124, 31318c2ecf20Sopenharmony_ci &sw_desc->flags)) 31328c2ecf20Sopenharmony_ci haddr = DMA_RXOR124 << 31338c2ecf20Sopenharmony_ci DMA_CUED_REGION_OFF; 31348c2ecf20Sopenharmony_ci else if (test_bit(PPC440SPE_DESC_RXOR125, 31358c2ecf20Sopenharmony_ci &sw_desc->flags)) 31368c2ecf20Sopenharmony_ci haddr = DMA_RXOR125 << 31378c2ecf20Sopenharmony_ci DMA_CUED_REGION_OFF; 31388c2ecf20Sopenharmony_ci else 31398c2ecf20Sopenharmony_ci BUG(); 31408c2ecf20Sopenharmony_ci haddr |= DMA_CUED_XOR_BASE; 31418c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 0); 31428c2ecf20Sopenharmony_ci } else if (index < iskip) { 31438c2ecf20Sopenharmony_ci /* 1st slot (RXOR) 31448c2ecf20Sopenharmony_ci * shall actually set source address only once 31458c2ecf20Sopenharmony_ci * instead of first <iskip> 31468c2ecf20Sopenharmony_ci */ 31478c2ecf20Sopenharmony_ci iter = NULL; 31488c2ecf20Sopenharmony_ci } else { 31498c2ecf20Sopenharmony_ci /* 2nd/3d and next slots (WXOR); 31508c2ecf20Sopenharmony_ci * skip first slot with RXOR 31518c2ecf20Sopenharmony_ci */ 31528c2ecf20Sopenharmony_ci haddr = DMA_CUED_XOR_HB; 31538c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 31548c2ecf20Sopenharmony_ci index - iskip + sw_desc->dst_cnt); 31558c2ecf20Sopenharmony_ci } 31568c2ecf20Sopenharmony_ci } else { 31578c2ecf20Sopenharmony_ci int znum = 0; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci /* WXOR-only operation; skip first slots with 31608c2ecf20Sopenharmony_ci * zeroing destinations 31618c2ecf20Sopenharmony_ci */ 31628c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags)) 31638c2ecf20Sopenharmony_ci znum++; 31648c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags)) 31658c2ecf20Sopenharmony_ci znum++; 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci haddr = DMA_CUED_XOR_HB; 31688c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 31698c2ecf20Sopenharmony_ci index + znum); 31708c2ecf20Sopenharmony_ci } 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci if (likely(iter)) { 31738c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, haddr, addr); 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci if (!index && 31768c2ecf20Sopenharmony_ci test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags) && 31778c2ecf20Sopenharmony_ci sw_desc->dst_cnt == 2) { 31788c2ecf20Sopenharmony_ci /* if we have two destinations for RXOR, then 31798c2ecf20Sopenharmony_ci * setup source in the second descr too 31808c2ecf20Sopenharmony_ci */ 31818c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 1); 31828c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(iter, chan, 0, 31838c2ecf20Sopenharmony_ci haddr, addr); 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci } 31868c2ecf20Sopenharmony_ci break; 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 31898c2ecf20Sopenharmony_ci /* DMA2 may do Biskup */ 31908c2ecf20Sopenharmony_ci iter = sw_desc->group_head; 31918c2ecf20Sopenharmony_ci if (iter->dst_cnt == 2) { 31928c2ecf20Sopenharmony_ci /* both P & Q calculations required; set P src here */ 31938c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_set_src(iter, index, addr); 31948c2ecf20Sopenharmony_ci 31958c2ecf20Sopenharmony_ci /* this is for Q */ 31968c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 31978c2ecf20Sopenharmony_ci sw_desc->descs_per_op); 31988c2ecf20Sopenharmony_ci } 31998c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_set_src(iter, index, addr); 32008c2ecf20Sopenharmony_ci break; 32018c2ecf20Sopenharmony_ci } 32028c2ecf20Sopenharmony_ci} 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci/** 32058c2ecf20Sopenharmony_ci * ppc440spe_adma_memcpy_xor_set_src - set source address into descriptor 32068c2ecf20Sopenharmony_ci */ 32078c2ecf20Sopenharmony_cistatic void ppc440spe_adma_memcpy_xor_set_src( 32088c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, 32098c2ecf20Sopenharmony_ci dma_addr_t addr, int index) 32108c2ecf20Sopenharmony_ci{ 32118c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 32148c2ecf20Sopenharmony_ci sw_desc = sw_desc->group_head; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci if (likely(sw_desc)) 32178c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_addr(sw_desc, chan, index, 0, addr); 32188c2ecf20Sopenharmony_ci} 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_ci/** 32218c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_inc_addr - 32228c2ecf20Sopenharmony_ci */ 32238c2ecf20Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_inc_addr( 32248c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 32258c2ecf20Sopenharmony_ci struct ppc440spe_rxor *cursor, int index, int src_cnt) 32268c2ecf20Sopenharmony_ci{ 32278c2ecf20Sopenharmony_ci cursor->addr_count++; 32288c2ecf20Sopenharmony_ci if (index == src_cnt - 1) { 32298c2ecf20Sopenharmony_ci ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count); 32308c2ecf20Sopenharmony_ci } else if (cursor->addr_count == XOR_MAX_OPS) { 32318c2ecf20Sopenharmony_ci ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count); 32328c2ecf20Sopenharmony_ci cursor->addr_count = 0; 32338c2ecf20Sopenharmony_ci cursor->desc_count++; 32348c2ecf20Sopenharmony_ci } 32358c2ecf20Sopenharmony_ci} 32368c2ecf20Sopenharmony_ci 32378c2ecf20Sopenharmony_ci/** 32388c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_prep_src - setup RXOR types in DMA2 CDB 32398c2ecf20Sopenharmony_ci */ 32408c2ecf20Sopenharmony_cistatic int ppc440spe_adma_dma2rxor_prep_src( 32418c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *hdesc, 32428c2ecf20Sopenharmony_ci struct ppc440spe_rxor *cursor, int index, 32438c2ecf20Sopenharmony_ci int src_cnt, u32 addr) 32448c2ecf20Sopenharmony_ci{ 32458c2ecf20Sopenharmony_ci int rval = 0; 32468c2ecf20Sopenharmony_ci u32 sign; 32478c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc = hdesc; 32488c2ecf20Sopenharmony_ci int i; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci for (i = 0; i < cursor->desc_count; i++) { 32518c2ecf20Sopenharmony_ci desc = list_entry(hdesc->chain_node.next, 32528c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 32538c2ecf20Sopenharmony_ci chain_node); 32548c2ecf20Sopenharmony_ci } 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci switch (cursor->state) { 32578c2ecf20Sopenharmony_ci case 0: 32588c2ecf20Sopenharmony_ci if (addr == cursor->addrl + cursor->len) { 32598c2ecf20Sopenharmony_ci /* direct RXOR */ 32608c2ecf20Sopenharmony_ci cursor->state = 1; 32618c2ecf20Sopenharmony_ci cursor->xor_count++; 32628c2ecf20Sopenharmony_ci if (index == src_cnt-1) { 32638c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 32648c2ecf20Sopenharmony_ci cursor->addr_count, 32658c2ecf20Sopenharmony_ci DMA_RXOR12 << DMA_CUED_REGION_OFF); 32668c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 32678c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 32688c2ecf20Sopenharmony_ci } 32698c2ecf20Sopenharmony_ci } else if (cursor->addrl == addr + cursor->len) { 32708c2ecf20Sopenharmony_ci /* reverse RXOR */ 32718c2ecf20Sopenharmony_ci cursor->state = 1; 32728c2ecf20Sopenharmony_ci cursor->xor_count++; 32738c2ecf20Sopenharmony_ci set_bit(cursor->addr_count, &desc->reverse_flags[0]); 32748c2ecf20Sopenharmony_ci if (index == src_cnt-1) { 32758c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 32768c2ecf20Sopenharmony_ci cursor->addr_count, 32778c2ecf20Sopenharmony_ci DMA_RXOR12 << DMA_CUED_REGION_OFF); 32788c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 32798c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 32808c2ecf20Sopenharmony_ci } 32818c2ecf20Sopenharmony_ci } else { 32828c2ecf20Sopenharmony_ci printk(KERN_ERR "Cannot build " 32838c2ecf20Sopenharmony_ci "DMA2 RXOR command block.\n"); 32848c2ecf20Sopenharmony_ci BUG(); 32858c2ecf20Sopenharmony_ci } 32868c2ecf20Sopenharmony_ci break; 32878c2ecf20Sopenharmony_ci case 1: 32888c2ecf20Sopenharmony_ci sign = test_bit(cursor->addr_count, 32898c2ecf20Sopenharmony_ci desc->reverse_flags) 32908c2ecf20Sopenharmony_ci ? -1 : 1; 32918c2ecf20Sopenharmony_ci if (index == src_cnt-2 || (sign == -1 32928c2ecf20Sopenharmony_ci && addr != cursor->addrl - 2*cursor->len)) { 32938c2ecf20Sopenharmony_ci cursor->state = 0; 32948c2ecf20Sopenharmony_ci cursor->xor_count = 1; 32958c2ecf20Sopenharmony_ci cursor->addrl = addr; 32968c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 32978c2ecf20Sopenharmony_ci cursor->addr_count, 32988c2ecf20Sopenharmony_ci DMA_RXOR12 << DMA_CUED_REGION_OFF); 32998c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33008c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33018c2ecf20Sopenharmony_ci } else if (addr == cursor->addrl + 2*sign*cursor->len) { 33028c2ecf20Sopenharmony_ci cursor->state = 2; 33038c2ecf20Sopenharmony_ci cursor->xor_count = 0; 33048c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 33058c2ecf20Sopenharmony_ci cursor->addr_count, 33068c2ecf20Sopenharmony_ci DMA_RXOR123 << DMA_CUED_REGION_OFF); 33078c2ecf20Sopenharmony_ci if (index == src_cnt-1) { 33088c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33098c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33108c2ecf20Sopenharmony_ci } 33118c2ecf20Sopenharmony_ci } else if (addr == cursor->addrl + 3*cursor->len) { 33128c2ecf20Sopenharmony_ci cursor->state = 2; 33138c2ecf20Sopenharmony_ci cursor->xor_count = 0; 33148c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 33158c2ecf20Sopenharmony_ci cursor->addr_count, 33168c2ecf20Sopenharmony_ci DMA_RXOR124 << DMA_CUED_REGION_OFF); 33178c2ecf20Sopenharmony_ci if (index == src_cnt-1) { 33188c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33198c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33208c2ecf20Sopenharmony_ci } 33218c2ecf20Sopenharmony_ci } else if (addr == cursor->addrl + 4*cursor->len) { 33228c2ecf20Sopenharmony_ci cursor->state = 2; 33238c2ecf20Sopenharmony_ci cursor->xor_count = 0; 33248c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 33258c2ecf20Sopenharmony_ci cursor->addr_count, 33268c2ecf20Sopenharmony_ci DMA_RXOR125 << DMA_CUED_REGION_OFF); 33278c2ecf20Sopenharmony_ci if (index == src_cnt-1) { 33288c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33298c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33308c2ecf20Sopenharmony_ci } 33318c2ecf20Sopenharmony_ci } else { 33328c2ecf20Sopenharmony_ci cursor->state = 0; 33338c2ecf20Sopenharmony_ci cursor->xor_count = 1; 33348c2ecf20Sopenharmony_ci cursor->addrl = addr; 33358c2ecf20Sopenharmony_ci ppc440spe_rxor_set_region(desc, 33368c2ecf20Sopenharmony_ci cursor->addr_count, 33378c2ecf20Sopenharmony_ci DMA_RXOR12 << DMA_CUED_REGION_OFF); 33388c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33398c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33408c2ecf20Sopenharmony_ci } 33418c2ecf20Sopenharmony_ci break; 33428c2ecf20Sopenharmony_ci case 2: 33438c2ecf20Sopenharmony_ci cursor->state = 0; 33448c2ecf20Sopenharmony_ci cursor->addrl = addr; 33458c2ecf20Sopenharmony_ci cursor->xor_count++; 33468c2ecf20Sopenharmony_ci if (index) { 33478c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_inc_addr( 33488c2ecf20Sopenharmony_ci desc, cursor, index, src_cnt); 33498c2ecf20Sopenharmony_ci } 33508c2ecf20Sopenharmony_ci break; 33518c2ecf20Sopenharmony_ci } 33528c2ecf20Sopenharmony_ci 33538c2ecf20Sopenharmony_ci return rval; 33548c2ecf20Sopenharmony_ci} 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci/** 33578c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_set_src - set RXOR source address; it's assumed that 33588c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_prep_src() has already done prior this call 33598c2ecf20Sopenharmony_ci */ 33608c2ecf20Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_src( 33618c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 33628c2ecf20Sopenharmony_ci int index, dma_addr_t addr) 33638c2ecf20Sopenharmony_ci{ 33648c2ecf20Sopenharmony_ci struct xor_cb *xcb = desc->hw_desc; 33658c2ecf20Sopenharmony_ci int k = 0, op = 0, lop = 0; 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci /* get the RXOR operand which corresponds to index addr */ 33688c2ecf20Sopenharmony_ci while (op <= index) { 33698c2ecf20Sopenharmony_ci lop = op; 33708c2ecf20Sopenharmony_ci if (k == XOR_MAX_OPS) { 33718c2ecf20Sopenharmony_ci k = 0; 33728c2ecf20Sopenharmony_ci desc = list_entry(desc->chain_node.next, 33738c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, chain_node); 33748c2ecf20Sopenharmony_ci xcb = desc->hw_desc; 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci } 33778c2ecf20Sopenharmony_ci if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) == 33788c2ecf20Sopenharmony_ci (DMA_RXOR12 << DMA_CUED_REGION_OFF)) 33798c2ecf20Sopenharmony_ci op += 2; 33808c2ecf20Sopenharmony_ci else 33818c2ecf20Sopenharmony_ci op += 3; 33828c2ecf20Sopenharmony_ci } 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci BUG_ON(k < 1); 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci if (test_bit(k-1, desc->reverse_flags)) { 33878c2ecf20Sopenharmony_ci /* reverse operand order; put last op in RXOR group */ 33888c2ecf20Sopenharmony_ci if (index == op - 1) 33898c2ecf20Sopenharmony_ci ppc440spe_rxor_set_src(desc, k - 1, addr); 33908c2ecf20Sopenharmony_ci } else { 33918c2ecf20Sopenharmony_ci /* direct operand order; put first op in RXOR group */ 33928c2ecf20Sopenharmony_ci if (index == lop) 33938c2ecf20Sopenharmony_ci ppc440spe_rxor_set_src(desc, k - 1, addr); 33948c2ecf20Sopenharmony_ci } 33958c2ecf20Sopenharmony_ci} 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci/** 33988c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_set_mult - set RXOR multipliers; it's assumed that 33998c2ecf20Sopenharmony_ci * ppc440spe_adma_dma2rxor_prep_src() has already done prior this call 34008c2ecf20Sopenharmony_ci */ 34018c2ecf20Sopenharmony_cistatic void ppc440spe_adma_dma2rxor_set_mult( 34028c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *desc, 34038c2ecf20Sopenharmony_ci int index, u8 mult) 34048c2ecf20Sopenharmony_ci{ 34058c2ecf20Sopenharmony_ci struct xor_cb *xcb = desc->hw_desc; 34068c2ecf20Sopenharmony_ci int k = 0, op = 0, lop = 0; 34078c2ecf20Sopenharmony_ci 34088c2ecf20Sopenharmony_ci /* get the RXOR operand which corresponds to index mult */ 34098c2ecf20Sopenharmony_ci while (op <= index) { 34108c2ecf20Sopenharmony_ci lop = op; 34118c2ecf20Sopenharmony_ci if (k == XOR_MAX_OPS) { 34128c2ecf20Sopenharmony_ci k = 0; 34138c2ecf20Sopenharmony_ci desc = list_entry(desc->chain_node.next, 34148c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot, 34158c2ecf20Sopenharmony_ci chain_node); 34168c2ecf20Sopenharmony_ci xcb = desc->hw_desc; 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci } 34198c2ecf20Sopenharmony_ci if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) == 34208c2ecf20Sopenharmony_ci (DMA_RXOR12 << DMA_CUED_REGION_OFF)) 34218c2ecf20Sopenharmony_ci op += 2; 34228c2ecf20Sopenharmony_ci else 34238c2ecf20Sopenharmony_ci op += 3; 34248c2ecf20Sopenharmony_ci } 34258c2ecf20Sopenharmony_ci 34268c2ecf20Sopenharmony_ci BUG_ON(k < 1); 34278c2ecf20Sopenharmony_ci if (test_bit(k-1, desc->reverse_flags)) { 34288c2ecf20Sopenharmony_ci /* reverse order */ 34298c2ecf20Sopenharmony_ci ppc440spe_rxor_set_mult(desc, k - 1, op - index - 1, mult); 34308c2ecf20Sopenharmony_ci } else { 34318c2ecf20Sopenharmony_ci /* direct order */ 34328c2ecf20Sopenharmony_ci ppc440spe_rxor_set_mult(desc, k - 1, index - lop, mult); 34338c2ecf20Sopenharmony_ci } 34348c2ecf20Sopenharmony_ci} 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci/** 34378c2ecf20Sopenharmony_ci * ppc440spe_init_rxor_cursor - 34388c2ecf20Sopenharmony_ci */ 34398c2ecf20Sopenharmony_cistatic void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor) 34408c2ecf20Sopenharmony_ci{ 34418c2ecf20Sopenharmony_ci memset(cursor, 0, sizeof(struct ppc440spe_rxor)); 34428c2ecf20Sopenharmony_ci cursor->state = 2; 34438c2ecf20Sopenharmony_ci} 34448c2ecf20Sopenharmony_ci 34458c2ecf20Sopenharmony_ci/** 34468c2ecf20Sopenharmony_ci * ppc440spe_adma_pq_set_src_mult - set multiplication coefficient into 34478c2ecf20Sopenharmony_ci * descriptor for the PQXOR operation 34488c2ecf20Sopenharmony_ci */ 34498c2ecf20Sopenharmony_cistatic void ppc440spe_adma_pq_set_src_mult( 34508c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, 34518c2ecf20Sopenharmony_ci unsigned char mult, int index, int dst_pos) 34528c2ecf20Sopenharmony_ci{ 34538c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 34548c2ecf20Sopenharmony_ci u32 mult_idx, mult_dst; 34558c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter = NULL, *iter1 = NULL; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci switch (chan->device->id) { 34608c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 34618c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 34628c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) { 34638c2ecf20Sopenharmony_ci int region = test_bit(PPC440SPE_DESC_RXOR12, 34648c2ecf20Sopenharmony_ci &sw_desc->flags) ? 2 : 3; 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci if (index < region) { 34678c2ecf20Sopenharmony_ci /* RXOR multipliers */ 34688c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 34698c2ecf20Sopenharmony_ci sw_desc->dst_cnt - 1); 34708c2ecf20Sopenharmony_ci if (sw_desc->dst_cnt == 2) 34718c2ecf20Sopenharmony_ci iter1 = ppc440spe_get_group_entry( 34728c2ecf20Sopenharmony_ci sw_desc, 0); 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci mult_idx = DMA_CUED_MULT1_OFF + (index << 3); 34758c2ecf20Sopenharmony_ci mult_dst = DMA_CDB_SG_SRC; 34768c2ecf20Sopenharmony_ci } else { 34778c2ecf20Sopenharmony_ci /* WXOR multiplier */ 34788c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 34798c2ecf20Sopenharmony_ci index - region + 34808c2ecf20Sopenharmony_ci sw_desc->dst_cnt); 34818c2ecf20Sopenharmony_ci mult_idx = DMA_CUED_MULT1_OFF; 34828c2ecf20Sopenharmony_ci mult_dst = dst_pos ? DMA_CDB_SG_DST2 : 34838c2ecf20Sopenharmony_ci DMA_CDB_SG_DST1; 34848c2ecf20Sopenharmony_ci } 34858c2ecf20Sopenharmony_ci } else { 34868c2ecf20Sopenharmony_ci int znum = 0; 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_ci /* WXOR-only; 34898c2ecf20Sopenharmony_ci * skip first slots with destinations (if ZERO_DST has 34908c2ecf20Sopenharmony_ci * place) 34918c2ecf20Sopenharmony_ci */ 34928c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags)) 34938c2ecf20Sopenharmony_ci znum++; 34948c2ecf20Sopenharmony_ci if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags)) 34958c2ecf20Sopenharmony_ci znum++; 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, index + znum); 34988c2ecf20Sopenharmony_ci mult_idx = DMA_CUED_MULT1_OFF; 34998c2ecf20Sopenharmony_ci mult_dst = dst_pos ? DMA_CDB_SG_DST2 : DMA_CDB_SG_DST1; 35008c2ecf20Sopenharmony_ci } 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci if (likely(iter)) { 35038c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter, chan, 35048c2ecf20Sopenharmony_ci mult_idx, mult_dst, mult); 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci if (unlikely(iter1)) { 35078c2ecf20Sopenharmony_ci /* if we have two destinations for RXOR, then 35088c2ecf20Sopenharmony_ci * we've just set Q mult. Set-up P now. 35098c2ecf20Sopenharmony_ci */ 35108c2ecf20Sopenharmony_ci ppc440spe_desc_set_src_mult(iter1, chan, 35118c2ecf20Sopenharmony_ci mult_idx, mult_dst, 1); 35128c2ecf20Sopenharmony_ci } 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci } 35158c2ecf20Sopenharmony_ci break; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 35188c2ecf20Sopenharmony_ci iter = sw_desc->group_head; 35198c2ecf20Sopenharmony_ci if (sw_desc->dst_cnt == 2) { 35208c2ecf20Sopenharmony_ci /* both P & Q calculations required; set P mult here */ 35218c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_set_mult(iter, index, 1); 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci /* and then set Q mult */ 35248c2ecf20Sopenharmony_ci iter = ppc440spe_get_group_entry(sw_desc, 35258c2ecf20Sopenharmony_ci sw_desc->descs_per_op); 35268c2ecf20Sopenharmony_ci } 35278c2ecf20Sopenharmony_ci ppc440spe_adma_dma2rxor_set_mult(iter, index, mult); 35288c2ecf20Sopenharmony_ci break; 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci} 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci/** 35338c2ecf20Sopenharmony_ci * ppc440spe_adma_free_chan_resources - free the resources allocated 35348c2ecf20Sopenharmony_ci */ 35358c2ecf20Sopenharmony_cistatic void ppc440spe_adma_free_chan_resources(struct dma_chan *chan) 35368c2ecf20Sopenharmony_ci{ 35378c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 35388c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *iter, *_iter; 35398c2ecf20Sopenharmony_ci int in_use_descs = 0; 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 35428c2ecf20Sopenharmony_ci ppc440spe_adma_slot_cleanup(ppc440spe_chan); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci spin_lock_bh(&ppc440spe_chan->lock); 35458c2ecf20Sopenharmony_ci list_for_each_entry_safe(iter, _iter, &ppc440spe_chan->chain, 35468c2ecf20Sopenharmony_ci chain_node) { 35478c2ecf20Sopenharmony_ci in_use_descs++; 35488c2ecf20Sopenharmony_ci list_del(&iter->chain_node); 35498c2ecf20Sopenharmony_ci } 35508c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(iter, _iter, 35518c2ecf20Sopenharmony_ci &ppc440spe_chan->all_slots, slot_node) { 35528c2ecf20Sopenharmony_ci list_del(&iter->slot_node); 35538c2ecf20Sopenharmony_ci kfree(iter); 35548c2ecf20Sopenharmony_ci ppc440spe_chan->slots_allocated--; 35558c2ecf20Sopenharmony_ci } 35568c2ecf20Sopenharmony_ci ppc440spe_chan->last_used = NULL; 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 35598c2ecf20Sopenharmony_ci "ppc440spe adma%d %s slots_allocated %d\n", 35608c2ecf20Sopenharmony_ci ppc440spe_chan->device->id, 35618c2ecf20Sopenharmony_ci __func__, ppc440spe_chan->slots_allocated); 35628c2ecf20Sopenharmony_ci spin_unlock_bh(&ppc440spe_chan->lock); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci /* one is ok since we left it on there on purpose */ 35658c2ecf20Sopenharmony_ci if (in_use_descs > 1) 35668c2ecf20Sopenharmony_ci printk(KERN_ERR "SPE: Freeing %d in use descriptors!\n", 35678c2ecf20Sopenharmony_ci in_use_descs - 1); 35688c2ecf20Sopenharmony_ci} 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci/** 35718c2ecf20Sopenharmony_ci * ppc440spe_adma_tx_status - poll the status of an ADMA transaction 35728c2ecf20Sopenharmony_ci * @chan: ADMA channel handle 35738c2ecf20Sopenharmony_ci * @cookie: ADMA transaction identifier 35748c2ecf20Sopenharmony_ci * @txstate: a holder for the current state of the channel 35758c2ecf20Sopenharmony_ci */ 35768c2ecf20Sopenharmony_cistatic enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan, 35778c2ecf20Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 35788c2ecf20Sopenharmony_ci{ 35798c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 35808c2ecf20Sopenharmony_ci enum dma_status ret; 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 35838c2ecf20Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 35848c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 35858c2ecf20Sopenharmony_ci return ret; 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci ppc440spe_adma_slot_cleanup(ppc440spe_chan); 35888c2ecf20Sopenharmony_ci 35898c2ecf20Sopenharmony_ci return dma_cookie_status(chan, cookie, txstate); 35908c2ecf20Sopenharmony_ci} 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci/** 35938c2ecf20Sopenharmony_ci * ppc440spe_adma_eot_handler - end of transfer interrupt handler 35948c2ecf20Sopenharmony_ci */ 35958c2ecf20Sopenharmony_cistatic irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data) 35968c2ecf20Sopenharmony_ci{ 35978c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan = data; 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, 36008c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s\n", chan->device->id, __func__); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci tasklet_schedule(&chan->irq_tasklet); 36038c2ecf20Sopenharmony_ci ppc440spe_adma_device_clear_eot_status(chan); 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 36068c2ecf20Sopenharmony_ci} 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci/** 36098c2ecf20Sopenharmony_ci * ppc440spe_adma_err_handler - DMA error interrupt handler; 36108c2ecf20Sopenharmony_ci * do the same things as a eot handler 36118c2ecf20Sopenharmony_ci */ 36128c2ecf20Sopenharmony_cistatic irqreturn_t ppc440spe_adma_err_handler(int irq, void *data) 36138c2ecf20Sopenharmony_ci{ 36148c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan = data; 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, 36178c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s\n", chan->device->id, __func__); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci tasklet_schedule(&chan->irq_tasklet); 36208c2ecf20Sopenharmony_ci ppc440spe_adma_device_clear_eot_status(chan); 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 36238c2ecf20Sopenharmony_ci} 36248c2ecf20Sopenharmony_ci 36258c2ecf20Sopenharmony_ci/** 36268c2ecf20Sopenharmony_ci * ppc440spe_test_callback - called when test operation has been done 36278c2ecf20Sopenharmony_ci */ 36288c2ecf20Sopenharmony_cistatic void ppc440spe_test_callback(void *unused) 36298c2ecf20Sopenharmony_ci{ 36308c2ecf20Sopenharmony_ci complete(&ppc440spe_r6_test_comp); 36318c2ecf20Sopenharmony_ci} 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci/** 36348c2ecf20Sopenharmony_ci * ppc440spe_adma_issue_pending - flush all pending descriptors to h/w 36358c2ecf20Sopenharmony_ci */ 36368c2ecf20Sopenharmony_cistatic void ppc440spe_adma_issue_pending(struct dma_chan *chan) 36378c2ecf20Sopenharmony_ci{ 36388c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 36418c2ecf20Sopenharmony_ci dev_dbg(ppc440spe_chan->device->common.dev, 36428c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s %d \n", ppc440spe_chan->device->id, 36438c2ecf20Sopenharmony_ci __func__, ppc440spe_chan->pending); 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci if (ppc440spe_chan->pending) { 36468c2ecf20Sopenharmony_ci ppc440spe_chan->pending = 0; 36478c2ecf20Sopenharmony_ci ppc440spe_chan_append(ppc440spe_chan); 36488c2ecf20Sopenharmony_ci } 36498c2ecf20Sopenharmony_ci} 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ci/** 36528c2ecf20Sopenharmony_ci * ppc440spe_chan_start_null_xor - initiate the first XOR operation (DMA engines 36538c2ecf20Sopenharmony_ci * use FIFOs (as opposite to chains used in XOR) so this is a XOR 36548c2ecf20Sopenharmony_ci * specific operation) 36558c2ecf20Sopenharmony_ci */ 36568c2ecf20Sopenharmony_cistatic void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan) 36578c2ecf20Sopenharmony_ci{ 36588c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *group_start; 36598c2ecf20Sopenharmony_ci dma_cookie_t cookie; 36608c2ecf20Sopenharmony_ci int slot_cnt, slots_per_op; 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ci dev_dbg(chan->device->common.dev, 36638c2ecf20Sopenharmony_ci "ppc440spe adma%d: %s\n", chan->device->id, __func__); 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci spin_lock_bh(&chan->lock); 36668c2ecf20Sopenharmony_ci slot_cnt = ppc440spe_chan_xor_slot_count(0, 2, &slots_per_op); 36678c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(chan, slot_cnt, slots_per_op); 36688c2ecf20Sopenharmony_ci if (sw_desc) { 36698c2ecf20Sopenharmony_ci group_start = sw_desc->group_head; 36708c2ecf20Sopenharmony_ci list_splice_init(&sw_desc->group_list, &chan->chain); 36718c2ecf20Sopenharmony_ci async_tx_ack(&sw_desc->async_tx); 36728c2ecf20Sopenharmony_ci ppc440spe_desc_init_null_xor(group_start); 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(&sw_desc->async_tx); 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci /* initialize the completed cookie to be less than 36778c2ecf20Sopenharmony_ci * the most recently used cookie 36788c2ecf20Sopenharmony_ci */ 36798c2ecf20Sopenharmony_ci chan->common.completed_cookie = cookie - 1; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci /* channel should not be busy */ 36828c2ecf20Sopenharmony_ci BUG_ON(ppc440spe_chan_is_busy(chan)); 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci /* set the descriptor address */ 36858c2ecf20Sopenharmony_ci ppc440spe_chan_set_first_xor_descriptor(chan, sw_desc); 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci /* run the descriptor */ 36888c2ecf20Sopenharmony_ci ppc440spe_chan_run(chan); 36898c2ecf20Sopenharmony_ci } else 36908c2ecf20Sopenharmony_ci printk(KERN_ERR "ppc440spe adma%d" 36918c2ecf20Sopenharmony_ci " failed to allocate null descriptor\n", 36928c2ecf20Sopenharmony_ci chan->device->id); 36938c2ecf20Sopenharmony_ci spin_unlock_bh(&chan->lock); 36948c2ecf20Sopenharmony_ci} 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci/** 36978c2ecf20Sopenharmony_ci * ppc440spe_test_raid6 - test are RAID-6 capabilities enabled successfully. 36988c2ecf20Sopenharmony_ci * For this we just perform one WXOR operation with the same source 36998c2ecf20Sopenharmony_ci * and destination addresses, the GF-multiplier is 1; so if RAID-6 37008c2ecf20Sopenharmony_ci * capabilities are enabled then we'll get src/dst filled with zero. 37018c2ecf20Sopenharmony_ci */ 37028c2ecf20Sopenharmony_cistatic int ppc440spe_test_raid6(struct ppc440spe_adma_chan *chan) 37038c2ecf20Sopenharmony_ci{ 37048c2ecf20Sopenharmony_ci struct ppc440spe_adma_desc_slot *sw_desc, *iter; 37058c2ecf20Sopenharmony_ci struct page *pg; 37068c2ecf20Sopenharmony_ci char *a; 37078c2ecf20Sopenharmony_ci dma_addr_t dma_addr, addrs[2]; 37088c2ecf20Sopenharmony_ci unsigned long op = 0; 37098c2ecf20Sopenharmony_ci int rval = 0; 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci set_bit(PPC440SPE_DESC_WXOR, &op); 37128c2ecf20Sopenharmony_ci 37138c2ecf20Sopenharmony_ci pg = alloc_page(GFP_KERNEL); 37148c2ecf20Sopenharmony_ci if (!pg) 37158c2ecf20Sopenharmony_ci return -ENOMEM; 37168c2ecf20Sopenharmony_ci 37178c2ecf20Sopenharmony_ci spin_lock_bh(&chan->lock); 37188c2ecf20Sopenharmony_ci sw_desc = ppc440spe_adma_alloc_slots(chan, 1, 1); 37198c2ecf20Sopenharmony_ci if (sw_desc) { 37208c2ecf20Sopenharmony_ci /* 1 src, 1 dsr, int_ena, WXOR */ 37218c2ecf20Sopenharmony_ci ppc440spe_desc_init_dma01pq(sw_desc, 1, 1, 1, op); 37228c2ecf20Sopenharmony_ci list_for_each_entry(iter, &sw_desc->group_list, chain_node) { 37238c2ecf20Sopenharmony_ci ppc440spe_desc_set_byte_count(iter, chan, PAGE_SIZE); 37248c2ecf20Sopenharmony_ci iter->unmap_len = PAGE_SIZE; 37258c2ecf20Sopenharmony_ci } 37268c2ecf20Sopenharmony_ci } else { 37278c2ecf20Sopenharmony_ci rval = -EFAULT; 37288c2ecf20Sopenharmony_ci spin_unlock_bh(&chan->lock); 37298c2ecf20Sopenharmony_ci goto exit; 37308c2ecf20Sopenharmony_ci } 37318c2ecf20Sopenharmony_ci spin_unlock_bh(&chan->lock); 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci /* Fill the test page with ones */ 37348c2ecf20Sopenharmony_ci memset(page_address(pg), 0xFF, PAGE_SIZE); 37358c2ecf20Sopenharmony_ci dma_addr = dma_map_page(chan->device->dev, pg, 0, 37368c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci /* Setup addresses */ 37398c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src(sw_desc, dma_addr, 0); 37408c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_src_mult(sw_desc, 1, 0, 0); 37418c2ecf20Sopenharmony_ci addrs[0] = dma_addr; 37428c2ecf20Sopenharmony_ci addrs[1] = 0; 37438c2ecf20Sopenharmony_ci ppc440spe_adma_pq_set_dest(sw_desc, addrs, DMA_PREP_PQ_DISABLE_Q); 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_ci async_tx_ack(&sw_desc->async_tx); 37468c2ecf20Sopenharmony_ci sw_desc->async_tx.callback = ppc440spe_test_callback; 37478c2ecf20Sopenharmony_ci sw_desc->async_tx.callback_param = NULL; 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_ci init_completion(&ppc440spe_r6_test_comp); 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci ppc440spe_adma_tx_submit(&sw_desc->async_tx); 37528c2ecf20Sopenharmony_ci ppc440spe_adma_issue_pending(&chan->common); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci wait_for_completion(&ppc440spe_r6_test_comp); 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci /* Now check if the test page is zeroed */ 37578c2ecf20Sopenharmony_ci a = page_address(pg); 37588c2ecf20Sopenharmony_ci if ((*(u32 *)a) == 0 && memcmp(a, a+4, PAGE_SIZE-4) == 0) { 37598c2ecf20Sopenharmony_ci /* page is zero - RAID-6 enabled */ 37608c2ecf20Sopenharmony_ci rval = 0; 37618c2ecf20Sopenharmony_ci } else { 37628c2ecf20Sopenharmony_ci /* RAID-6 was not enabled */ 37638c2ecf20Sopenharmony_ci rval = -EINVAL; 37648c2ecf20Sopenharmony_ci } 37658c2ecf20Sopenharmony_ciexit: 37668c2ecf20Sopenharmony_ci __free_page(pg); 37678c2ecf20Sopenharmony_ci return rval; 37688c2ecf20Sopenharmony_ci} 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_cistatic void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev) 37718c2ecf20Sopenharmony_ci{ 37728c2ecf20Sopenharmony_ci switch (adev->id) { 37738c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 37748c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 37758c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, adev->common.cap_mask); 37768c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask); 37778c2ecf20Sopenharmony_ci dma_cap_set(DMA_PQ, adev->common.cap_mask); 37788c2ecf20Sopenharmony_ci dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask); 37798c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask); 37808c2ecf20Sopenharmony_ci break; 37818c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 37828c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR, adev->common.cap_mask); 37838c2ecf20Sopenharmony_ci dma_cap_set(DMA_PQ, adev->common.cap_mask); 37848c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask); 37858c2ecf20Sopenharmony_ci adev->common.cap_mask = adev->common.cap_mask; 37868c2ecf20Sopenharmony_ci break; 37878c2ecf20Sopenharmony_ci } 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci /* Set base routines */ 37908c2ecf20Sopenharmony_ci adev->common.device_alloc_chan_resources = 37918c2ecf20Sopenharmony_ci ppc440spe_adma_alloc_chan_resources; 37928c2ecf20Sopenharmony_ci adev->common.device_free_chan_resources = 37938c2ecf20Sopenharmony_ci ppc440spe_adma_free_chan_resources; 37948c2ecf20Sopenharmony_ci adev->common.device_tx_status = ppc440spe_adma_tx_status; 37958c2ecf20Sopenharmony_ci adev->common.device_issue_pending = ppc440spe_adma_issue_pending; 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci /* Set prep routines based on capability */ 37988c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_MEMCPY, adev->common.cap_mask)) { 37998c2ecf20Sopenharmony_ci adev->common.device_prep_dma_memcpy = 38008c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_memcpy; 38018c2ecf20Sopenharmony_ci } 38028c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_XOR, adev->common.cap_mask)) { 38038c2ecf20Sopenharmony_ci adev->common.max_xor = XOR_MAX_OPS; 38048c2ecf20Sopenharmony_ci adev->common.device_prep_dma_xor = 38058c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_xor; 38068c2ecf20Sopenharmony_ci } 38078c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_PQ, adev->common.cap_mask)) { 38088c2ecf20Sopenharmony_ci switch (adev->id) { 38098c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 38108c2ecf20Sopenharmony_ci dma_set_maxpq(&adev->common, 38118c2ecf20Sopenharmony_ci DMA0_FIFO_SIZE / sizeof(struct dma_cdb), 0); 38128c2ecf20Sopenharmony_ci break; 38138c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 38148c2ecf20Sopenharmony_ci dma_set_maxpq(&adev->common, 38158c2ecf20Sopenharmony_ci DMA1_FIFO_SIZE / sizeof(struct dma_cdb), 0); 38168c2ecf20Sopenharmony_ci break; 38178c2ecf20Sopenharmony_ci case PPC440SPE_XOR_ID: 38188c2ecf20Sopenharmony_ci adev->common.max_pq = XOR_MAX_OPS * 3; 38198c2ecf20Sopenharmony_ci break; 38208c2ecf20Sopenharmony_ci } 38218c2ecf20Sopenharmony_ci adev->common.device_prep_dma_pq = 38228c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_pq; 38238c2ecf20Sopenharmony_ci } 38248c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask)) { 38258c2ecf20Sopenharmony_ci switch (adev->id) { 38268c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 38278c2ecf20Sopenharmony_ci adev->common.max_pq = DMA0_FIFO_SIZE / 38288c2ecf20Sopenharmony_ci sizeof(struct dma_cdb); 38298c2ecf20Sopenharmony_ci break; 38308c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 38318c2ecf20Sopenharmony_ci adev->common.max_pq = DMA1_FIFO_SIZE / 38328c2ecf20Sopenharmony_ci sizeof(struct dma_cdb); 38338c2ecf20Sopenharmony_ci break; 38348c2ecf20Sopenharmony_ci } 38358c2ecf20Sopenharmony_ci adev->common.device_prep_dma_pq_val = 38368c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_pqzero_sum; 38378c2ecf20Sopenharmony_ci } 38388c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask)) { 38398c2ecf20Sopenharmony_ci switch (adev->id) { 38408c2ecf20Sopenharmony_ci case PPC440SPE_DMA0_ID: 38418c2ecf20Sopenharmony_ci adev->common.max_xor = DMA0_FIFO_SIZE / 38428c2ecf20Sopenharmony_ci sizeof(struct dma_cdb); 38438c2ecf20Sopenharmony_ci break; 38448c2ecf20Sopenharmony_ci case PPC440SPE_DMA1_ID: 38458c2ecf20Sopenharmony_ci adev->common.max_xor = DMA1_FIFO_SIZE / 38468c2ecf20Sopenharmony_ci sizeof(struct dma_cdb); 38478c2ecf20Sopenharmony_ci break; 38488c2ecf20Sopenharmony_ci } 38498c2ecf20Sopenharmony_ci adev->common.device_prep_dma_xor_val = 38508c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_xor_zero_sum; 38518c2ecf20Sopenharmony_ci } 38528c2ecf20Sopenharmony_ci if (dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask)) { 38538c2ecf20Sopenharmony_ci adev->common.device_prep_dma_interrupt = 38548c2ecf20Sopenharmony_ci ppc440spe_adma_prep_dma_interrupt; 38558c2ecf20Sopenharmony_ci } 38568c2ecf20Sopenharmony_ci pr_info("%s: AMCC(R) PPC440SP(E) ADMA Engine: " 38578c2ecf20Sopenharmony_ci "( %s%s%s%s%s%s)\n", 38588c2ecf20Sopenharmony_ci dev_name(adev->dev), 38598c2ecf20Sopenharmony_ci dma_has_cap(DMA_PQ, adev->common.cap_mask) ? "pq " : "", 38608c2ecf20Sopenharmony_ci dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask) ? "pq_val " : "", 38618c2ecf20Sopenharmony_ci dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "", 38628c2ecf20Sopenharmony_ci dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask) ? "xor_val " : "", 38638c2ecf20Sopenharmony_ci dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "", 38648c2ecf20Sopenharmony_ci dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask) ? "intr " : ""); 38658c2ecf20Sopenharmony_ci} 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_cistatic int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev, 38688c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan, 38698c2ecf20Sopenharmony_ci int *initcode) 38708c2ecf20Sopenharmony_ci{ 38718c2ecf20Sopenharmony_ci struct platform_device *ofdev; 38728c2ecf20Sopenharmony_ci struct device_node *np; 38738c2ecf20Sopenharmony_ci int ret; 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_ci ofdev = container_of(adev->dev, struct platform_device, dev); 38768c2ecf20Sopenharmony_ci np = ofdev->dev.of_node; 38778c2ecf20Sopenharmony_ci if (adev->id != PPC440SPE_XOR_ID) { 38788c2ecf20Sopenharmony_ci adev->err_irq = irq_of_parse_and_map(np, 1); 38798c2ecf20Sopenharmony_ci if (!adev->err_irq) { 38808c2ecf20Sopenharmony_ci dev_warn(adev->dev, "no err irq resource?\n"); 38818c2ecf20Sopenharmony_ci *initcode = PPC_ADMA_INIT_IRQ2; 38828c2ecf20Sopenharmony_ci adev->err_irq = -ENXIO; 38838c2ecf20Sopenharmony_ci } else 38848c2ecf20Sopenharmony_ci atomic_inc(&ppc440spe_adma_err_irq_ref); 38858c2ecf20Sopenharmony_ci } else { 38868c2ecf20Sopenharmony_ci adev->err_irq = -ENXIO; 38878c2ecf20Sopenharmony_ci } 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci adev->irq = irq_of_parse_and_map(np, 0); 38908c2ecf20Sopenharmony_ci if (!adev->irq) { 38918c2ecf20Sopenharmony_ci dev_err(adev->dev, "no irq resource\n"); 38928c2ecf20Sopenharmony_ci *initcode = PPC_ADMA_INIT_IRQ1; 38938c2ecf20Sopenharmony_ci ret = -ENXIO; 38948c2ecf20Sopenharmony_ci goto err_irq_map; 38958c2ecf20Sopenharmony_ci } 38968c2ecf20Sopenharmony_ci dev_dbg(adev->dev, "irq %d, err irq %d\n", 38978c2ecf20Sopenharmony_ci adev->irq, adev->err_irq); 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci ret = request_irq(adev->irq, ppc440spe_adma_eot_handler, 39008c2ecf20Sopenharmony_ci 0, dev_driver_string(adev->dev), chan); 39018c2ecf20Sopenharmony_ci if (ret) { 39028c2ecf20Sopenharmony_ci dev_err(adev->dev, "can't request irq %d\n", 39038c2ecf20Sopenharmony_ci adev->irq); 39048c2ecf20Sopenharmony_ci *initcode = PPC_ADMA_INIT_IRQ1; 39058c2ecf20Sopenharmony_ci ret = -EIO; 39068c2ecf20Sopenharmony_ci goto err_req1; 39078c2ecf20Sopenharmony_ci } 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci /* only DMA engines have a separate error IRQ 39108c2ecf20Sopenharmony_ci * so it's Ok if err_irq < 0 in XOR engine case. 39118c2ecf20Sopenharmony_ci */ 39128c2ecf20Sopenharmony_ci if (adev->err_irq > 0) { 39138c2ecf20Sopenharmony_ci /* both DMA engines share common error IRQ */ 39148c2ecf20Sopenharmony_ci ret = request_irq(adev->err_irq, 39158c2ecf20Sopenharmony_ci ppc440spe_adma_err_handler, 39168c2ecf20Sopenharmony_ci IRQF_SHARED, 39178c2ecf20Sopenharmony_ci dev_driver_string(adev->dev), 39188c2ecf20Sopenharmony_ci chan); 39198c2ecf20Sopenharmony_ci if (ret) { 39208c2ecf20Sopenharmony_ci dev_err(adev->dev, "can't request irq %d\n", 39218c2ecf20Sopenharmony_ci adev->err_irq); 39228c2ecf20Sopenharmony_ci *initcode = PPC_ADMA_INIT_IRQ2; 39238c2ecf20Sopenharmony_ci ret = -EIO; 39248c2ecf20Sopenharmony_ci goto err_req2; 39258c2ecf20Sopenharmony_ci } 39268c2ecf20Sopenharmony_ci } 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_ci if (adev->id == PPC440SPE_XOR_ID) { 39298c2ecf20Sopenharmony_ci /* enable XOR engine interrupts */ 39308c2ecf20Sopenharmony_ci iowrite32be(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT | 39318c2ecf20Sopenharmony_ci XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT, 39328c2ecf20Sopenharmony_ci &adev->xor_reg->ier); 39338c2ecf20Sopenharmony_ci } else { 39348c2ecf20Sopenharmony_ci u32 mask, enable; 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe"); 39378c2ecf20Sopenharmony_ci if (!np) { 39388c2ecf20Sopenharmony_ci pr_err("%s: can't find I2O device tree node\n", 39398c2ecf20Sopenharmony_ci __func__); 39408c2ecf20Sopenharmony_ci ret = -ENODEV; 39418c2ecf20Sopenharmony_ci goto err_req2; 39428c2ecf20Sopenharmony_ci } 39438c2ecf20Sopenharmony_ci adev->i2o_reg = of_iomap(np, 0); 39448c2ecf20Sopenharmony_ci if (!adev->i2o_reg) { 39458c2ecf20Sopenharmony_ci pr_err("%s: failed to map I2O registers\n", __func__); 39468c2ecf20Sopenharmony_ci of_node_put(np); 39478c2ecf20Sopenharmony_ci ret = -EINVAL; 39488c2ecf20Sopenharmony_ci goto err_req2; 39498c2ecf20Sopenharmony_ci } 39508c2ecf20Sopenharmony_ci of_node_put(np); 39518c2ecf20Sopenharmony_ci /* Unmask 'CS FIFO Attention' interrupts and 39528c2ecf20Sopenharmony_ci * enable generating interrupts on errors 39538c2ecf20Sopenharmony_ci */ 39548c2ecf20Sopenharmony_ci enable = (adev->id == PPC440SPE_DMA0_ID) ? 39558c2ecf20Sopenharmony_ci ~(I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) : 39568c2ecf20Sopenharmony_ci ~(I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM); 39578c2ecf20Sopenharmony_ci mask = ioread32(&adev->i2o_reg->iopim) & enable; 39588c2ecf20Sopenharmony_ci iowrite32(mask, &adev->i2o_reg->iopim); 39598c2ecf20Sopenharmony_ci } 39608c2ecf20Sopenharmony_ci return 0; 39618c2ecf20Sopenharmony_ci 39628c2ecf20Sopenharmony_cierr_req2: 39638c2ecf20Sopenharmony_ci free_irq(adev->irq, chan); 39648c2ecf20Sopenharmony_cierr_req1: 39658c2ecf20Sopenharmony_ci irq_dispose_mapping(adev->irq); 39668c2ecf20Sopenharmony_cierr_irq_map: 39678c2ecf20Sopenharmony_ci if (adev->err_irq > 0) { 39688c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) 39698c2ecf20Sopenharmony_ci irq_dispose_mapping(adev->err_irq); 39708c2ecf20Sopenharmony_ci } 39718c2ecf20Sopenharmony_ci return ret; 39728c2ecf20Sopenharmony_ci} 39738c2ecf20Sopenharmony_ci 39748c2ecf20Sopenharmony_cistatic void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev, 39758c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan) 39768c2ecf20Sopenharmony_ci{ 39778c2ecf20Sopenharmony_ci u32 mask, disable; 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci if (adev->id == PPC440SPE_XOR_ID) { 39808c2ecf20Sopenharmony_ci /* disable XOR engine interrupts */ 39818c2ecf20Sopenharmony_ci mask = ioread32be(&adev->xor_reg->ier); 39828c2ecf20Sopenharmony_ci mask &= ~(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT | 39838c2ecf20Sopenharmony_ci XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT); 39848c2ecf20Sopenharmony_ci iowrite32be(mask, &adev->xor_reg->ier); 39858c2ecf20Sopenharmony_ci } else { 39868c2ecf20Sopenharmony_ci /* disable DMAx engine interrupts */ 39878c2ecf20Sopenharmony_ci disable = (adev->id == PPC440SPE_DMA0_ID) ? 39888c2ecf20Sopenharmony_ci (I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) : 39898c2ecf20Sopenharmony_ci (I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM); 39908c2ecf20Sopenharmony_ci mask = ioread32(&adev->i2o_reg->iopim) | disable; 39918c2ecf20Sopenharmony_ci iowrite32(mask, &adev->i2o_reg->iopim); 39928c2ecf20Sopenharmony_ci } 39938c2ecf20Sopenharmony_ci free_irq(adev->irq, chan); 39948c2ecf20Sopenharmony_ci irq_dispose_mapping(adev->irq); 39958c2ecf20Sopenharmony_ci if (adev->err_irq > 0) { 39968c2ecf20Sopenharmony_ci free_irq(adev->err_irq, chan); 39978c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) { 39988c2ecf20Sopenharmony_ci irq_dispose_mapping(adev->err_irq); 39998c2ecf20Sopenharmony_ci iounmap(adev->i2o_reg); 40008c2ecf20Sopenharmony_ci } 40018c2ecf20Sopenharmony_ci } 40028c2ecf20Sopenharmony_ci} 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci/** 40058c2ecf20Sopenharmony_ci * ppc440spe_adma_probe - probe the asynch device 40068c2ecf20Sopenharmony_ci */ 40078c2ecf20Sopenharmony_cistatic int ppc440spe_adma_probe(struct platform_device *ofdev) 40088c2ecf20Sopenharmony_ci{ 40098c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 40108c2ecf20Sopenharmony_ci struct resource res; 40118c2ecf20Sopenharmony_ci struct ppc440spe_adma_device *adev; 40128c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *chan; 40138c2ecf20Sopenharmony_ci struct ppc_dma_chan_ref *ref, *_ref; 40148c2ecf20Sopenharmony_ci int ret = 0, initcode = PPC_ADMA_INIT_OK; 40158c2ecf20Sopenharmony_ci const u32 *idx; 40168c2ecf20Sopenharmony_ci int len; 40178c2ecf20Sopenharmony_ci void *regs; 40188c2ecf20Sopenharmony_ci u32 id, pool_size; 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "amcc,xor-accelerator")) { 40218c2ecf20Sopenharmony_ci id = PPC440SPE_XOR_ID; 40228c2ecf20Sopenharmony_ci /* As far as the XOR engine is concerned, it does not 40238c2ecf20Sopenharmony_ci * use FIFOs but uses linked list. So there is no dependency 40248c2ecf20Sopenharmony_ci * between pool size to allocate and the engine configuration. 40258c2ecf20Sopenharmony_ci */ 40268c2ecf20Sopenharmony_ci pool_size = PAGE_SIZE << 1; 40278c2ecf20Sopenharmony_ci } else { 40288c2ecf20Sopenharmony_ci /* it is DMA0 or DMA1 */ 40298c2ecf20Sopenharmony_ci idx = of_get_property(np, "cell-index", &len); 40308c2ecf20Sopenharmony_ci if (!idx || (len != sizeof(u32))) { 40318c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "Device node %pOF has missing " 40328c2ecf20Sopenharmony_ci "or invalid cell-index property\n", 40338c2ecf20Sopenharmony_ci np); 40348c2ecf20Sopenharmony_ci return -EINVAL; 40358c2ecf20Sopenharmony_ci } 40368c2ecf20Sopenharmony_ci id = *idx; 40378c2ecf20Sopenharmony_ci /* DMA0,1 engines use FIFO to maintain CDBs, so we 40388c2ecf20Sopenharmony_ci * should allocate the pool accordingly to size of this 40398c2ecf20Sopenharmony_ci * FIFO. Thus, the pool size depends on the FIFO depth: 40408c2ecf20Sopenharmony_ci * how much CDBs pointers the FIFO may contain then so 40418c2ecf20Sopenharmony_ci * much CDBs we should provide in the pool. 40428c2ecf20Sopenharmony_ci * That is 40438c2ecf20Sopenharmony_ci * CDB size = 32B; 40448c2ecf20Sopenharmony_ci * CDBs number = (DMA0_FIFO_SIZE >> 3); 40458c2ecf20Sopenharmony_ci * Pool size = CDBs number * CDB size = 40468c2ecf20Sopenharmony_ci * = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2. 40478c2ecf20Sopenharmony_ci */ 40488c2ecf20Sopenharmony_ci pool_size = (id == PPC440SPE_DMA0_ID) ? 40498c2ecf20Sopenharmony_ci DMA0_FIFO_SIZE : DMA1_FIFO_SIZE; 40508c2ecf20Sopenharmony_ci pool_size <<= 2; 40518c2ecf20Sopenharmony_ci } 40528c2ecf20Sopenharmony_ci 40538c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, &res)) { 40548c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to get memory resource\n"); 40558c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_MEMRES; 40568c2ecf20Sopenharmony_ci ret = -ENODEV; 40578c2ecf20Sopenharmony_ci goto out; 40588c2ecf20Sopenharmony_ci } 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ci if (!request_mem_region(res.start, resource_size(&res), 40618c2ecf20Sopenharmony_ci dev_driver_string(&ofdev->dev))) { 40628c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to request memory region %pR\n", 40638c2ecf20Sopenharmony_ci &res); 40648c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_MEMREG; 40658c2ecf20Sopenharmony_ci ret = -EBUSY; 40668c2ecf20Sopenharmony_ci goto out; 40678c2ecf20Sopenharmony_ci } 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci /* create a device */ 40708c2ecf20Sopenharmony_ci adev = kzalloc(sizeof(*adev), GFP_KERNEL); 40718c2ecf20Sopenharmony_ci if (!adev) { 40728c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_ALLOC; 40738c2ecf20Sopenharmony_ci ret = -ENOMEM; 40748c2ecf20Sopenharmony_ci goto err_adev_alloc; 40758c2ecf20Sopenharmony_ci } 40768c2ecf20Sopenharmony_ci 40778c2ecf20Sopenharmony_ci adev->id = id; 40788c2ecf20Sopenharmony_ci adev->pool_size = pool_size; 40798c2ecf20Sopenharmony_ci /* allocate coherent memory for hardware descriptors */ 40808c2ecf20Sopenharmony_ci adev->dma_desc_pool_virt = dma_alloc_coherent(&ofdev->dev, 40818c2ecf20Sopenharmony_ci adev->pool_size, &adev->dma_desc_pool, 40828c2ecf20Sopenharmony_ci GFP_KERNEL); 40838c2ecf20Sopenharmony_ci if (adev->dma_desc_pool_virt == NULL) { 40848c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to allocate %d bytes of coherent " 40858c2ecf20Sopenharmony_ci "memory for hardware descriptors\n", 40868c2ecf20Sopenharmony_ci adev->pool_size); 40878c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_COHERENT; 40888c2ecf20Sopenharmony_ci ret = -ENOMEM; 40898c2ecf20Sopenharmony_ci goto err_dma_alloc; 40908c2ecf20Sopenharmony_ci } 40918c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n", 40928c2ecf20Sopenharmony_ci adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool); 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci regs = ioremap(res.start, resource_size(&res)); 40958c2ecf20Sopenharmony_ci if (!regs) { 40968c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to ioremap regs!\n"); 40978c2ecf20Sopenharmony_ci ret = -ENOMEM; 40988c2ecf20Sopenharmony_ci goto err_regs_alloc; 40998c2ecf20Sopenharmony_ci } 41008c2ecf20Sopenharmony_ci 41018c2ecf20Sopenharmony_ci if (adev->id == PPC440SPE_XOR_ID) { 41028c2ecf20Sopenharmony_ci adev->xor_reg = regs; 41038c2ecf20Sopenharmony_ci /* Reset XOR */ 41048c2ecf20Sopenharmony_ci iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr); 41058c2ecf20Sopenharmony_ci iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr); 41068c2ecf20Sopenharmony_ci } else { 41078c2ecf20Sopenharmony_ci size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ? 41088c2ecf20Sopenharmony_ci DMA0_FIFO_SIZE : DMA1_FIFO_SIZE; 41098c2ecf20Sopenharmony_ci adev->dma_reg = regs; 41108c2ecf20Sopenharmony_ci /* DMAx_FIFO_SIZE is defined in bytes, 41118c2ecf20Sopenharmony_ci * <fsiz> - is defined in number of CDB pointers (8byte). 41128c2ecf20Sopenharmony_ci * DMA FIFO Length = CSlength + CPlength, where 41138c2ecf20Sopenharmony_ci * CSlength = CPlength = (fsiz + 1) * 8. 41148c2ecf20Sopenharmony_ci */ 41158c2ecf20Sopenharmony_ci iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2), 41168c2ecf20Sopenharmony_ci &adev->dma_reg->fsiz); 41178c2ecf20Sopenharmony_ci /* Configure DMA engine */ 41188c2ecf20Sopenharmony_ci iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN, 41198c2ecf20Sopenharmony_ci &adev->dma_reg->cfg); 41208c2ecf20Sopenharmony_ci /* Clear Status */ 41218c2ecf20Sopenharmony_ci iowrite32(~0, &adev->dma_reg->dsts); 41228c2ecf20Sopenharmony_ci } 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci adev->dev = &ofdev->dev; 41258c2ecf20Sopenharmony_ci adev->common.dev = &ofdev->dev; 41268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&adev->common.channels); 41278c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, adev); 41288c2ecf20Sopenharmony_ci 41298c2ecf20Sopenharmony_ci /* create a channel */ 41308c2ecf20Sopenharmony_ci chan = kzalloc(sizeof(*chan), GFP_KERNEL); 41318c2ecf20Sopenharmony_ci if (!chan) { 41328c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_CHANNEL; 41338c2ecf20Sopenharmony_ci ret = -ENOMEM; 41348c2ecf20Sopenharmony_ci goto err_chan_alloc; 41358c2ecf20Sopenharmony_ci } 41368c2ecf20Sopenharmony_ci 41378c2ecf20Sopenharmony_ci spin_lock_init(&chan->lock); 41388c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chan->chain); 41398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chan->all_slots); 41408c2ecf20Sopenharmony_ci chan->device = adev; 41418c2ecf20Sopenharmony_ci chan->common.device = &adev->common; 41428c2ecf20Sopenharmony_ci dma_cookie_init(&chan->common); 41438c2ecf20Sopenharmony_ci list_add_tail(&chan->common.device_node, &adev->common.channels); 41448c2ecf20Sopenharmony_ci tasklet_setup(&chan->irq_tasklet, ppc440spe_adma_tasklet); 41458c2ecf20Sopenharmony_ci 41468c2ecf20Sopenharmony_ci /* allocate and map helper pages for async validation or 41478c2ecf20Sopenharmony_ci * async_mult/async_sum_product operations on DMA0/1. 41488c2ecf20Sopenharmony_ci */ 41498c2ecf20Sopenharmony_ci if (adev->id != PPC440SPE_XOR_ID) { 41508c2ecf20Sopenharmony_ci chan->pdest_page = alloc_page(GFP_KERNEL); 41518c2ecf20Sopenharmony_ci chan->qdest_page = alloc_page(GFP_KERNEL); 41528c2ecf20Sopenharmony_ci if (!chan->pdest_page || 41538c2ecf20Sopenharmony_ci !chan->qdest_page) { 41548c2ecf20Sopenharmony_ci if (chan->pdest_page) 41558c2ecf20Sopenharmony_ci __free_page(chan->pdest_page); 41568c2ecf20Sopenharmony_ci if (chan->qdest_page) 41578c2ecf20Sopenharmony_ci __free_page(chan->qdest_page); 41588c2ecf20Sopenharmony_ci ret = -ENOMEM; 41598c2ecf20Sopenharmony_ci goto err_page_alloc; 41608c2ecf20Sopenharmony_ci } 41618c2ecf20Sopenharmony_ci chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0, 41628c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 41638c2ecf20Sopenharmony_ci chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0, 41648c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 41658c2ecf20Sopenharmony_ci } 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci ref = kmalloc(sizeof(*ref), GFP_KERNEL); 41688c2ecf20Sopenharmony_ci if (ref) { 41698c2ecf20Sopenharmony_ci ref->chan = &chan->common; 41708c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ref->node); 41718c2ecf20Sopenharmony_ci list_add_tail(&ref->node, &ppc440spe_adma_chan_list); 41728c2ecf20Sopenharmony_ci } else { 41738c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to allocate channel reference!\n"); 41748c2ecf20Sopenharmony_ci ret = -ENOMEM; 41758c2ecf20Sopenharmony_ci goto err_ref_alloc; 41768c2ecf20Sopenharmony_ci } 41778c2ecf20Sopenharmony_ci 41788c2ecf20Sopenharmony_ci ret = ppc440spe_adma_setup_irqs(adev, chan, &initcode); 41798c2ecf20Sopenharmony_ci if (ret) 41808c2ecf20Sopenharmony_ci goto err_irq; 41818c2ecf20Sopenharmony_ci 41828c2ecf20Sopenharmony_ci ppc440spe_adma_init_capabilities(adev); 41838c2ecf20Sopenharmony_ci 41848c2ecf20Sopenharmony_ci ret = dma_async_device_register(&adev->common); 41858c2ecf20Sopenharmony_ci if (ret) { 41868c2ecf20Sopenharmony_ci initcode = PPC_ADMA_INIT_REGISTER; 41878c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "failed to register dma device\n"); 41888c2ecf20Sopenharmony_ci goto err_dev_reg; 41898c2ecf20Sopenharmony_ci } 41908c2ecf20Sopenharmony_ci 41918c2ecf20Sopenharmony_ci goto out; 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_cierr_dev_reg: 41948c2ecf20Sopenharmony_ci ppc440spe_adma_release_irqs(adev, chan); 41958c2ecf20Sopenharmony_cierr_irq: 41968c2ecf20Sopenharmony_ci list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list, node) { 41978c2ecf20Sopenharmony_ci if (chan == to_ppc440spe_adma_chan(ref->chan)) { 41988c2ecf20Sopenharmony_ci list_del(&ref->node); 41998c2ecf20Sopenharmony_ci kfree(ref); 42008c2ecf20Sopenharmony_ci } 42018c2ecf20Sopenharmony_ci } 42028c2ecf20Sopenharmony_cierr_ref_alloc: 42038c2ecf20Sopenharmony_ci if (adev->id != PPC440SPE_XOR_ID) { 42048c2ecf20Sopenharmony_ci dma_unmap_page(&ofdev->dev, chan->pdest, 42058c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 42068c2ecf20Sopenharmony_ci dma_unmap_page(&ofdev->dev, chan->qdest, 42078c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 42088c2ecf20Sopenharmony_ci __free_page(chan->pdest_page); 42098c2ecf20Sopenharmony_ci __free_page(chan->qdest_page); 42108c2ecf20Sopenharmony_ci } 42118c2ecf20Sopenharmony_cierr_page_alloc: 42128c2ecf20Sopenharmony_ci kfree(chan); 42138c2ecf20Sopenharmony_cierr_chan_alloc: 42148c2ecf20Sopenharmony_ci if (adev->id == PPC440SPE_XOR_ID) 42158c2ecf20Sopenharmony_ci iounmap(adev->xor_reg); 42168c2ecf20Sopenharmony_ci else 42178c2ecf20Sopenharmony_ci iounmap(adev->dma_reg); 42188c2ecf20Sopenharmony_cierr_regs_alloc: 42198c2ecf20Sopenharmony_ci dma_free_coherent(adev->dev, adev->pool_size, 42208c2ecf20Sopenharmony_ci adev->dma_desc_pool_virt, 42218c2ecf20Sopenharmony_ci adev->dma_desc_pool); 42228c2ecf20Sopenharmony_cierr_dma_alloc: 42238c2ecf20Sopenharmony_ci kfree(adev); 42248c2ecf20Sopenharmony_cierr_adev_alloc: 42258c2ecf20Sopenharmony_ci release_mem_region(res.start, resource_size(&res)); 42268c2ecf20Sopenharmony_ciout: 42278c2ecf20Sopenharmony_ci if (id < PPC440SPE_ADMA_ENGINES_NUM) 42288c2ecf20Sopenharmony_ci ppc440spe_adma_devices[id] = initcode; 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci return ret; 42318c2ecf20Sopenharmony_ci} 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci/** 42348c2ecf20Sopenharmony_ci * ppc440spe_adma_remove - remove the asynch device 42358c2ecf20Sopenharmony_ci */ 42368c2ecf20Sopenharmony_cistatic int ppc440spe_adma_remove(struct platform_device *ofdev) 42378c2ecf20Sopenharmony_ci{ 42388c2ecf20Sopenharmony_ci struct ppc440spe_adma_device *adev = platform_get_drvdata(ofdev); 42398c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 42408c2ecf20Sopenharmony_ci struct resource res; 42418c2ecf20Sopenharmony_ci struct dma_chan *chan, *_chan; 42428c2ecf20Sopenharmony_ci struct ppc_dma_chan_ref *ref, *_ref; 42438c2ecf20Sopenharmony_ci struct ppc440spe_adma_chan *ppc440spe_chan; 42448c2ecf20Sopenharmony_ci 42458c2ecf20Sopenharmony_ci if (adev->id < PPC440SPE_ADMA_ENGINES_NUM) 42468c2ecf20Sopenharmony_ci ppc440spe_adma_devices[adev->id] = -1; 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci dma_async_device_unregister(&adev->common); 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &adev->common.channels, 42518c2ecf20Sopenharmony_ci device_node) { 42528c2ecf20Sopenharmony_ci ppc440spe_chan = to_ppc440spe_adma_chan(chan); 42538c2ecf20Sopenharmony_ci ppc440spe_adma_release_irqs(adev, ppc440spe_chan); 42548c2ecf20Sopenharmony_ci tasklet_kill(&ppc440spe_chan->irq_tasklet); 42558c2ecf20Sopenharmony_ci if (adev->id != PPC440SPE_XOR_ID) { 42568c2ecf20Sopenharmony_ci dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest, 42578c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 42588c2ecf20Sopenharmony_ci dma_unmap_page(&ofdev->dev, ppc440spe_chan->qdest, 42598c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 42608c2ecf20Sopenharmony_ci __free_page(ppc440spe_chan->pdest_page); 42618c2ecf20Sopenharmony_ci __free_page(ppc440spe_chan->qdest_page); 42628c2ecf20Sopenharmony_ci } 42638c2ecf20Sopenharmony_ci list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list, 42648c2ecf20Sopenharmony_ci node) { 42658c2ecf20Sopenharmony_ci if (ppc440spe_chan == 42668c2ecf20Sopenharmony_ci to_ppc440spe_adma_chan(ref->chan)) { 42678c2ecf20Sopenharmony_ci list_del(&ref->node); 42688c2ecf20Sopenharmony_ci kfree(ref); 42698c2ecf20Sopenharmony_ci } 42708c2ecf20Sopenharmony_ci } 42718c2ecf20Sopenharmony_ci list_del(&chan->device_node); 42728c2ecf20Sopenharmony_ci kfree(ppc440spe_chan); 42738c2ecf20Sopenharmony_ci } 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_ci dma_free_coherent(adev->dev, adev->pool_size, 42768c2ecf20Sopenharmony_ci adev->dma_desc_pool_virt, adev->dma_desc_pool); 42778c2ecf20Sopenharmony_ci if (adev->id == PPC440SPE_XOR_ID) 42788c2ecf20Sopenharmony_ci iounmap(adev->xor_reg); 42798c2ecf20Sopenharmony_ci else 42808c2ecf20Sopenharmony_ci iounmap(adev->dma_reg); 42818c2ecf20Sopenharmony_ci of_address_to_resource(np, 0, &res); 42828c2ecf20Sopenharmony_ci release_mem_region(res.start, resource_size(&res)); 42838c2ecf20Sopenharmony_ci kfree(adev); 42848c2ecf20Sopenharmony_ci return 0; 42858c2ecf20Sopenharmony_ci} 42868c2ecf20Sopenharmony_ci 42878c2ecf20Sopenharmony_ci/* 42888c2ecf20Sopenharmony_ci * /sys driver interface to enable h/w RAID-6 capabilities 42898c2ecf20Sopenharmony_ci * Files created in e.g. /sys/devices/plb.0/400100100.dma0/driver/ 42908c2ecf20Sopenharmony_ci * directory are "devices", "enable" and "poly". 42918c2ecf20Sopenharmony_ci * "devices" shows available engines. 42928c2ecf20Sopenharmony_ci * "enable" is used to enable RAID-6 capabilities or to check 42938c2ecf20Sopenharmony_ci * whether these has been activated. 42948c2ecf20Sopenharmony_ci * "poly" allows setting/checking used polynomial (for PPC440SPe only). 42958c2ecf20Sopenharmony_ci */ 42968c2ecf20Sopenharmony_ci 42978c2ecf20Sopenharmony_cistatic ssize_t devices_show(struct device_driver *dev, char *buf) 42988c2ecf20Sopenharmony_ci{ 42998c2ecf20Sopenharmony_ci ssize_t size = 0; 43008c2ecf20Sopenharmony_ci int i; 43018c2ecf20Sopenharmony_ci 43028c2ecf20Sopenharmony_ci for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) { 43038c2ecf20Sopenharmony_ci if (ppc440spe_adma_devices[i] == -1) 43048c2ecf20Sopenharmony_ci continue; 43058c2ecf20Sopenharmony_ci size += scnprintf(buf + size, PAGE_SIZE - size, 43068c2ecf20Sopenharmony_ci "PPC440SP(E)-ADMA.%d: %s\n", i, 43078c2ecf20Sopenharmony_ci ppc_adma_errors[ppc440spe_adma_devices[i]]); 43088c2ecf20Sopenharmony_ci } 43098c2ecf20Sopenharmony_ci return size; 43108c2ecf20Sopenharmony_ci} 43118c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(devices); 43128c2ecf20Sopenharmony_ci 43138c2ecf20Sopenharmony_cistatic ssize_t enable_show(struct device_driver *dev, char *buf) 43148c2ecf20Sopenharmony_ci{ 43158c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, 43168c2ecf20Sopenharmony_ci "PPC440SP(e) RAID-6 capabilities are %sABLED.\n", 43178c2ecf20Sopenharmony_ci ppc440spe_r6_enabled ? "EN" : "DIS"); 43188c2ecf20Sopenharmony_ci} 43198c2ecf20Sopenharmony_ci 43208c2ecf20Sopenharmony_cistatic ssize_t enable_store(struct device_driver *dev, const char *buf, 43218c2ecf20Sopenharmony_ci size_t count) 43228c2ecf20Sopenharmony_ci{ 43238c2ecf20Sopenharmony_ci unsigned long val; 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_ci if (!count || count > 11) 43268c2ecf20Sopenharmony_ci return -EINVAL; 43278c2ecf20Sopenharmony_ci 43288c2ecf20Sopenharmony_ci if (!ppc440spe_r6_tchan) 43298c2ecf20Sopenharmony_ci return -EFAULT; 43308c2ecf20Sopenharmony_ci 43318c2ecf20Sopenharmony_ci /* Write a key */ 43328c2ecf20Sopenharmony_ci sscanf(buf, "%lx", &val); 43338c2ecf20Sopenharmony_ci dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_XORBA, val); 43348c2ecf20Sopenharmony_ci isync(); 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci /* Verify whether it really works now */ 43378c2ecf20Sopenharmony_ci if (ppc440spe_test_raid6(ppc440spe_r6_tchan) == 0) { 43388c2ecf20Sopenharmony_ci pr_info("PPC440SP(e) RAID-6 has been activated " 43398c2ecf20Sopenharmony_ci "successfully\n"); 43408c2ecf20Sopenharmony_ci ppc440spe_r6_enabled = 1; 43418c2ecf20Sopenharmony_ci } else { 43428c2ecf20Sopenharmony_ci pr_info("PPC440SP(e) RAID-6 hasn't been activated!" 43438c2ecf20Sopenharmony_ci " Error key ?\n"); 43448c2ecf20Sopenharmony_ci ppc440spe_r6_enabled = 0; 43458c2ecf20Sopenharmony_ci } 43468c2ecf20Sopenharmony_ci return count; 43478c2ecf20Sopenharmony_ci} 43488c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(enable); 43498c2ecf20Sopenharmony_ci 43508c2ecf20Sopenharmony_cistatic ssize_t poly_show(struct device_driver *dev, char *buf) 43518c2ecf20Sopenharmony_ci{ 43528c2ecf20Sopenharmony_ci ssize_t size = 0; 43538c2ecf20Sopenharmony_ci u32 reg; 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_ci#ifdef CONFIG_440SP 43568c2ecf20Sopenharmony_ci /* 440SP has fixed polynomial */ 43578c2ecf20Sopenharmony_ci reg = 0x4d; 43588c2ecf20Sopenharmony_ci#else 43598c2ecf20Sopenharmony_ci reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL); 43608c2ecf20Sopenharmony_ci reg >>= MQ0_CFBHL_POLY; 43618c2ecf20Sopenharmony_ci reg &= 0xFF; 43628c2ecf20Sopenharmony_ci#endif 43638c2ecf20Sopenharmony_ci 43648c2ecf20Sopenharmony_ci size = snprintf(buf, PAGE_SIZE, "PPC440SP(e) RAID-6 driver " 43658c2ecf20Sopenharmony_ci "uses 0x1%02x polynomial.\n", reg); 43668c2ecf20Sopenharmony_ci return size; 43678c2ecf20Sopenharmony_ci} 43688c2ecf20Sopenharmony_ci 43698c2ecf20Sopenharmony_cistatic ssize_t poly_store(struct device_driver *dev, const char *buf, 43708c2ecf20Sopenharmony_ci size_t count) 43718c2ecf20Sopenharmony_ci{ 43728c2ecf20Sopenharmony_ci unsigned long reg, val; 43738c2ecf20Sopenharmony_ci 43748c2ecf20Sopenharmony_ci#ifdef CONFIG_440SP 43758c2ecf20Sopenharmony_ci /* 440SP uses default 0x14D polynomial only */ 43768c2ecf20Sopenharmony_ci return -EINVAL; 43778c2ecf20Sopenharmony_ci#endif 43788c2ecf20Sopenharmony_ci 43798c2ecf20Sopenharmony_ci if (!count || count > 6) 43808c2ecf20Sopenharmony_ci return -EINVAL; 43818c2ecf20Sopenharmony_ci 43828c2ecf20Sopenharmony_ci /* e.g., 0x14D or 0x11D */ 43838c2ecf20Sopenharmony_ci sscanf(buf, "%lx", &val); 43848c2ecf20Sopenharmony_ci 43858c2ecf20Sopenharmony_ci if (val & ~0x1FF) 43868c2ecf20Sopenharmony_ci return -EINVAL; 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci val &= 0xFF; 43898c2ecf20Sopenharmony_ci reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL); 43908c2ecf20Sopenharmony_ci reg &= ~(0xFF << MQ0_CFBHL_POLY); 43918c2ecf20Sopenharmony_ci reg |= val << MQ0_CFBHL_POLY; 43928c2ecf20Sopenharmony_ci dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, reg); 43938c2ecf20Sopenharmony_ci 43948c2ecf20Sopenharmony_ci return count; 43958c2ecf20Sopenharmony_ci} 43968c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(poly); 43978c2ecf20Sopenharmony_ci 43988c2ecf20Sopenharmony_ci/* 43998c2ecf20Sopenharmony_ci * Common initialisation for RAID engines; allocate memory for 44008c2ecf20Sopenharmony_ci * DMAx FIFOs, perform configuration common for all DMA engines. 44018c2ecf20Sopenharmony_ci * Further DMA engine specific configuration is done at probe time. 44028c2ecf20Sopenharmony_ci */ 44038c2ecf20Sopenharmony_cistatic int ppc440spe_configure_raid_devices(void) 44048c2ecf20Sopenharmony_ci{ 44058c2ecf20Sopenharmony_ci struct device_node *np; 44068c2ecf20Sopenharmony_ci struct resource i2o_res; 44078c2ecf20Sopenharmony_ci struct i2o_regs __iomem *i2o_reg; 44088c2ecf20Sopenharmony_ci dcr_host_t i2o_dcr_host; 44098c2ecf20Sopenharmony_ci unsigned int dcr_base, dcr_len; 44108c2ecf20Sopenharmony_ci int i, ret; 44118c2ecf20Sopenharmony_ci 44128c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe"); 44138c2ecf20Sopenharmony_ci if (!np) { 44148c2ecf20Sopenharmony_ci pr_err("%s: can't find I2O device tree node\n", 44158c2ecf20Sopenharmony_ci __func__); 44168c2ecf20Sopenharmony_ci return -ENODEV; 44178c2ecf20Sopenharmony_ci } 44188c2ecf20Sopenharmony_ci 44198c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, &i2o_res)) { 44208c2ecf20Sopenharmony_ci of_node_put(np); 44218c2ecf20Sopenharmony_ci return -EINVAL; 44228c2ecf20Sopenharmony_ci } 44238c2ecf20Sopenharmony_ci 44248c2ecf20Sopenharmony_ci i2o_reg = of_iomap(np, 0); 44258c2ecf20Sopenharmony_ci if (!i2o_reg) { 44268c2ecf20Sopenharmony_ci pr_err("%s: failed to map I2O registers\n", __func__); 44278c2ecf20Sopenharmony_ci of_node_put(np); 44288c2ecf20Sopenharmony_ci return -EINVAL; 44298c2ecf20Sopenharmony_ci } 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_ci /* Get I2O DCRs base */ 44328c2ecf20Sopenharmony_ci dcr_base = dcr_resource_start(np, 0); 44338c2ecf20Sopenharmony_ci dcr_len = dcr_resource_len(np, 0); 44348c2ecf20Sopenharmony_ci if (!dcr_base && !dcr_len) { 44358c2ecf20Sopenharmony_ci pr_err("%pOF: can't get DCR registers base/len!\n", np); 44368c2ecf20Sopenharmony_ci of_node_put(np); 44378c2ecf20Sopenharmony_ci iounmap(i2o_reg); 44388c2ecf20Sopenharmony_ci return -ENODEV; 44398c2ecf20Sopenharmony_ci } 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_ci i2o_dcr_host = dcr_map(np, dcr_base, dcr_len); 44428c2ecf20Sopenharmony_ci if (!DCR_MAP_OK(i2o_dcr_host)) { 44438c2ecf20Sopenharmony_ci pr_err("%pOF: failed to map DCRs!\n", np); 44448c2ecf20Sopenharmony_ci of_node_put(np); 44458c2ecf20Sopenharmony_ci iounmap(i2o_reg); 44468c2ecf20Sopenharmony_ci return -ENODEV; 44478c2ecf20Sopenharmony_ci } 44488c2ecf20Sopenharmony_ci of_node_put(np); 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_ci /* Provide memory regions for DMA's FIFOs: I2O, DMA0 and DMA1 share 44518c2ecf20Sopenharmony_ci * the base address of FIFO memory space. 44528c2ecf20Sopenharmony_ci * Actually we need twice more physical memory than programmed in the 44538c2ecf20Sopenharmony_ci * <fsiz> register (because there are two FIFOs for each DMA: CP and CS) 44548c2ecf20Sopenharmony_ci */ 44558c2ecf20Sopenharmony_ci ppc440spe_dma_fifo_buf = kmalloc((DMA0_FIFO_SIZE + DMA1_FIFO_SIZE) << 1, 44568c2ecf20Sopenharmony_ci GFP_KERNEL); 44578c2ecf20Sopenharmony_ci if (!ppc440spe_dma_fifo_buf) { 44588c2ecf20Sopenharmony_ci pr_err("%s: DMA FIFO buffer allocation failed.\n", __func__); 44598c2ecf20Sopenharmony_ci iounmap(i2o_reg); 44608c2ecf20Sopenharmony_ci dcr_unmap(i2o_dcr_host, dcr_len); 44618c2ecf20Sopenharmony_ci return -ENOMEM; 44628c2ecf20Sopenharmony_ci } 44638c2ecf20Sopenharmony_ci 44648c2ecf20Sopenharmony_ci /* 44658c2ecf20Sopenharmony_ci * Configure h/w 44668c2ecf20Sopenharmony_ci */ 44678c2ecf20Sopenharmony_ci /* Reset I2O/DMA */ 44688c2ecf20Sopenharmony_ci mtdcri(SDR0, DCRN_SDR0_SRST, DCRN_SDR0_SRST_I2ODMA); 44698c2ecf20Sopenharmony_ci mtdcri(SDR0, DCRN_SDR0_SRST, 0); 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci /* Setup the base address of mmaped registers */ 44728c2ecf20Sopenharmony_ci dcr_write(i2o_dcr_host, DCRN_I2O0_IBAH, (u32)(i2o_res.start >> 32)); 44738c2ecf20Sopenharmony_ci dcr_write(i2o_dcr_host, DCRN_I2O0_IBAL, (u32)(i2o_res.start) | 44748c2ecf20Sopenharmony_ci I2O_REG_ENABLE); 44758c2ecf20Sopenharmony_ci dcr_unmap(i2o_dcr_host, dcr_len); 44768c2ecf20Sopenharmony_ci 44778c2ecf20Sopenharmony_ci /* Setup FIFO memory space base address */ 44788c2ecf20Sopenharmony_ci iowrite32(0, &i2o_reg->ifbah); 44798c2ecf20Sopenharmony_ci iowrite32(((u32)__pa(ppc440spe_dma_fifo_buf)), &i2o_reg->ifbal); 44808c2ecf20Sopenharmony_ci 44818c2ecf20Sopenharmony_ci /* set zero FIFO size for I2O, so the whole 44828c2ecf20Sopenharmony_ci * ppc440spe_dma_fifo_buf is used by DMAs. 44838c2ecf20Sopenharmony_ci * DMAx_FIFOs will be configured while probe. 44848c2ecf20Sopenharmony_ci */ 44858c2ecf20Sopenharmony_ci iowrite32(0, &i2o_reg->ifsiz); 44868c2ecf20Sopenharmony_ci iounmap(i2o_reg); 44878c2ecf20Sopenharmony_ci 44888c2ecf20Sopenharmony_ci /* To prepare WXOR/RXOR functionality we need access to 44898c2ecf20Sopenharmony_ci * Memory Queue Module DCRs (finally it will be enabled 44908c2ecf20Sopenharmony_ci * via /sys interface of the ppc440spe ADMA driver). 44918c2ecf20Sopenharmony_ci */ 44928c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ibm,mq-440spe"); 44938c2ecf20Sopenharmony_ci if (!np) { 44948c2ecf20Sopenharmony_ci pr_err("%s: can't find MQ device tree node\n", 44958c2ecf20Sopenharmony_ci __func__); 44968c2ecf20Sopenharmony_ci ret = -ENODEV; 44978c2ecf20Sopenharmony_ci goto out_free; 44988c2ecf20Sopenharmony_ci } 44998c2ecf20Sopenharmony_ci 45008c2ecf20Sopenharmony_ci /* Get MQ DCRs base */ 45018c2ecf20Sopenharmony_ci dcr_base = dcr_resource_start(np, 0); 45028c2ecf20Sopenharmony_ci dcr_len = dcr_resource_len(np, 0); 45038c2ecf20Sopenharmony_ci if (!dcr_base && !dcr_len) { 45048c2ecf20Sopenharmony_ci pr_err("%pOF: can't get DCR registers base/len!\n", np); 45058c2ecf20Sopenharmony_ci ret = -ENODEV; 45068c2ecf20Sopenharmony_ci goto out_mq; 45078c2ecf20Sopenharmony_ci } 45088c2ecf20Sopenharmony_ci 45098c2ecf20Sopenharmony_ci ppc440spe_mq_dcr_host = dcr_map(np, dcr_base, dcr_len); 45108c2ecf20Sopenharmony_ci if (!DCR_MAP_OK(ppc440spe_mq_dcr_host)) { 45118c2ecf20Sopenharmony_ci pr_err("%pOF: failed to map DCRs!\n", np); 45128c2ecf20Sopenharmony_ci ret = -ENODEV; 45138c2ecf20Sopenharmony_ci goto out_mq; 45148c2ecf20Sopenharmony_ci } 45158c2ecf20Sopenharmony_ci of_node_put(np); 45168c2ecf20Sopenharmony_ci ppc440spe_mq_dcr_len = dcr_len; 45178c2ecf20Sopenharmony_ci 45188c2ecf20Sopenharmony_ci /* Set HB alias */ 45198c2ecf20Sopenharmony_ci dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_BAUH, DMA_CUED_XOR_HB); 45208c2ecf20Sopenharmony_ci 45218c2ecf20Sopenharmony_ci /* Set: 45228c2ecf20Sopenharmony_ci * - LL transaction passing limit to 1; 45238c2ecf20Sopenharmony_ci * - Memory controller cycle limit to 1; 45248c2ecf20Sopenharmony_ci * - Galois Polynomial to 0x14d (default) 45258c2ecf20Sopenharmony_ci */ 45268c2ecf20Sopenharmony_ci dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, 45278c2ecf20Sopenharmony_ci (1 << MQ0_CFBHL_TPLM) | (1 << MQ0_CFBHL_HBCL) | 45288c2ecf20Sopenharmony_ci (PPC440SPE_DEFAULT_POLY << MQ0_CFBHL_POLY)); 45298c2ecf20Sopenharmony_ci 45308c2ecf20Sopenharmony_ci atomic_set(&ppc440spe_adma_err_irq_ref, 0); 45318c2ecf20Sopenharmony_ci for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) 45328c2ecf20Sopenharmony_ci ppc440spe_adma_devices[i] = -1; 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci return 0; 45358c2ecf20Sopenharmony_ci 45368c2ecf20Sopenharmony_ciout_mq: 45378c2ecf20Sopenharmony_ci of_node_put(np); 45388c2ecf20Sopenharmony_ciout_free: 45398c2ecf20Sopenharmony_ci kfree(ppc440spe_dma_fifo_buf); 45408c2ecf20Sopenharmony_ci return ret; 45418c2ecf20Sopenharmony_ci} 45428c2ecf20Sopenharmony_ci 45438c2ecf20Sopenharmony_cistatic const struct of_device_id ppc440spe_adma_of_match[] = { 45448c2ecf20Sopenharmony_ci { .compatible = "ibm,dma-440spe", }, 45458c2ecf20Sopenharmony_ci { .compatible = "amcc,xor-accelerator", }, 45468c2ecf20Sopenharmony_ci {}, 45478c2ecf20Sopenharmony_ci}; 45488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match); 45498c2ecf20Sopenharmony_ci 45508c2ecf20Sopenharmony_cistatic struct platform_driver ppc440spe_adma_driver = { 45518c2ecf20Sopenharmony_ci .probe = ppc440spe_adma_probe, 45528c2ecf20Sopenharmony_ci .remove = ppc440spe_adma_remove, 45538c2ecf20Sopenharmony_ci .driver = { 45548c2ecf20Sopenharmony_ci .name = "PPC440SP(E)-ADMA", 45558c2ecf20Sopenharmony_ci .of_match_table = ppc440spe_adma_of_match, 45568c2ecf20Sopenharmony_ci }, 45578c2ecf20Sopenharmony_ci}; 45588c2ecf20Sopenharmony_ci 45598c2ecf20Sopenharmony_cistatic __init int ppc440spe_adma_init(void) 45608c2ecf20Sopenharmony_ci{ 45618c2ecf20Sopenharmony_ci int ret; 45628c2ecf20Sopenharmony_ci 45638c2ecf20Sopenharmony_ci ret = ppc440spe_configure_raid_devices(); 45648c2ecf20Sopenharmony_ci if (ret) 45658c2ecf20Sopenharmony_ci return ret; 45668c2ecf20Sopenharmony_ci 45678c2ecf20Sopenharmony_ci ret = platform_driver_register(&ppc440spe_adma_driver); 45688c2ecf20Sopenharmony_ci if (ret) { 45698c2ecf20Sopenharmony_ci pr_err("%s: failed to register platform driver\n", 45708c2ecf20Sopenharmony_ci __func__); 45718c2ecf20Sopenharmony_ci goto out_reg; 45728c2ecf20Sopenharmony_ci } 45738c2ecf20Sopenharmony_ci 45748c2ecf20Sopenharmony_ci /* Initialization status */ 45758c2ecf20Sopenharmony_ci ret = driver_create_file(&ppc440spe_adma_driver.driver, 45768c2ecf20Sopenharmony_ci &driver_attr_devices); 45778c2ecf20Sopenharmony_ci if (ret) 45788c2ecf20Sopenharmony_ci goto out_dev; 45798c2ecf20Sopenharmony_ci 45808c2ecf20Sopenharmony_ci /* RAID-6 h/w enable entry */ 45818c2ecf20Sopenharmony_ci ret = driver_create_file(&ppc440spe_adma_driver.driver, 45828c2ecf20Sopenharmony_ci &driver_attr_enable); 45838c2ecf20Sopenharmony_ci if (ret) 45848c2ecf20Sopenharmony_ci goto out_en; 45858c2ecf20Sopenharmony_ci 45868c2ecf20Sopenharmony_ci /* GF polynomial to use */ 45878c2ecf20Sopenharmony_ci ret = driver_create_file(&ppc440spe_adma_driver.driver, 45888c2ecf20Sopenharmony_ci &driver_attr_poly); 45898c2ecf20Sopenharmony_ci if (!ret) 45908c2ecf20Sopenharmony_ci return ret; 45918c2ecf20Sopenharmony_ci 45928c2ecf20Sopenharmony_ci driver_remove_file(&ppc440spe_adma_driver.driver, 45938c2ecf20Sopenharmony_ci &driver_attr_enable); 45948c2ecf20Sopenharmony_ciout_en: 45958c2ecf20Sopenharmony_ci driver_remove_file(&ppc440spe_adma_driver.driver, 45968c2ecf20Sopenharmony_ci &driver_attr_devices); 45978c2ecf20Sopenharmony_ciout_dev: 45988c2ecf20Sopenharmony_ci /* User will not be able to enable h/w RAID-6 */ 45998c2ecf20Sopenharmony_ci pr_err("%s: failed to create RAID-6 driver interface\n", 46008c2ecf20Sopenharmony_ci __func__); 46018c2ecf20Sopenharmony_ci platform_driver_unregister(&ppc440spe_adma_driver); 46028c2ecf20Sopenharmony_ciout_reg: 46038c2ecf20Sopenharmony_ci dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len); 46048c2ecf20Sopenharmony_ci kfree(ppc440spe_dma_fifo_buf); 46058c2ecf20Sopenharmony_ci return ret; 46068c2ecf20Sopenharmony_ci} 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_cistatic void __exit ppc440spe_adma_exit(void) 46098c2ecf20Sopenharmony_ci{ 46108c2ecf20Sopenharmony_ci driver_remove_file(&ppc440spe_adma_driver.driver, 46118c2ecf20Sopenharmony_ci &driver_attr_poly); 46128c2ecf20Sopenharmony_ci driver_remove_file(&ppc440spe_adma_driver.driver, 46138c2ecf20Sopenharmony_ci &driver_attr_enable); 46148c2ecf20Sopenharmony_ci driver_remove_file(&ppc440spe_adma_driver.driver, 46158c2ecf20Sopenharmony_ci &driver_attr_devices); 46168c2ecf20Sopenharmony_ci platform_driver_unregister(&ppc440spe_adma_driver); 46178c2ecf20Sopenharmony_ci dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len); 46188c2ecf20Sopenharmony_ci kfree(ppc440spe_dma_fifo_buf); 46198c2ecf20Sopenharmony_ci} 46208c2ecf20Sopenharmony_ci 46218c2ecf20Sopenharmony_ciarch_initcall(ppc440spe_adma_init); 46228c2ecf20Sopenharmony_cimodule_exit(ppc440spe_adma_exit); 46238c2ecf20Sopenharmony_ci 46248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yuri Tikhonov <yur@emcraft.com>"); 46258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PPC440SPE ADMA Engine Driver"); 46268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4627