Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/dma/ppc4xx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 123 kB image not shown  

Quelle  adma.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2006-2009 DENX Software Engineering.
 *
 * Author: Yuri Tikhonov <yur@emcraft.com>
 *
 * Further porting to arch/powerpc by
 *  Anatolij Gustschin <agust@denx.de>
 */


/*
 * This driver supports the asynchronous DMA copy and RAID engines available
 * on the AMCC PPC440SPe Processors.
 * Based on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x)
 * ADMA driver written by D.Williams.
 */


#include <linux/init.h>
#include <linux/module.h>
#include <linux/async_tx.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <asm/dcr.h>
#include <asm/dcr-regs.h>
#include "adma.h"
#include "../dmaengine.h"

enum ppc_adma_init_code {
 PPC_ADMA_INIT_OK = 0,
 PPC_ADMA_INIT_MEMRES,
 PPC_ADMA_INIT_MEMREG,
 PPC_ADMA_INIT_ALLOC,
 PPC_ADMA_INIT_COHERENT,
 PPC_ADMA_INIT_CHANNEL,
 PPC_ADMA_INIT_IRQ1,
 PPC_ADMA_INIT_IRQ2,
 PPC_ADMA_INIT_REGISTER
};

static char *ppc_adma_errors[] = {
 [PPC_ADMA_INIT_OK] = "ok",
 [PPC_ADMA_INIT_MEMRES] = "failed to get memory resource",
 [PPC_ADMA_INIT_MEMREG] = "failed to request memory region",
 [PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev "
    "structure",
 [PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for "
       "hardware descriptors",
 [PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel",
 [PPC_ADMA_INIT_IRQ1] = "failed to request first irq",
 [PPC_ADMA_INIT_IRQ2] = "failed to request second irq",
 [PPC_ADMA_INIT_REGISTER] = "failed to register dma async device",
};

static enum ppc_adma_init_code
ppc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM];

struct ppc_dma_chan_ref {
 struct dma_chan *chan;
 struct list_head node;
};

/* The list of channels exported by ppc440spe ADMA */
static struct list_head
ppc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list);

/* This flag is set when want to refetch the xor chain in the interrupt
 * handler
 */

static u32 do_xor_refetch;

/* Pointer to DMA0, DMA1 CP/CS FIFO */
static void *ppc440spe_dma_fifo_buf;

/* Pointers to last submitted to DMA0, DMA1 CDBs */
static struct ppc440spe_adma_desc_slot *chan_last_sub[3];
static struct ppc440spe_adma_desc_slot *chan_first_cdb[3];

/* Pointer to last linked and submitted xor CB */
static struct ppc440spe_adma_desc_slot *xor_last_linked;
static struct ppc440spe_adma_desc_slot *xor_last_submit;

/* This array is used in data-check operations for storing a pattern */
static char ppc440spe_qword[16];

static atomic_t ppc440spe_adma_err_irq_ref;
static dcr_host_t ppc440spe_mq_dcr_host;
static unsigned int ppc440spe_mq_dcr_len;

/* Since RXOR operations use the common register (MQ0_CF2H) for setting-up
 * the block size in transactions, then we do not allow to activate more than
 * only one RXOR transactions simultaneously. So use this var to store
 * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is
 * set) or not (PPC440SPE_RXOR_RUN is clear).
 */

static unsigned long ppc440spe_rxor_state;

/* These are used in enable & check routines
 */

static u32 ppc440spe_r6_enabled;
static struct ppc440spe_adma_chan *ppc440spe_r6_tchan;
static struct completion ppc440spe_r6_test_comp;

static int ppc440spe_adma_dma2rxor_prep_src(
  struct ppc440spe_adma_desc_slot *desc,
  struct ppc440spe_rxor *cursor, int index,
  int src_cnt, u32 addr);
static void ppc440spe_adma_dma2rxor_set_src(
  struct ppc440spe_adma_desc_slot *desc,
  int index, dma_addr_t addr);
static void ppc440spe_adma_dma2rxor_set_mult(
  struct ppc440spe_adma_desc_slot *desc,
  int index, u8 mult);

#ifdef ADMA_LL_DEBUG
#define ADMA_LL_DBG(x) ({ if (1) x; 0; })
#else
#define ADMA_LL_DBG(x) ({ if (0) x; 0; })
#endif

static void print_cb(struct ppc440spe_adma_chan *chan, void *block)
{
 struct dma_cdb *cdb;
 struct xor_cb *cb;
 int i;

 switch (chan->device->id) {
 case 0:
 case 1:
  cdb = block;

  pr_debug("CDB at %p [%d]:\n"
   "\t attr 0x%02x opc 0x%02x cnt 0x%08x\n"
   "\t sg1u 0x%08x sg1l 0x%08x\n"
   "\t sg2u 0x%08x sg2l 0x%08x\n"
   "\t sg3u 0x%08x sg3l 0x%08x\n",
   cdb, chan->device->id,
   cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt),
   le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l),
   le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l),
   le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l)
  );
  break;
 case 2:
  cb = block;

  pr_debug("CB at %p [%d]:\n"
   "\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n"
   "\t cbtah 0x%08x cbtal 0x%08x\n"
   "\t cblah 0x%08x cblal 0x%08x\n",
   cb, chan->device->id,
   cb->cbc, cb->cbbc, cb->cbs,
   cb->cbtah, cb->cbtal,
   cb->cblah, cb->cblal);
  for (i = 0; i < 16; i++) {
   if (i && !cb->ops[i].h && !cb->ops[i].l)
    continue;
   pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n",
    i, cb->ops[i].h, cb->ops[i].l);
  }
  break;
 }
}

static void print_cb_list(struct ppc440spe_adma_chan *chan,
     struct ppc440spe_adma_desc_slot *iter)
{
 for (; iter; iter = iter->hw_next)
  print_cb(chan, iter->hw_desc);
}

static void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src,
        unsigned int src_cnt)
{
 int i;

 pr_debug("\n%s(%d):\nsrc: ", __func__, id);
 for (i = 0; i < src_cnt; i++)
  pr_debug("\t0x%016llx ", src[i]);
 pr_debug("dst:\n\t0x%016llx\n", dst);
}

static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
       unsigned int src_cnt)
{
 int i;

 pr_debug("\n%s(%d):\nsrc: ", __func__, id);
 for (i = 0; i < src_cnt; i++)
  pr_debug("\t0x%016llx ", src[i]);
 pr_debug("dst: ");
 for (i = 0; i < 2; i++)
  pr_debug("\t0x%016llx ", dst[i]);
}

static void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
        unsigned int src_cnt,
        const unsigned char *scf)
{
 int i;

 pr_debug("\n%s(%d):\nsrc(coef): ", __func__, id);
 if (scf) {
  for (i = 0; i < src_cnt; i++)
   pr_debug("\t0x%016llx(0x%02x) ", src[i], scf[i]);
 } else {
  for (i = 0; i < src_cnt; i++)
   pr_debug("\t0x%016llx(no) ", src[i]);
 }

 pr_debug("dst: ");
 for (i = 0; i < 2; i++)
  pr_debug("\t0x%016llx ", src[src_cnt + i]);
}

/******************************************************************************
 * Command (Descriptor) Blocks low-level routines
 ******************************************************************************/

/**
 * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT
 * pseudo operation
 */

static void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot *desc,
       struct ppc440spe_adma_chan *chan)
{
 struct xor_cb *p;

 switch (chan->device->id) {
 case PPC440SPE_XOR_ID:
  p = desc->hw_desc;
  memset(desc->hw_desc, 0, sizeof(struct xor_cb));
  /* NOP with Command Block Complete Enable */
  p->cbc = XOR_CBCR_CBCE_BIT;
  break;
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
  /* NOP with interrupt */
  set_bit(PPC440SPE_DESC_INT, &desc->flags);
  break;
 default:
  printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id,
    __func__);
  break;
 }
}

/**
 * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR
 * pseudo operation
 */

static void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc)
{
 memset(desc->hw_desc, 0, sizeof(struct xor_cb));
 desc->hw_next = NULL;
 desc->src_cnt = 0;
 desc->dst_cnt = 1;
}

/**
 * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation
 */

static void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc,
      int src_cnt, unsigned long flags)
{
 struct xor_cb *hw_desc = desc->hw_desc;

 memset(desc->hw_desc, 0, sizeof(struct xor_cb));
 desc->hw_next = NULL;
 desc->src_cnt = src_cnt;
 desc->dst_cnt = 1;

 hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt;
 if (flags & DMA_PREP_INTERRUPT)
  /* Enable interrupt on completion */
  hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
}

/**
 * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ
 * operation in DMA2 controller
 */

static void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc,
  int dst_cnt, int src_cnt, unsigned long flags)
{
 struct xor_cb *hw_desc = desc->hw_desc;

 memset(desc->hw_desc, 0, sizeof(struct xor_cb));
 desc->hw_next = NULL;
 desc->src_cnt = src_cnt;
 desc->dst_cnt = dst_cnt;
 memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags));
 desc->descs_per_op = 0;

 hw_desc->cbc = XOR_CBCR_TGT_BIT;
 if (flags & DMA_PREP_INTERRUPT)
  /* Enable interrupt on completion */
  hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
}

#define DMA_CTRL_FLAGS_LAST DMA_PREP_FENCE
#define DMA_PREP_ZERO_P  (DMA_CTRL_FLAGS_LAST << 1)
#define DMA_PREP_ZERO_Q  (DMA_PREP_ZERO_P << 1)

/**
 * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation
 * with DMA0/1
 */

static void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc,
    int dst_cnt, int src_cnt, unsigned long flags,
    unsigned long op)
{
 struct dma_cdb *hw_desc;
 struct ppc440spe_adma_desc_slot *iter;
 u8 dopc;

 /* Common initialization of a PQ descriptors chain */
 set_bits(op, &desc->flags);
 desc->src_cnt = src_cnt;
 desc->dst_cnt = dst_cnt;

 /* WXOR MULTICAST if both P and Q are being computed
 * MV_SG1_SG2 if Q only
 */

 dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ?
  DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2;

 list_for_each_entry(iter, &desc->group_list, chain_node) {
  hw_desc = iter->hw_desc;
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));

  if (likely(!list_is_last(&iter->chain_node,
    &desc->group_list))) {
   /* set 'next' pointer */
   iter->hw_next = list_entry(iter->chain_node.next,
    struct ppc440spe_adma_desc_slot, chain_node);
   clear_bit(PPC440SPE_DESC_INT, &iter->flags);
  } else {
   /* this is the last descriptor.
 * this slot will be pasted from ADMA level
 * each time it wants to configure parameters
 * of the transaction (src, dst, ...)
 */

   iter->hw_next = NULL;
   if (flags & DMA_PREP_INTERRUPT)
    set_bit(PPC440SPE_DESC_INT, &iter->flags);
   else
    clear_bit(PPC440SPE_DESC_INT, &iter->flags);
  }
 }

 /* Set OPS depending on WXOR/RXOR type of operation */
 if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) {
  /* This is a WXOR only chain:
 * - first descriptors are for zeroing destinations
 *   if PPC440SPE_ZERO_P/Q set;
 * - descriptors remained are for GF-XOR operations.
 */

  iter = list_first_entry(&desc->group_list,
     struct ppc440spe_adma_desc_slot,
     chain_node);

  if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) {
   hw_desc = iter->hw_desc;
   hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
   iter = list_first_entry(&iter->chain_node,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  }

  if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) {
   hw_desc = iter->hw_desc;
   hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
   iter = list_first_entry(&iter->chain_node,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  }

  list_for_each_entry_from(iter, &desc->group_list, chain_node) {
   hw_desc = iter->hw_desc;
   hw_desc->opc = dopc;
  }
 } else {
  /* This is either RXOR-only or mixed RXOR/WXOR */

  /* The first 1 or 2 slots in chain are always RXOR,
 * if need to calculate P & Q, then there are two
 * RXOR slots; if only P or only Q, then there is one
 */

  iter = list_first_entry(&desc->group_list,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;

  if (desc->dst_cnt == DMA_DEST_MAX_NUM) {
   iter = list_first_entry(&iter->chain_node,
      struct ppc440spe_adma_desc_slot,
      chain_node);
   hw_desc = iter->hw_desc;
   hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
  }

  /* The remaining descs (if any) are WXORs */
  if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) {
   iter = list_first_entry(&iter->chain_node,
      struct ppc440spe_adma_desc_slot,
      chain_node);
   list_for_each_entry_from(iter, &desc->group_list,
      chain_node) {
    hw_desc = iter->hw_desc;
    hw_desc->opc = dopc;
   }
  }
 }
}

/**
 * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor
 * for PQ_ZERO_SUM operation
 */

static void ppc440spe_desc_init_dma01pqzero_sum(
    struct ppc440spe_adma_desc_slot *desc,
    int dst_cnt, int src_cnt)
{
 struct dma_cdb *hw_desc;
 struct ppc440spe_adma_desc_slot *iter;
 int i = 0;
 u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST :
       DMA_CDB_OPC_MV_SG1_SG2;
 /*
 * Initialize starting from 2nd or 3rd descriptor dependent
 * on dst_cnt. First one or two slots are for cloning P
 * and/or Q to chan->pdest and/or chan->qdest as we have
 * to preserve original P/Q.
 */

 iter = list_first_entry(&desc->group_list,
    struct ppc440spe_adma_desc_slot, chain_node);
 iter = list_entry(iter->chain_node.next,
     struct ppc440spe_adma_desc_slot, chain_node);

 if (dst_cnt > 1) {
  iter = list_entry(iter->chain_node.next,
      struct ppc440spe_adma_desc_slot, chain_node);
 }
 /* initialize each source descriptor in chain */
 list_for_each_entry_from(iter, &desc->group_list, chain_node) {
  hw_desc = iter->hw_desc;
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  iter->src_cnt = 0;
  iter->dst_cnt = 0;

  /* This is a ZERO_SUM operation:
 * - <src_cnt> descriptors starting from 2nd or 3rd
 *   descriptor are for GF-XOR operations;
 * - remaining <dst_cnt> descriptors are for checking the result
 */

  if (i++ < src_cnt)
   /* MV_SG1_SG2 if only Q is being verified
 * MULTICAST if both P and Q are being verified
 */

   hw_desc->opc = dopc;
  else
   /* DMA_CDB_OPC_DCHECK128 operation */
   hw_desc->opc = DMA_CDB_OPC_DCHECK128;

  if (likely(!list_is_last(&iter->chain_node,
      &desc->group_list))) {
   /* set 'next' pointer */
   iter->hw_next = list_entry(iter->chain_node.next,
      struct ppc440spe_adma_desc_slot,
      chain_node);
  } else {
   /* this is the last descriptor.
 * this slot will be pasted from ADMA level
 * each time it wants to configure parameters
 * of the transaction (src, dst, ...)
 */

   iter->hw_next = NULL;
   /* always enable interrupt generation since we get
 * the status of pqzero from the handler
 */

   set_bit(PPC440SPE_DESC_INT, &iter->flags);
  }
 }
 desc->src_cnt = src_cnt;
 desc->dst_cnt = dst_cnt;
}

/**
 * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation
 */

static void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc,
     unsigned long flags)
{
 struct dma_cdb *hw_desc = desc->hw_desc;

 memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
 desc->hw_next = NULL;
 desc->src_cnt = 1;
 desc->dst_cnt = 1;

 if (flags & DMA_PREP_INTERRUPT)
  set_bit(PPC440SPE_DESC_INT, &desc->flags);
 else
  clear_bit(PPC440SPE_DESC_INT, &desc->flags);

 hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
}

/**
 * ppc440spe_desc_set_src_addr - set source address into the descriptor
 */

static void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc,
     struct ppc440spe_adma_chan *chan,
     int src_idx, dma_addr_t addrh,
     dma_addr_t addrl)
{
 struct dma_cdb *dma_hw_desc;
 struct xor_cb *xor_hw_desc;
 phys_addr_t addr64, tmplow, tmphi;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  if (!addrh) {
   addr64 = addrl;
   tmphi = (addr64 >> 32);
   tmplow = (addr64 & 0xFFFFFFFF);
  } else {
   tmphi = addrh;
   tmplow = addrl;
  }
  dma_hw_desc = desc->hw_desc;
  dma_hw_desc->sg1l = cpu_to_le32((u32)tmplow);
  dma_hw_desc->sg1u |= cpu_to_le32((u32)tmphi);
  break;
 case PPC440SPE_XOR_ID:
  xor_hw_desc = desc->hw_desc;
  xor_hw_desc->ops[src_idx].l = addrl;
  xor_hw_desc->ops[src_idx].h |= addrh;
  break;
 }
}

/**
 * ppc440spe_desc_set_src_mult - set source address mult into the descriptor
 */

static void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
   struct ppc440spe_adma_chan *chan, u32 mult_index,
   int sg_index, unsigned char mult_value)
{
 struct dma_cdb *dma_hw_desc;
 u32 *psgu;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  dma_hw_desc = desc->hw_desc;

  switch (sg_index) {
  /* for RXOR operations set multiplier
 * into source cued address
 */

  case DMA_CDB_SG_SRC:
   psgu = &dma_hw_desc->sg1u;
   break;
  /* for WXOR operations set multiplier
 * into destination cued address(es)
 */

  case DMA_CDB_SG_DST1:
   psgu = &dma_hw_desc->sg2u;
   break;
  case DMA_CDB_SG_DST2:
   psgu = &dma_hw_desc->sg3u;
   break;
  default:
   BUG();
  }

  *psgu |= cpu_to_le32(mult_value << mult_index);
  break;
 case PPC440SPE_XOR_ID:
  break;
 default:
  BUG();
 }
}

/**
 * ppc440spe_desc_set_dest_addr - set destination address into the descriptor
 */

static void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc,
    struct ppc440spe_adma_chan *chan,
    dma_addr_t addrh, dma_addr_t addrl,
    u32 dst_idx)
{
 struct dma_cdb *dma_hw_desc;
 struct xor_cb *xor_hw_desc;
 phys_addr_t addr64, tmphi, tmplow;
 u32 *psgu, *psgl;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  if (!addrh) {
   addr64 = addrl;
   tmphi = (addr64 >> 32);
   tmplow = (addr64 & 0xFFFFFFFF);
  } else {
   tmphi = addrh;
   tmplow = addrl;
  }
  dma_hw_desc = desc->hw_desc;

  psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u;
  psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l;

  *psgl = cpu_to_le32((u32)tmplow);
  *psgu |= cpu_to_le32((u32)tmphi);
  break;
 case PPC440SPE_XOR_ID:
  xor_hw_desc = desc->hw_desc;
  xor_hw_desc->cbtal = addrl;
  xor_hw_desc->cbtah |= addrh;
  break;
 }
}

/**
 * ppc440spe_desc_set_byte_count - set number of data bytes involved
 * into the operation
 */

static void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot *desc,
    struct ppc440spe_adma_chan *chan,
    u32 byte_count)
{
 struct dma_cdb *dma_hw_desc;
 struct xor_cb *xor_hw_desc;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  dma_hw_desc = desc->hw_desc;
  dma_hw_desc->cnt = cpu_to_le32(byte_count);
  break;
 case PPC440SPE_XOR_ID:
  xor_hw_desc = desc->hw_desc;
  xor_hw_desc->cbbc = byte_count;
  break;
 }
}

/**
 * ppc440spe_desc_set_rxor_block_size - set RXOR block size
 */

static inline void ppc440spe_desc_set_rxor_block_size(u32 byte_count)
{
 /* assume that byte_count is aligned on the 512-boundary;
 * thus write it directly to the register (bits 23:31 are
 * reserved there).
 */

 dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count);
}

/**
 * ppc440spe_desc_set_dcheck - set CHECK pattern
 */

static void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc,
    struct ppc440spe_adma_chan *chan, u8 *qword)
{
 struct dma_cdb *dma_hw_desc;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  dma_hw_desc = desc->hw_desc;
  iowrite32(qword[0], &dma_hw_desc->sg3l);
  iowrite32(qword[4], &dma_hw_desc->sg3u);
  iowrite32(qword[8], &dma_hw_desc->sg2l);
  iowrite32(qword[12], &dma_hw_desc->sg2u);
  break;
 default:
  BUG();
 }
}

/**
 * ppc440spe_xor_set_link - set link address in xor CB
 */

static void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
    struct ppc440spe_adma_desc_slot *next_desc)
{
 struct xor_cb *xor_hw_desc = prev_desc->hw_desc;

 if (unlikely(!next_desc || !(next_desc->phys))) {
  printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n",
   __func__, next_desc,
   next_desc ? next_desc->phys : 0);
  BUG();
 }

 xor_hw_desc->cbs = 0;
 xor_hw_desc->cblal = next_desc->phys;
 xor_hw_desc->cblah = 0;
 xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT;
}

/**
 * ppc440spe_desc_set_link - set the address of descriptor following this
 * descriptor in chain
 */

static void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
    struct ppc440spe_adma_desc_slot *prev_desc,
    struct ppc440spe_adma_desc_slot *next_desc)
{
 unsigned long flags;
 struct ppc440spe_adma_desc_slot *tail = next_desc;

 if (unlikely(!prev_desc || !next_desc ||
  (prev_desc->hw_next && prev_desc->hw_next != next_desc))) {
  /* If previous next is overwritten something is wrong.
 * though we may refetch from append to initiate list
 * processing; in this case - it's ok.
 */

  printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; "
   "prev->hw_next=0x%p\n", __func__, prev_desc,
   next_desc, prev_desc ? prev_desc->hw_next : 0);
  BUG();
 }

 local_irq_save(flags);

 /* do s/w chaining both for DMA and XOR descriptors */
 prev_desc->hw_next = next_desc;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  break;
 case PPC440SPE_XOR_ID:
  /* bind descriptor to the chain */
  while (tail->hw_next)
   tail = tail->hw_next;
  xor_last_linked = tail;

  if (prev_desc == xor_last_submit)
   /* do not link to the last submitted CB */
   break;
  ppc440spe_xor_set_link(prev_desc, next_desc);
  break;
 }

 local_irq_restore(flags);
}

/**
 * ppc440spe_desc_get_link - get the address of the descriptor that
 * follows this one
 */

static inline u32 ppc440spe_desc_get_link(struct ppc440spe_adma_desc_slot *desc,
     struct ppc440spe_adma_chan *chan)
{
 if (!desc->hw_next)
  return 0;

 return desc->hw_next->phys;
}

/**
 * ppc440spe_desc_is_aligned - check alignment
 */

static inline int ppc440spe_desc_is_aligned(
 struct ppc440spe_adma_desc_slot *desc, int num_slots)
{
 return (desc->idx & (num_slots - 1)) ? 0 : 1;
}

/**
 * ppc440spe_chan_xor_slot_count - get the number of slots necessary for
 * XOR operation
 */

static int ppc440spe_chan_xor_slot_count(size_t len, int src_cnt,
   int *slots_per_op)
{
 int slot_cnt;

 /* each XOR descriptor provides up to 16 source operands */
 slot_cnt = *slots_per_op = (src_cnt + XOR_MAX_OPS - 1)/XOR_MAX_OPS;

 if (likely(len <= PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT))
  return slot_cnt;

 printk(KERN_ERR "%s: len %d > max %d !!\n",
  __func__, len, PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
 BUG();
 return slot_cnt;
}

/**
 * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for
 * DMA2 PQ operation
 */

static int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs,
  int src_cnt, size_t len)
{
 signed long long order = 0;
 int state = 0;
 int addr_count = 0;
 int i;
 for (i = 1; i < src_cnt; i++) {
  dma_addr_t cur_addr = srcs[i];
  dma_addr_t old_addr = srcs[i-1];
  switch (state) {
  case 0:
   if (cur_addr == old_addr + len) {
    /* direct RXOR */
    order = 1;
    state = 1;
    if (i == src_cnt-1)
     addr_count++;
   } else if (old_addr == cur_addr + len) {
    /* reverse RXOR */
    order = -1;
    state = 1;
    if (i == src_cnt-1)
     addr_count++;
   } else {
    state = 3;
   }
   break;
  case 1:
   if (i == src_cnt-2 || (order == -1
    && cur_addr != old_addr - len)) {
    order = 0;
    state = 0;
    addr_count++;
   } else if (cur_addr == old_addr + len*order) {
    state = 2;
    if (i == src_cnt-1)
     addr_count++;
   } else if (cur_addr == old_addr + 2*len) {
    state = 2;
    if (i == src_cnt-1)
     addr_count++;
   } else if (cur_addr == old_addr + 3*len) {
    state = 2;
    if (i == src_cnt-1)
     addr_count++;
   } else {
    order = 0;
    state = 0;
    addr_count++;
   }
   break;
  case 2:
   order = 0;
   state = 0;
   addr_count++;
    break;
  }
  if (state == 3)
   break;
 }
 if (src_cnt <= 1 || (state != 1 && state != 2)) {
  pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n",
   __func__, src_cnt, state, addr_count, order);
  for (i = 0; i < src_cnt; i++)
   pr_err("\t[%d] 0x%llx \n", i, srcs[i]);
  BUG();
 }

 return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS;
}


/******************************************************************************
 * ADMA channel low-level routines
 ******************************************************************************/


static u32
ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan);
static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan);

/**
 * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine
 */

static void ppc440spe_adma_device_clear_eot_status(
     struct ppc440spe_adma_chan *chan)
{
 struct dma_regs *dma_reg;
 struct xor_regs *xor_reg;
 u8 *p = chan->device->dma_desc_pool_virt;
 struct dma_cdb *cdb;
 u32 rv, i;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  /* read FIFO to ack */
  dma_reg = chan->device->dma_reg;
  while ((rv = ioread32(&dma_reg->csfpl))) {
   i = rv & DMA_CDB_ADDR_MSK;
   cdb = (struct dma_cdb *)&p[i -
       (u32)chan->device->dma_desc_pool];

   /* Clear opcode to ack. This is necessary for
 * ZeroSum operations only
 */

   cdb->opc = 0;

   if (test_bit(PPC440SPE_RXOR_RUN,
       &ppc440spe_rxor_state)) {
    /* probably this is a completed RXOR op,
 * get pointer to CDB using the fact that
 * physical and virtual addresses of CDB
 * in pools have the same offsets
 */

    if (le32_to_cpu(cdb->sg1u) &
        DMA_CUED_XOR_BASE) {
     /* this is a RXOR */
     clear_bit(PPC440SPE_RXOR_RUN,
        &ppc440spe_rxor_state);
    }
   }

   if (rv & DMA_CDB_STATUS_MSK) {
    /* ZeroSum check failed
 */

    struct ppc440spe_adma_desc_slot *iter;
    dma_addr_t phys = rv & ~DMA_CDB_MSK;

    /*
 * Update the status of corresponding
 * descriptor.
 */

    list_for_each_entry(iter, &chan->chain,
        chain_node) {
     if (iter->phys == phys)
      break;
    }
    /*
 * if cannot find the corresponding
 * slot it's a bug
 */

    BUG_ON(&iter->chain_node == &chan->chain);

    if (iter->xor_check_result) {
     if (test_bit(PPC440SPE_DESC_PCHECK,
           &iter->flags)) {
      *iter->xor_check_result |=
       SUM_CHECK_P_RESULT;
     } else
     if (test_bit(PPC440SPE_DESC_QCHECK,
           &iter->flags)) {
      *iter->xor_check_result |=
       SUM_CHECK_Q_RESULT;
     } else
      BUG();
    }
   }
  }

  rv = ioread32(&dma_reg->dsts);
  if (rv) {
   pr_err("DMA%d err status: 0x%x\n",
          chan->device->id, rv);
   /* write back to clear */
   iowrite32(rv, &dma_reg->dsts);
  }
  break;
 case PPC440SPE_XOR_ID:
  /* reset status bits to ack */
  xor_reg = chan->device->xor_reg;
  rv = ioread32be(&xor_reg->sr);
  iowrite32be(rv, &xor_reg->sr);

  if (rv & (XOR_IE_ICBIE_BIT|XOR_IE_ICIE_BIT|XOR_IE_RPTIE_BIT)) {
   if (rv & XOR_IE_RPTIE_BIT) {
    /* Read PLB Timeout Error.
 * Try to resubmit the CB
 */

    u32 val = ioread32be(&xor_reg->ccbalr);

    iowrite32be(val, &xor_reg->cblalr);

    val = ioread32be(&xor_reg->crsr);
    iowrite32be(val | XOR_CRSR_XAE_BIT,
         &xor_reg->crsr);
   } else
    pr_err("XOR ERR 0x%x status\n", rv);
   break;
  }

  /*  if the XORcore is idle, but there are unprocessed CBs
 * then refetch the s/w chain here
 */

  if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) &&
      do_xor_refetch)
   ppc440spe_chan_append(chan);
  break;
 }
}

/**
 * ppc440spe_chan_is_busy - get the channel status
 */

static int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan)
{
 struct dma_regs *dma_reg;
 struct xor_regs *xor_reg;
 int busy = 0;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  dma_reg = chan->device->dma_reg;
  /*  if command FIFO's head and tail pointers are equal and
 * status tail is the same as command, then channel is free
 */

  if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) ||
      ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp))
   busy = 1;
  break;
 case PPC440SPE_XOR_ID:
  /* use the special status bit for the XORcore
 */

  xor_reg = chan->device->xor_reg;
  busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0;
  break;
 }

 return busy;
}

/**
 * ppc440spe_chan_set_first_xor_descriptor -  init XORcore chain
 */

static void ppc440spe_chan_set_first_xor_descriptor(
    struct ppc440spe_adma_chan *chan,
    struct ppc440spe_adma_desc_slot *next_desc)
{
 struct xor_regs *xor_reg = chan->device->xor_reg;

 if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)
  printk(KERN_INFO "%s: Warn: XORcore is running "
   "when try to set the first CDB!\n",
   __func__);

 xor_last_submit = xor_last_linked = next_desc;

 iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr);

 iowrite32be(next_desc->phys, &xor_reg->cblalr);
 iowrite32be(0, &xor_reg->cblahr);
 iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT,
      &xor_reg->cbcr);

 chan->hw_chain_inited = 1;
}

/**
 * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO.
 * called with irqs disabled
 */

static void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
  struct ppc440spe_adma_desc_slot *desc)
{
 u32 pcdb;
 struct dma_regs *dma_reg = chan->device->dma_reg;

 pcdb = desc->phys;
 if (!test_bit(PPC440SPE_DESC_INT, &desc->flags))
  pcdb |= DMA_CDB_NO_INT;

 chan_last_sub[chan->device->id] = desc;

 ADMA_LL_DBG(print_cb(chan, desc->hw_desc));

 iowrite32(pcdb, &dma_reg->cpfpl);
}

/**
 * ppc440spe_chan_append - update the h/w chain in the channel
 */

static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan)
{
 struct xor_regs *xor_reg;
 struct ppc440spe_adma_desc_slot *iter;
 struct xor_cb *xcb;
 u32 cur_desc;
 unsigned long flags;

 local_irq_save(flags);

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  cur_desc = ppc440spe_chan_get_current_descriptor(chan);

  if (likely(cur_desc)) {
   iter = chan_last_sub[chan->device->id];
   BUG_ON(!iter);
  } else {
   /* first peer */
   iter = chan_first_cdb[chan->device->id];
   BUG_ON(!iter);
   ppc440spe_dma_put_desc(chan, iter);
   chan->hw_chain_inited = 1;
  }

  /* is there something new to append */
  if (!iter->hw_next)
   break;

  /* flush descriptors from the s/w queue to fifo */
  list_for_each_entry_continue(iter, &chan->chain, chain_node) {
   ppc440spe_dma_put_desc(chan, iter);
   if (!iter->hw_next)
    break;
  }
  break;
 case PPC440SPE_XOR_ID:
  /* update h/w links and refetch */
  if (!xor_last_submit->hw_next)
   break;

  xor_reg = chan->device->xor_reg;
  /* the last linked CDB has to generate an interrupt
 * that we'd be able to append the next lists to h/w
 * regardless of the XOR engine state at the moment of
 * appending of these next lists
 */

  xcb = xor_last_linked->hw_desc;
  xcb->cbc |= XOR_CBCR_CBCE_BIT;

  if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) {
   /* XORcore is idle. Refetch now */
   do_xor_refetch = 0;
   ppc440spe_xor_set_link(xor_last_submit,
    xor_last_submit->hw_next);

   ADMA_LL_DBG(print_cb_list(chan,
    xor_last_submit->hw_next));

   xor_last_submit = xor_last_linked;
   iowrite32be(ioread32be(&xor_reg->crsr) |
        XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT,
        &xor_reg->crsr);
  } else {
   /* XORcore is running. Refetch later in the handler */
   do_xor_refetch = 1;
  }

  break;
 }

 local_irq_restore(flags);
}

/**
 * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor
 */

static u32
ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan)
{
 struct dma_regs *dma_reg;
 struct xor_regs *xor_reg;

 if (unlikely(!chan->hw_chain_inited))
  /* h/w descriptor chain is not initialized yet */
  return 0;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  dma_reg = chan->device->dma_reg;
  return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK);
 case PPC440SPE_XOR_ID:
  xor_reg = chan->device->xor_reg;
  return ioread32be(&xor_reg->ccbalr);
 }
 return 0;
}

/**
 * ppc440spe_chan_run - enable the channel
 */

static void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan)
{
 struct xor_regs *xor_reg;

 switch (chan->device->id) {
 case PPC440SPE_DMA0_ID:
 case PPC440SPE_DMA1_ID:
  /* DMAs are always enabled, do nothing */
  break;
 case PPC440SPE_XOR_ID:
  /* drain write buffer */
  xor_reg = chan->device->xor_reg;

  /* fetch descriptor pointed to in <link> */
  iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT,
       &xor_reg->crsr);
  break;
 }
}

/******************************************************************************
 * ADMA device level
 ******************************************************************************/


static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan);
static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan);

static dma_cookie_t
ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx);

static void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *tx,
        dma_addr_t addr, int index);
static void
ppc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot *tx,
      dma_addr_t addr, int index);

static void
ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *tx,
      dma_addr_t *paddr, unsigned long flags);
static void
ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *tx,
     dma_addr_t addr, int index);
static void
ppc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot *tx,
          unsigned char mult, int index, int dst_pos);
static void
ppc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot *tx,
       dma_addr_t paddr, dma_addr_t qaddr);

static struct page *ppc440spe_rxor_srcs[32];

/**
 * ppc440spe_can_rxor - check if the operands may be processed with RXOR
 */

static int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len)
{
 int i, order = 0, state = 0;
 int idx = 0;

 if (unlikely(!(src_cnt > 1)))
  return 0;

 BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs));

 /* Skip holes in the source list before checking */
 for (i = 0; i < src_cnt; i++) {
  if (!srcs[i])
   continue;
  ppc440spe_rxor_srcs[idx++] = srcs[i];
 }
 src_cnt = idx;

 for (i = 1; i < src_cnt; i++) {
  char *cur_addr = page_address(ppc440spe_rxor_srcs[i]);
  char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]);

  switch (state) {
  case 0:
   if (cur_addr == old_addr + len) {
    /* direct RXOR */
    order = 1;
    state = 1;
   } else if (old_addr == cur_addr + len) {
    /* reverse RXOR */
    order = -1;
    state = 1;
   } else
    goto out;
   break;
  case 1:
   if ((i == src_cnt - 2) ||
       (order == -1 && cur_addr != old_addr - len)) {
    order = 0;
    state = 0;
   } else if ((cur_addr == old_addr + len * order) ||
       (cur_addr == old_addr + 2 * len) ||
       (cur_addr == old_addr + 3 * len)) {
    state = 2;
   } else {
    order = 0;
    state = 0;
   }
   break;
  case 2:
   order = 0;
   state = 0;
   break;
  }
 }

out:
 if (state == 1 || state == 2)
  return 1;

 return 0;
}

/**
 * ppc440spe_adma_device_estimate - estimate the efficiency of processing
 * the operation given on this channel. It's assumed that 'chan' is
 * capable to process 'cap' type of operation.
 * @chan: channel to use
 * @cap: type of transaction
 * @dst_lst: array of destination pointers
 * @dst_cnt: number of destination operands
 * @src_lst: array of source pointers
 * @src_cnt: number of source operands
 * @src_sz: size of each source operand
 */

static int ppc440spe_adma_estimate(struct dma_chan *chan,
 enum dma_transaction_type cap, struct page **dst_lst, int dst_cnt,
 struct page **src_lst, int src_cnt, size_t src_sz)
{
 int ef = 1;

 if (cap == DMA_PQ || cap == DMA_PQ_VAL) {
  /* If RAID-6 capabilities were not activated don't try
 * to use them
 */

  if (unlikely(!ppc440spe_r6_enabled))
   return -1;
 }
 /*  In the current implementation of ppc440spe ADMA driver it
 * makes sense to pick out only pq case, because it may be
 * processed:
 * (1) either using Biskup method on DMA2;
 * (2) or on DMA0/1.
 *  Thus we give a favour to (1) if the sources are suitable;
 * else let it be processed on one of the DMA0/1 engines.
 *  In the sum_product case where destination is also the
 * source process it on DMA0/1 only.
 */

 if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) {

  if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1])
   ef = 0; /* sum_product case, process on DMA0/1 */
  else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz))
   ef = 3; /* override (DMA0/1 + idle) */
  else
   ef = 0; /* can't process on DMA2 if !rxor */
 }

 /* channel idleness increases the priority */
 if (likely(ef) &&
     !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan)))
  ef++;

 return ef;
}

struct dma_chan *
ppc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,
 struct page **dst_lst, int dst_cnt, struct page **src_lst,
 int src_cnt, size_t src_sz)
{
 struct dma_chan *best_chan = NULL;
 struct ppc_dma_chan_ref *ref;
 int best_rank = -1;

 if (unlikely(!src_sz))
  return NULL;
 if (src_sz > PAGE_SIZE) {
  /*
 * should a user of the api ever pass > PAGE_SIZE requests
 * we sort out cases where temporary page-sized buffers
 * are used.
 */

  switch (cap) {
  case DMA_PQ:
   if (src_cnt == 1 && dst_lst[1] == src_lst[0])
    return NULL;
   if (src_cnt == 2 && dst_lst[1] == src_lst[1])
    return NULL;
   break;
  case DMA_PQ_VAL:
  case DMA_XOR_VAL:
   return NULL;
  default:
   break;
  }
 }

 list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) {
  if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
   int rank;

   rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst,
     dst_cnt, src_lst, src_cnt, src_sz);
   if (rank > best_rank) {
    best_rank = rank;
    best_chan = ref->chan;
   }
  }
 }

 return best_chan;
}
EXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel);

/**
 * ppc440spe_get_group_entry - get group entry with index idx
 * @tdesc: is the last allocated slot in the group.
 */

static struct ppc440spe_adma_desc_slot *
ppc440spe_get_group_entry(struct ppc440spe_adma_desc_slot *tdesc, u32 entry_idx)
{
 struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
 int i = 0;

 if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
  printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
   __func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
  BUG();
 }

 list_for_each_entry(iter, &tdesc->group_list, chain_node) {
  if (i++ == entry_idx)
   break;
 }
 return iter;
}

/**
 * ppc440spe_adma_free_slots - flags descriptor slots for reuse
 * @slot: Slot to free
 * Caller must hold &ppc440spe_chan->lock while calling this function
 */

static void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
          struct ppc440spe_adma_chan *chan)
{
 int stride = slot->slots_per_op;

 while (stride--) {
  slot->slots_per_op = 0;
  slot = list_entry(slot->slot_node.next,
    struct ppc440spe_adma_desc_slot,
    slot_node);
 }
}

/**
 * ppc440spe_adma_run_tx_complete_actions - call functions to be called
 * upon completion
 */

static dma_cookie_t ppc440spe_adma_run_tx_complete_actions(
  struct ppc440spe_adma_desc_slot *desc,
  struct ppc440spe_adma_chan *chan,
  dma_cookie_t cookie)
{
 BUG_ON(desc->async_tx.cookie < 0);
 if (desc->async_tx.cookie > 0) {
  cookie = desc->async_tx.cookie;
  desc->async_tx.cookie = 0;

  dma_descriptor_unmap(&desc->async_tx);
  /* call the callback (must not sleep or submit new
 * operations to this channel)
 */

  dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL);
 }

 /* run dependent operations */
 dma_run_dependencies(&desc->async_tx);

 return cookie;
}

/**
 * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set)
 */

static int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc,
  struct ppc440spe_adma_chan *chan)
{
 /* the client is allowed to attach dependent operations
 * until 'ack' is set
 */

 if (!async_tx_test_ack(&desc->async_tx))
  return 0;

 /* leave the last descriptor in the chain
 * so we can append to it
 */

 if (list_is_last(&desc->chain_node, &chan->chain) ||
     desc->phys == ppc440spe_chan_get_current_descriptor(chan))
  return 1;

 if (chan->device->id != PPC440SPE_XOR_ID) {
  /* our DMA interrupt handler clears opc field of
 * each processed descriptor. For all types of
 * operations except for ZeroSum we do not actually
 * need ack from the interrupt handler. ZeroSum is a
 * special case since the result of this operation
 * is available from the handler only, so if we see
 * such type of descriptor (which is unprocessed yet)
 * then leave it in chain.
 */

  struct dma_cdb *cdb = desc->hw_desc;
  if (cdb->opc == DMA_CDB_OPC_DCHECK128)
   return 1;
 }

 dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n",
  desc->phys, desc->idx, desc->slots_per_op);

 list_del(&desc->chain_node);
 ppc440spe_adma_free_slots(desc, chan);
 return 0;
}

/**
 * __ppc440spe_adma_slot_cleanup - this is the common clean-up routine
 * which runs through the channel CDBs list until reach the descriptor
 * currently processed. When routine determines that all CDBs of group
 * are completed then corresponding callbacks (if any) are called and slots
 * are freed.
 */

static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
{
 struct ppc440spe_adma_desc_slot *iter, *_iter, *group_start = NULL;
 dma_cookie_t cookie = 0;
 u32 current_desc = ppc440spe_chan_get_current_descriptor(chan);
 int busy = ppc440spe_chan_is_busy(chan);
 int seen_current = 0, slot_cnt = 0, slots_per_op = 0;

 dev_dbg(chan->device->common.dev, "ppc440spe adma%d: %s\n",
  chan->device->id, __func__);

 if (!current_desc) {
  /*  There were no transactions yet, so
 * nothing to clean
 */

  return;
 }

 /* free completed slots from the chain starting with
 * the oldest descriptor
 */

 list_for_each_entry_safe(iter, _iter, &chan->chain,
     chain_node) {
  dev_dbg(chan->device->common.dev, "\tcookie: %d slot: %d "
      "busy: %d this_desc: %#llx next_desc: %#x "
      "cur: %#x ack: %d\n",
      iter->async_tx.cookie, iter->idx, busy, iter->phys,
      ppc440spe_desc_get_link(iter, chan), current_desc,
      async_tx_test_ack(&iter->async_tx));
  prefetch(_iter);
  prefetch(&_iter->async_tx);

  /* do not advance past the current descriptor loaded into the
 * hardware channel,subsequent descriptors are either in process
 * or have not been submitted
 */

  if (seen_current)
   break;

  /* stop the search if we reach the current descriptor and the
 * channel is busy, or if it appears that the current descriptor
 * needs to be re-read (i.e. has been appended to)
 */

  if (iter->phys == current_desc) {
   BUG_ON(seen_current++);
   if (busy || ppc440spe_desc_get_link(iter, chan)) {
    /* not all descriptors of the group have
 * been completed; exit.
 */

    break;
   }
  }

  /* detect the start of a group transaction */
  if (!slot_cnt && !slots_per_op) {
   slot_cnt = iter->slot_cnt;
   slots_per_op = iter->slots_per_op;
   if (slot_cnt <= slots_per_op) {
    slot_cnt = 0;
    slots_per_op = 0;
   }
  }

  if (slot_cnt) {
   if (!group_start)
    group_start = iter;
   slot_cnt -= slots_per_op;
  }

  /* all the members of a group are complete */
  if (slots_per_op != 0 && slot_cnt == 0) {
   struct ppc440spe_adma_desc_slot *grp_iter, *_grp_iter;
   int end_of_chain = 0;

   /* clean up the group */
   slot_cnt = group_start->slot_cnt;
   grp_iter = group_start;
   list_for_each_entry_safe_from(grp_iter, _grp_iter,
    &chan->chain, chain_node) {

    cookie = ppc440spe_adma_run_tx_complete_actions(
     grp_iter, chan, cookie);

    slot_cnt -= slots_per_op;
    end_of_chain = ppc440spe_adma_clean_slot(
        grp_iter, chan);
    if (end_of_chain && slot_cnt) {
     /* Should wait for ZeroSum completion */
     if (cookie > 0)
      chan->common.completed_cookie = cookie;
     return;
    }

    if (slot_cnt == 0 || end_of_chain)
     break;
   }

   /* the group should be complete at this point */
   BUG_ON(slot_cnt);

   slots_per_op = 0;
   group_start = NULL;
   if (end_of_chain)
    break;
   else
    continue;
  } else if (slots_per_op) /* wait for group completion */
   continue;

  cookie = ppc440spe_adma_run_tx_complete_actions(iter, chan,
      cookie);

  if (ppc440spe_adma_clean_slot(iter, chan))
   break;
 }

 BUG_ON(!seen_current);

 if (cookie > 0) {
  chan->common.completed_cookie = cookie;
  pr_debug("\tcompleted cookie %d\n", cookie);
 }

}

/**
 * ppc440spe_adma_tasklet - clean up watch-dog initiator
 */

static void ppc440spe_adma_tasklet(struct tasklet_struct *t)
{
 struct ppc440spe_adma_chan *chan = from_tasklet(chan, t, irq_tasklet);

 spin_lock_nested(&chan->lock, SINGLE_DEPTH_NESTING);
 __ppc440spe_adma_slot_cleanup(chan);
 spin_unlock(&chan->lock);
}

/**
 * ppc440spe_adma_slot_cleanup - clean up scheduled initiator
 */

static void ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
{
 spin_lock_bh(&chan->lock);
 __ppc440spe_adma_slot_cleanup(chan);
 spin_unlock_bh(&chan->lock);
}

/**
 * ppc440spe_adma_alloc_slots - allocate free slots (if any)
 */

static struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
  struct ppc440spe_adma_chan *chan, int num_slots,
  int slots_per_op)
{
 struct ppc440spe_adma_desc_slot *iter = NULL, *_iter;
 struct ppc440spe_adma_desc_slot *alloc_start = NULL;
 int slots_found, retry = 0;
 LIST_HEAD(chain);


 BUG_ON(!num_slots || !slots_per_op);
 /* start search from the last allocated descrtiptor
 * if a contiguous allocation can not be found start searching
 * from the beginning of the list
 */

retry:
 slots_found = 0;
 if (retry == 0)
  iter = chan->last_used;
 else
  iter = list_entry(&chan->all_slots,
      struct ppc440spe_adma_desc_slot,
      slot_node);
 list_for_each_entry_safe_continue(iter, _iter, &chan->all_slots,
     slot_node) {
  prefetch(_iter);
  prefetch(&_iter->async_tx);
  if (iter->slots_per_op) {
   slots_found = 0;
   continue;
  }

  /* start the allocation if the slot is correctly aligned */
  if (!slots_found++)
   alloc_start = iter;

  if (slots_found == num_slots) {
   struct ppc440spe_adma_desc_slot *alloc_tail = NULL;
   struct ppc440spe_adma_desc_slot *last_used = NULL;

   iter = alloc_start;
   while (num_slots) {
    int i;
    /* pre-ack all but the last descriptor */
    if (num_slots != slots_per_op)
     async_tx_ack(&iter->async_tx);

    list_add_tail(&iter->chain_node, &chain);
    alloc_tail = iter;
    iter->async_tx.cookie = 0;
    iter->hw_next = NULL;
    iter->flags = 0;
    iter->slot_cnt = num_slots;
    iter->xor_check_result = NULL;
    for (i = 0; i < slots_per_op; i++) {
     iter->slots_per_op = slots_per_op - i;
     last_used = iter;
     iter = list_entry(iter->slot_node.next,
      struct ppc440spe_adma_desc_slot,
      slot_node);
    }
    num_slots -= slots_per_op;
   }
   alloc_tail->group_head = alloc_start;
   alloc_tail->async_tx.cookie = -EBUSY;
   list_splice(&chain, &alloc_tail->group_list);
   chan->last_used = last_used;
   return alloc_tail;
  }
 }
 if (!retry++)
  goto retry;

 /* try to free some slots if the allocation fails */
 tasklet_schedule(&chan->irq_tasklet);
 return NULL;
}

/**
 * ppc440spe_adma_alloc_chan_resources -  allocate pools for CDB slots
 */

static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan)
{
 struct ppc440spe_adma_chan *ppc440spe_chan;
 struct ppc440spe_adma_desc_slot *slot = NULL;
 char *hw_desc;
 int i, db_sz;
 int init;

 ppc440spe_chan = to_ppc440spe_adma_chan(chan);
 init = ppc440spe_chan->slots_allocated ? 0 : 1;
 chan->chan_id = ppc440spe_chan->device->id;

 /* Allocate descriptor slots */
 i = ppc440spe_chan->slots_allocated;
 if (ppc440spe_chan->device->id != PPC440SPE_XOR_ID)
  db_sz = sizeof(struct dma_cdb);
 else
  db_sz = sizeof(struct xor_cb);

 for (; i < (ppc440spe_chan->device->pool_size / db_sz); i++) {
  slot = kzalloc(sizeof(struct ppc440spe_adma_desc_slot),
          GFP_KERNEL);
  if (!slot) {
   printk(KERN_INFO "SPE ADMA Channel only initialized"
    " %d descriptor slots", i--);
   break;
  }

  hw_desc = (char *) ppc440spe_chan->device->dma_desc_pool_virt;
  slot->hw_desc = (void *) &hw_desc[i * db_sz];
  dma_async_tx_descriptor_init(&slot->async_tx, chan);
  slot->async_tx.tx_submit = ppc440spe_adma_tx_submit;
  INIT_LIST_HEAD(&slot->chain_node);
  INIT_LIST_HEAD(&slot->slot_node);
  INIT_LIST_HEAD(&slot->group_list);
  slot->phys = ppc440spe_chan->device->dma_desc_pool + i * db_sz;
  slot->idx = i;

  spin_lock_bh(&ppc440spe_chan->lock);
  ppc440spe_chan->slots_allocated++;
  list_add_tail(&slot->slot_node, &ppc440spe_chan->all_slots);
  spin_unlock_bh(&ppc440spe_chan->lock);
 }

 if (i && !ppc440spe_chan->last_used) {
  ppc440spe_chan->last_used =
   list_entry(ppc440spe_chan->all_slots.next,
    struct ppc440spe_adma_desc_slot,
    slot_node);
 }

 dev_dbg(ppc440spe_chan->device->common.dev,
  "ppc440spe adma%d: allocated %d descriptor slots\n",
  ppc440spe_chan->device->id, i);

 /* initialize the channel and the chain with a null operation */
 if (init) {
  switch (ppc440spe_chan->device->id) {
  case PPC440SPE_DMA0_ID:
  case PPC440SPE_DMA1_ID:
   ppc440spe_chan->hw_chain_inited = 0;
   /* Use WXOR for self-testing */
   if (!ppc440spe_r6_tchan)
    ppc440spe_r6_tchan = ppc440spe_chan;
   break;
  case PPC440SPE_XOR_ID:
   ppc440spe_chan_start_null_xor(ppc440spe_chan);
   break;
  default:
   BUG();
  }
  ppc440spe_chan->needs_unmap = 1;
 }

 return (i > 0) ? i : -ENOMEM;
}

/**
 * ppc440spe_rxor_set_region_data -
 */

static void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
 u8 xor_arg_no, u32 mask)
{
 struct xor_cb *xcb = desc->hw_desc;

 xcb->ops[xor_arg_no].h |= mask;
}

/**
 * ppc440spe_rxor_set_src -
 */

static void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc,
 u8 xor_arg_no, dma_addr_t addr)
{
 struct xor_cb *xcb = desc->hw_desc;

 xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE;
 xcb->ops[xor_arg_no].l = addr;
}

/**
 * ppc440spe_rxor_set_mult -
 */

static void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
 u8 xor_arg_no, u8 idx, u8 mult)
{
 struct xor_cb *xcb = desc->hw_desc;

 xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8);
}

/**
 * ppc440spe_adma_check_threshold - append CDBs to h/w chain if threshold
 * has been achieved
 */

static void ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan *chan)
{
 dev_dbg(chan->device->common.dev, "ppc440spe adma%d: pending: %d\n",
  chan->device->id, chan->pending);

 if (chan->pending >= PPC440SPE_ADMA_THRESHOLD) {
  chan->pending = 0;
  ppc440spe_chan_append(chan);
 }
}

/**
 * ppc440spe_adma_tx_submit - submit new descriptor group to the channel
 * (it's not necessary that descriptors will be submitted to the h/w
 * chains too right now)
 */

static dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
{
 struct ppc440spe_adma_desc_slot *sw_desc;
 struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(tx->chan);
 struct ppc440spe_adma_desc_slot *group_start, *old_chain_tail;
 int slot_cnt;
 int slots_per_op;
 dma_cookie_t cookie;

 sw_desc = tx_to_ppc440spe_adma_slot(tx);

 group_start = sw_desc->group_head;
 slot_cnt = group_start->slot_cnt;
 slots_per_op = group_start->slots_per_op;

 spin_lock_bh(&chan->lock);
 cookie = dma_cookie_assign(tx);

 if (unlikely(list_empty(&chan->chain))) {
  /* first peer */
  list_splice_init(&sw_desc->group_list, &chan->chain);
  chan_first_cdb[chan->device->id] = group_start;
 } else {
  /* isn't first peer, bind CDBs to chain */
  old_chain_tail = list_entry(chan->chain.prev,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  list_splice_init(&sw_desc->group_list,
      &old_chain_tail->chain_node);
  /* fix up the hardware chain */
  ppc440spe_desc_set_link(chan, old_chain_tail, group_start);
 }

 /* increment the pending count by the number of operations */
 chan->pending += slot_cnt / slots_per_op;
 ppc440spe_adma_check_threshold(chan);
 spin_unlock_bh(&chan->lock);

 dev_dbg(chan->device->common.dev,
  "ppc440spe adma%d: %s cookie: %d slot: %d tx %p\n",
  chan->device->id, __func__,
  sw_desc->async_tx.cookie, sw_desc->idx, sw_desc);

 return cookie;
}

/**
 * ppc440spe_adma_prep_dma_interrupt - prepare CDB for a pseudo DMA operation
 */

static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_interrupt(
  struct dma_chan *chan, unsigned long flags)
{
 struct ppc440spe_adma_chan *ppc440spe_chan;
 struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
 int slot_cnt, slots_per_op;

 ppc440spe_chan = to_ppc440spe_adma_chan(chan);

 dev_dbg(ppc440spe_chan->device->common.dev,
  "ppc440spe adma%d: %s\n", ppc440spe_chan->device->id,
  __func__);

 spin_lock_bh(&ppc440spe_chan->lock);
 slot_cnt = slots_per_op = 1;
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
   slots_per_op);
 if (sw_desc) {
  group_start = sw_desc->group_head;
  ppc440spe_desc_init_interrupt(group_start, ppc440spe_chan);
  group_start->unmap_len = 0;
  sw_desc->async_tx.flags = flags;
 }
 spin_unlock_bh(&ppc440spe_chan->lock);

 return sw_desc ? &sw_desc->async_tx : NULL;
}

/**
 * ppc440spe_adma_prep_dma_memcpy - prepare CDB for a MEMCPY operation
 */

static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy(
  struct dma_chan *chan, dma_addr_t dma_dest,
  dma_addr_t dma_src, size_t len, unsigned long flags)
{
 struct ppc440spe_adma_chan *ppc440spe_chan;
 struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
 int slot_cnt, slots_per_op;

 ppc440spe_chan = to_ppc440spe_adma_chan(chan);

 if (unlikely(!len))
  return NULL;

 BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT);

 spin_lock_bh(&ppc440spe_chan->lock);

 dev_dbg(ppc440spe_chan->device->common.dev,
  "ppc440spe adma%d: %s len: %u int_en %d\n",
  ppc440spe_chan->device->id, __func__, len,
  flags & DMA_PREP_INTERRUPT ? 1 : 0);
 slot_cnt = slots_per_op = 1;
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
  slots_per_op);
 if (sw_desc) {
  group_start = sw_desc->group_head;
  ppc440spe_desc_init_memcpy(group_start, flags);
  ppc440spe_adma_set_dest(group_start, dma_dest, 0);
  ppc440spe_adma_memcpy_xor_set_src(group_start, dma_src, 0);
  ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
  sw_desc->unmap_len = len;
  sw_desc->async_tx.flags = flags;
 }
 spin_unlock_bh(&ppc440spe_chan->lock);

 return sw_desc ? &sw_desc->async_tx : NULL;
}

/**
 * ppc440spe_adma_prep_dma_xor - prepare CDB for a XOR operation
 */

static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor(
  struct dma_chan *chan, dma_addr_t dma_dest,
  dma_addr_t *dma_src, u32 src_cnt, size_t len,
  unsigned long flags)
{
 struct ppc440spe_adma_chan *ppc440spe_chan;
 struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
 int slot_cnt, slots_per_op;

 ppc440spe_chan = to_ppc440spe_adma_chan(chan);

 ADMA_LL_DBG(prep_dma_xor_dbg(ppc440spe_chan->device->id,
         dma_dest, dma_src, src_cnt));
 if (unlikely(!len))
  return NULL;
 BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);

 dev_dbg(ppc440spe_chan->device->common.dev,
  "ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
  ppc440spe_chan->device->id, __func__, src_cnt, len,
  flags & DMA_PREP_INTERRUPT ? 1 : 0);

 spin_lock_bh(&ppc440spe_chan->lock);
 slot_cnt = ppc440spe_chan_xor_slot_count(len, src_cnt, &slots_per_op);
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
   slots_per_op);
 if (sw_desc) {
  group_start = sw_desc->group_head;
  ppc440spe_desc_init_xor(group_start, src_cnt, flags);
  ppc440spe_adma_set_dest(group_start, dma_dest, 0);
  while (src_cnt--)
   ppc440spe_adma_memcpy_xor_set_src(group_start,
    dma_src[src_cnt], src_cnt);
  ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len);
  sw_desc->unmap_len = len;
  sw_desc->async_tx.flags = flags;
 }
 spin_unlock_bh(&ppc440spe_chan->lock);

 return sw_desc ? &sw_desc->async_tx : NULL;
}

static inline void
ppc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot *desc,
    int src_cnt);
static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor);

/**
 * ppc440spe_adma_init_dma2rxor_slot -
 */

static void ppc440spe_adma_init_dma2rxor_slot(
  struct ppc440spe_adma_desc_slot *desc,
  dma_addr_t *src, int src_cnt)
{
 int i;

 /* initialize CDB */
 for (i = 0; i < src_cnt; i++) {
  ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i,
       desc->src_cnt, (u32)src[i]);
 }
}

/**
 * ppc440spe_dma01_prep_mult -
 * for Q operation where destination is also the source
 */

static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_mult(
  struct ppc440spe_adma_chan *ppc440spe_chan,
  dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
  const unsigned char *scf, size_t len, unsigned long flags)
{
 struct ppc440spe_adma_desc_slot *sw_desc = NULL;
 unsigned long op = 0;
 int slot_cnt;

 set_bit(PPC440SPE_DESC_WXOR, &op);
 slot_cnt = 2;

 spin_lock_bh(&ppc440spe_chan->lock);

 /* use WXOR, each descriptor occupies one slot */
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
 if (sw_desc) {
  struct ppc440spe_adma_chan *chan;
  struct ppc440spe_adma_desc_slot *iter;
  struct dma_cdb *hw_desc;

  chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
  set_bits(op, &sw_desc->flags);
  sw_desc->src_cnt = src_cnt;
  sw_desc->dst_cnt = dst_cnt;
  /* First descriptor, zero data in the destination and copy it
 * to q page using MULTICAST transfer.
 */

  iter = list_first_entry(&sw_desc->group_list,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  /* set 'next' pointer */
  iter->hw_next = list_entry(iter->chain_node.next,
        struct ppc440spe_adma_desc_slot,
        chain_node);
  clear_bit(PPC440SPE_DESC_INT, &iter->flags);
  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MULTICAST;

  ppc440spe_desc_set_dest_addr(iter, chan,
          DMA_CUED_XOR_BASE, dst[0], 0);
  ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1);
  ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
         src[0]);
  ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
  iter->unmap_len = len;

  /*
 * Second descriptor, multiply data from the q page
 * and store the result in real destination.
 */

  iter = list_first_entry(&iter->chain_node,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  iter->hw_next = NULL;
  if (flags & DMA_PREP_INTERRUPT)
   set_bit(PPC440SPE_DESC_INT, &iter->flags);
  else
   clear_bit(PPC440SPE_DESC_INT, &iter->flags);

  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
  ppc440spe_desc_set_src_addr(iter, chan, 0,
         DMA_CUED_XOR_HB, dst[1]);
  ppc440spe_desc_set_dest_addr(iter, chan,
          DMA_CUED_XOR_BASE, dst[0], 0);

  ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
         DMA_CDB_SG_DST1, scf[0]);
  ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
  iter->unmap_len = len;
  sw_desc->async_tx.flags = flags;
 }

 spin_unlock_bh(&ppc440spe_chan->lock);

 return sw_desc;
}

/**
 * ppc440spe_dma01_prep_sum_product -
 * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also
 * the source.
 */

static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_sum_product(
  struct ppc440spe_adma_chan *ppc440spe_chan,
  dma_addr_t *dst, dma_addr_t *src, int src_cnt,
  const unsigned char *scf, size_t len, unsigned long flags)
{
 struct ppc440spe_adma_desc_slot *sw_desc = NULL;
 unsigned long op = 0;
 int slot_cnt;

 set_bit(PPC440SPE_DESC_WXOR, &op);
 slot_cnt = 3;

 spin_lock_bh(&ppc440spe_chan->lock);

 /* WXOR, each descriptor occupies one slot */
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
 if (sw_desc) {
  struct ppc440spe_adma_chan *chan;
  struct ppc440spe_adma_desc_slot *iter;
  struct dma_cdb *hw_desc;

  chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
  set_bits(op, &sw_desc->flags);
  sw_desc->src_cnt = src_cnt;
  sw_desc->dst_cnt = 1;
  /* 1st descriptor, src[1] data to q page and zero destination */
  iter = list_first_entry(&sw_desc->group_list,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  iter->hw_next = list_entry(iter->chain_node.next,
        struct ppc440spe_adma_desc_slot,
        chain_node);
  clear_bit(PPC440SPE_DESC_INT, &iter->flags);
  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MULTICAST;

  ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
          *dst, 0);
  ppc440spe_desc_set_dest_addr(iter, chan, 0,
          ppc440spe_chan->qdest, 1);
  ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
         src[1]);
  ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
  iter->unmap_len = len;

  /* 2nd descriptor, multiply src[1] data and store the
 * result in destination */

  iter = list_first_entry(&iter->chain_node,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  /* set 'next' pointer */
  iter->hw_next = list_entry(iter->chain_node.next,
        struct ppc440spe_adma_desc_slot,
        chain_node);
  if (flags & DMA_PREP_INTERRUPT)
   set_bit(PPC440SPE_DESC_INT, &iter->flags);
  else
   clear_bit(PPC440SPE_DESC_INT, &iter->flags);

  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
  ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
         ppc440spe_chan->qdest);
  ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
          *dst, 0);
  ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
         DMA_CDB_SG_DST1, scf[1]);
  ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
  iter->unmap_len = len;

  /*
 * 3rd descriptor, multiply src[0] data and xor it
 * with destination
 */

  iter = list_first_entry(&iter->chain_node,
     struct ppc440spe_adma_desc_slot,
     chain_node);
  memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
  iter->hw_next = NULL;
  if (flags & DMA_PREP_INTERRUPT)
   set_bit(PPC440SPE_DESC_INT, &iter->flags);
  else
   clear_bit(PPC440SPE_DESC_INT, &iter->flags);

  hw_desc = iter->hw_desc;
  hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
  ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
         src[0]);
  ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
          *dst, 0);
  ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
         DMA_CDB_SG_DST1, scf[0]);
  ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
  iter->unmap_len = len;
  sw_desc->async_tx.flags = flags;
 }

 spin_unlock_bh(&ppc440spe_chan->lock);

 return sw_desc;
}

static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq(
  struct ppc440spe_adma_chan *ppc440spe_chan,
  dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
  const unsigned char *scf, size_t len, unsigned long flags)
{
 int slot_cnt;
 struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
 unsigned long op = 0;
 unsigned char mult = 1;

 pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
   __func__, dst_cnt, src_cnt, len);
 /*  select operations WXOR/RXOR depending on the
 * source addresses of operators and the number
 * of destinations (RXOR support only Q-parity calculations)
 */

 set_bit(PPC440SPE_DESC_WXOR, &op);
 if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
  /* no active RXOR;
 * do RXOR if:
 * - there are more than 1 source,
 * - len is aligned on 512-byte boundary,
 * - source addresses fit to one of 4 possible regions.
 */

  if (src_cnt > 1 &&
      !(len & MQ0_CF2H_RXOR_BS_MASK) &&
      (src[0] + len) == src[1]) {
   /* may do RXOR R1 R2 */
   set_bit(PPC440SPE_DESC_RXOR, &op);
   if (src_cnt != 2) {
    /* may try to enhance region of RXOR */
    if ((src[1] + len) == src[2]) {
     /* do RXOR R1 R2 R3 */
     set_bit(PPC440SPE_DESC_RXOR123,
      &op);
    } else if ((src[1] + len * 2) == src[2]) {
     /* do RXOR R1 R2 R4 */
     set_bit(PPC440SPE_DESC_RXOR124, &op);
    } else if ((src[1] + len * 3) == src[2]) {
     /* do RXOR R1 R2 R5 */
     set_bit(PPC440SPE_DESC_RXOR125,
      &op);
    } else {
     /* do RXOR R1 R2 */
     set_bit(PPC440SPE_DESC_RXOR12,
      &op);
    }
   } else {
    /* do RXOR R1 R2 */
    set_bit(PPC440SPE_DESC_RXOR12, &op);
   }
  }

  if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
   /* can not do this operation with RXOR */
   clear_bit(PPC440SPE_RXOR_RUN,
    &ppc440spe_rxor_state);
  } else {
   /* can do; set block size right now */
   ppc440spe_desc_set_rxor_block_size(len);
  }
 }

 /* Number of necessary slots depends on operation type selected */
 if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
  /*  This is a WXOR only chain. Need descriptors for each
 * source to GF-XOR them with WXOR, and need descriptors
 * for each destination to zero them with WXOR
 */

  slot_cnt = src_cnt;

  if (flags & DMA_PREP_ZERO_P) {
   slot_cnt++;
   set_bit(PPC440SPE_ZERO_P, &op);
  }
  if (flags & DMA_PREP_ZERO_Q) {
   slot_cnt++;
   set_bit(PPC440SPE_ZERO_Q, &op);
  }
 } else {
  /*  Need 1/2 descriptor for RXOR operation, and
 * need (src_cnt - (2 or 3)) for WXOR of sources
 * remained (if any)
 */

  slot_cnt = dst_cnt;

  if (flags & DMA_PREP_ZERO_P)
   set_bit(PPC440SPE_ZERO_P, &op);
  if (flags & DMA_PREP_ZERO_Q)
   set_bit(PPC440SPE_ZERO_Q, &op);

  if (test_bit(PPC440SPE_DESC_RXOR12, &op))
   slot_cnt += src_cnt - 2;
  else
   slot_cnt += src_cnt - 3;

  /*  Thus we have either RXOR only chain or
 * mixed RXOR/WXOR
 */

  if (slot_cnt == dst_cnt)
   /* RXOR only chain */
   clear_bit(PPC440SPE_DESC_WXOR, &op);
 }

 spin_lock_bh(&ppc440spe_chan->lock);
 /* for both RXOR/WXOR each descriptor occupies one slot */
 sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
 if (sw_desc) {
  ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt,
    flags, op);

  /* setup dst/src/mult */
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=89 G=91

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.