1
0

Merge branch 'ioat-md-accel-for-linus' of git://lost.foo-projects.org/~dwillia2/git/iop

* 'ioat-md-accel-for-linus' of git://lost.foo-projects.org/~dwillia2/git/iop: (28 commits)
  ioatdma: add the unisys "i/oat" pci vendor/device id
  ARM: Add drivers/dma to arch/arm/Kconfig
  iop3xx: surface the iop3xx DMA and AAU units to the iop-adma driver
  iop13xx: surface the iop13xx adma units to the iop-adma driver
  dmaengine: driver for the iop32x, iop33x, and iop13xx raid engines
  md: remove raid5 compute_block and compute_parity5
  md: handle_stripe5 - request io processing in raid5_run_ops
  md: handle_stripe5 - add request/completion logic for async expand ops
  md: handle_stripe5 - add request/completion logic for async read ops
  md: handle_stripe5 - add request/completion logic for async check ops
  md: handle_stripe5 - add request/completion logic for async compute ops
  md: handle_stripe5 - add request/completion logic for async write ops
  md: common infrastructure for running operations with raid5_run_ops
  md: raid5_run_ops - run stripe operations outside sh->lock
  raid5: replace custom debug PRINTKs with standard pr_debug
  raid5: refactor handle_stripe5 and handle_stripe6 (v3)
  async_tx: add the async_tx api
  xor: make 'xor_blocks' a library routine for use with async_tx
  dmaengine: make clients responsible for managing channels
  dmaengine: refactor dmaengine around dma_async_tx_descriptor
  ...
This commit is contained in:
Linus Torvalds
2007-07-13 10:52:27 -07:00
45 changed files with 7367 additions and 1753 deletions

View File

@@ -0,0 +1,544 @@
/*
* Copyright(c) 2006, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef _ADMA_H
#define _ADMA_H
#include <linux/types.h>
#include <linux/io.h>
#include <asm/hardware.h>
#include <asm/hardware/iop_adma.h>
#define ADMA_ACCR(chan) (chan->mmr_base + 0x0)
#define ADMA_ACSR(chan) (chan->mmr_base + 0x4)
#define ADMA_ADAR(chan) (chan->mmr_base + 0x8)
#define ADMA_IIPCR(chan) (chan->mmr_base + 0x18)
#define ADMA_IIPAR(chan) (chan->mmr_base + 0x1c)
#define ADMA_IIPUAR(chan) (chan->mmr_base + 0x20)
#define ADMA_ANDAR(chan) (chan->mmr_base + 0x24)
#define ADMA_ADCR(chan) (chan->mmr_base + 0x28)
#define ADMA_CARMD(chan) (chan->mmr_base + 0x2c)
#define ADMA_ABCR(chan) (chan->mmr_base + 0x30)
#define ADMA_DLADR(chan) (chan->mmr_base + 0x34)
#define ADMA_DUADR(chan) (chan->mmr_base + 0x38)
#define ADMA_SLAR(src, chan) (chan->mmr_base + (0x3c + (src << 3)))
#define ADMA_SUAR(src, chan) (chan->mmr_base + (0x40 + (src << 3)))
struct iop13xx_adma_src {
u32 src_addr;
union {
u32 upper_src_addr;
struct {
unsigned int pq_upper_src_addr:24;
unsigned int pq_dmlt:8;
};
};
};
struct iop13xx_adma_desc_ctrl {
unsigned int int_en:1;
unsigned int xfer_dir:2;
unsigned int src_select:4;
unsigned int zero_result:1;
unsigned int block_fill_en:1;
unsigned int crc_gen_en:1;
unsigned int crc_xfer_dis:1;
unsigned int crc_seed_fetch_dis:1;
unsigned int status_write_back_en:1;
unsigned int endian_swap_en:1;
unsigned int reserved0:2;
unsigned int pq_update_xfer_en:1;
unsigned int dual_xor_en:1;
unsigned int pq_xfer_en:1;
unsigned int p_xfer_dis:1;
unsigned int reserved1:10;
unsigned int relax_order_en:1;
unsigned int no_snoop_en:1;
};
struct iop13xx_adma_byte_count {
unsigned int byte_count:24;
unsigned int host_if:3;
unsigned int reserved:2;
unsigned int zero_result_err_q:1;
unsigned int zero_result_err:1;
unsigned int tx_complete:1;
};
struct iop13xx_adma_desc_hw {
u32 next_desc;
union {
u32 desc_ctrl;
struct iop13xx_adma_desc_ctrl desc_ctrl_field;
};
union {
u32 crc_addr;
u32 block_fill_data;
u32 q_dest_addr;
};
union {
u32 byte_count;
struct iop13xx_adma_byte_count byte_count_field;
};
union {
u32 dest_addr;
u32 p_dest_addr;
};
union {
u32 upper_dest_addr;
u32 pq_upper_dest_addr;
};
struct iop13xx_adma_src src[1];
};
struct iop13xx_adma_desc_dual_xor {
u32 next_desc;
u32 desc_ctrl;
u32 reserved;
u32 byte_count;
u32 h_dest_addr;
u32 h_upper_dest_addr;
u32 src0_addr;
u32 upper_src0_addr;
u32 src1_addr;
u32 upper_src1_addr;
u32 h_src_addr;
u32 h_upper_src_addr;
u32 d_src_addr;
u32 d_upper_src_addr;
u32 d_dest_addr;
u32 d_upper_dest_addr;
};
struct iop13xx_adma_desc_pq_update {
u32 next_desc;
u32 desc_ctrl;
u32 reserved;
u32 byte_count;
u32 p_dest_addr;
u32 p_upper_dest_addr;
u32 src0_addr;
u32 upper_src0_addr;
u32 src1_addr;
u32 upper_src1_addr;
u32 p_src_addr;
u32 p_upper_src_addr;
u32 q_src_addr;
struct {
unsigned int q_upper_src_addr:24;
unsigned int q_dmlt:8;
};
u32 q_dest_addr;
u32 q_upper_dest_addr;
};
static inline int iop_adma_get_max_xor(void)
{
return 16;
}
static inline u32 iop_chan_get_current_descriptor(struct iop_adma_chan *chan)
{
return __raw_readl(ADMA_ADAR(chan));
}
static inline void iop_chan_set_next_descriptor(struct iop_adma_chan *chan,
u32 next_desc_addr)
{
__raw_writel(next_desc_addr, ADMA_ANDAR(chan));
}
#define ADMA_STATUS_BUSY (1 << 13)
static inline char iop_chan_is_busy(struct iop_adma_chan *chan)
{
if (__raw_readl(ADMA_ACSR(chan)) &
ADMA_STATUS_BUSY)
return 1;
else
return 0;
}
static inline int
iop_chan_get_desc_align(struct iop_adma_chan *chan, int num_slots)
{
return 1;
}
#define iop_desc_is_aligned(x, y) 1
static inline int
iop_chan_memcpy_slot_count(size_t len, int *slots_per_op)
{
*slots_per_op = 1;
return 1;
}
#define iop_chan_interrupt_slot_count(s, c) iop_chan_memcpy_slot_count(0, s)
static inline int
iop_chan_memset_slot_count(size_t len, int *slots_per_op)
{
*slots_per_op = 1;
return 1;
}
static inline int
iop_chan_xor_slot_count(size_t len, int src_cnt, int *slots_per_op)
{
int num_slots;
/* slots_to_find = 1 for basic descriptor + 1 per 4 sources above 1
* (1 source => 8 bytes) (1 slot => 32 bytes)
*/
num_slots = 1 + (((src_cnt - 1) << 3) >> 5);
if (((src_cnt - 1) << 3) & 0x1f)
num_slots++;
*slots_per_op = num_slots;
return num_slots;
}
#define ADMA_MAX_BYTE_COUNT (16 * 1024 * 1024)
#define IOP_ADMA_MAX_BYTE_COUNT ADMA_MAX_BYTE_COUNT
#define IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT ADMA_MAX_BYTE_COUNT
#define IOP_ADMA_XOR_MAX_BYTE_COUNT ADMA_MAX_BYTE_COUNT
#define iop_chan_zero_sum_slot_count(l, s, o) iop_chan_xor_slot_count(l, s, o)
static inline u32 iop_desc_get_dest_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
return hw_desc->dest_addr;
}
static inline u32 iop_desc_get_byte_count(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
return hw_desc->byte_count_field.byte_count;
}
static inline u32 iop_desc_get_src_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
int src_idx)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
return hw_desc->src[src_idx].src_addr;
}
static inline u32 iop_desc_get_src_count(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
return hw_desc->desc_ctrl_field.src_select + 1;
}
static inline void
iop_desc_init_memcpy(struct iop_adma_desc_slot *desc, int int_en)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop13xx_adma_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.xfer_dir = 3; /* local to internal bus */
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
hw_desc->crc_addr = 0;
}
static inline void
iop_desc_init_memset(struct iop_adma_desc_slot *desc, int int_en)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop13xx_adma_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.xfer_dir = 3; /* local to internal bus */
u_desc_ctrl.field.block_fill_en = 1;
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
hw_desc->crc_addr = 0;
}
/* to do: support buffers larger than ADMA_MAX_BYTE_COUNT */
static inline void
iop_desc_init_xor(struct iop_adma_desc_slot *desc, int src_cnt, int int_en)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop13xx_adma_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.src_select = src_cnt - 1;
u_desc_ctrl.field.xfer_dir = 3; /* local to internal bus */
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
hw_desc->crc_addr = 0;
}
#define iop_desc_init_null_xor(d, s, i) iop_desc_init_xor(d, s, i)
/* to do: support buffers larger than ADMA_MAX_BYTE_COUNT */
static inline int
iop_desc_init_zero_sum(struct iop_adma_desc_slot *desc, int src_cnt, int int_en)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop13xx_adma_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.src_select = src_cnt - 1;
u_desc_ctrl.field.xfer_dir = 3; /* local to internal bus */
u_desc_ctrl.field.zero_result = 1;
u_desc_ctrl.field.status_write_back_en = 1;
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
hw_desc->crc_addr = 0;
return 1;
}
static inline void iop_desc_set_byte_count(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
u32 byte_count)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
hw_desc->byte_count = byte_count;
}
static inline void
iop_desc_set_zero_sum_byte_count(struct iop_adma_desc_slot *desc, u32 len)
{
int slots_per_op = desc->slots_per_op;
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc, *iter;
int i = 0;
if (len <= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT) {
hw_desc->byte_count = len;
} else {
do {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iter->byte_count = IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
len -= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
i += slots_per_op;
} while (len > IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT);
if (len) {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iter->byte_count = len;
}
}
}
static inline void iop_desc_set_dest_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
dma_addr_t addr)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
hw_desc->dest_addr = addr;
hw_desc->upper_dest_addr = 0;
}
static inline void iop_desc_set_memcpy_src_addr(struct iop_adma_desc_slot *desc,
dma_addr_t addr)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
hw_desc->src[0].src_addr = addr;
hw_desc->src[0].upper_src_addr = 0;
}
static inline void iop_desc_set_xor_src_addr(struct iop_adma_desc_slot *desc,
int src_idx, dma_addr_t addr)
{
int slot_cnt = desc->slot_cnt, slots_per_op = desc->slots_per_op;
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc, *iter;
int i = 0;
do {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iter->src[src_idx].src_addr = addr;
iter->src[src_idx].upper_src_addr = 0;
slot_cnt -= slots_per_op;
if (slot_cnt) {
i += slots_per_op;
addr += IOP_ADMA_XOR_MAX_BYTE_COUNT;
}
} while (slot_cnt);
}
static inline void
iop_desc_init_interrupt(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
iop_desc_init_memcpy(desc, 1);
iop_desc_set_byte_count(desc, chan, 0);
iop_desc_set_dest_addr(desc, chan, 0);
iop_desc_set_memcpy_src_addr(desc, 0);
}
#define iop_desc_set_zero_sum_src_addr iop_desc_set_xor_src_addr
static inline void iop_desc_set_next_desc(struct iop_adma_desc_slot *desc,
u32 next_desc_addr)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
BUG_ON(hw_desc->next_desc);
hw_desc->next_desc = next_desc_addr;
}
static inline u32 iop_desc_get_next_desc(struct iop_adma_desc_slot *desc)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
return hw_desc->next_desc;
}
static inline void iop_desc_clear_next_desc(struct iop_adma_desc_slot *desc)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
hw_desc->next_desc = 0;
}
static inline void iop_desc_set_block_fill_val(struct iop_adma_desc_slot *desc,
u32 val)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
hw_desc->block_fill_data = val;
}
static inline int iop_desc_get_zero_result(struct iop_adma_desc_slot *desc)
{
struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;
struct iop13xx_adma_desc_ctrl desc_ctrl = hw_desc->desc_ctrl_field;
struct iop13xx_adma_byte_count byte_count = hw_desc->byte_count_field;
BUG_ON(!(byte_count.tx_complete && desc_ctrl.zero_result));
if (desc_ctrl.pq_xfer_en)
return byte_count.zero_result_err_q;
else
return byte_count.zero_result_err;
}
static inline void iop_chan_append(struct iop_adma_chan *chan)
{
u32 adma_accr;
adma_accr = __raw_readl(ADMA_ACCR(chan));
adma_accr |= 0x2;
__raw_writel(adma_accr, ADMA_ACCR(chan));
}
static inline void iop_chan_idle(int busy, struct iop_adma_chan *chan)
{
do { } while (0);
}
static inline u32 iop_chan_get_status(struct iop_adma_chan *chan)
{
return __raw_readl(ADMA_ACSR(chan));
}
static inline void iop_chan_disable(struct iop_adma_chan *chan)
{
u32 adma_chan_ctrl = __raw_readl(ADMA_ACCR(chan));
adma_chan_ctrl &= ~0x1;
__raw_writel(adma_chan_ctrl, ADMA_ACCR(chan));
}
static inline void iop_chan_enable(struct iop_adma_chan *chan)
{
u32 adma_chan_ctrl;
adma_chan_ctrl = __raw_readl(ADMA_ACCR(chan));
adma_chan_ctrl |= 0x1;
__raw_writel(adma_chan_ctrl, ADMA_ACCR(chan));
}
static inline void iop_adma_device_clear_eot_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(ADMA_ACSR(chan));
status &= (1 << 12);
__raw_writel(status, ADMA_ACSR(chan));
}
static inline void iop_adma_device_clear_eoc_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(ADMA_ACSR(chan));
status &= (1 << 11);
__raw_writel(status, ADMA_ACSR(chan));
}
static inline void iop_adma_device_clear_err_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(ADMA_ACSR(chan));
status &= (1 << 9) | (1 << 5) | (1 << 4) | (1 << 3);
__raw_writel(status, ADMA_ACSR(chan));
}
static inline int
iop_is_err_int_parity(unsigned long status, struct iop_adma_chan *chan)
{
return test_bit(9, &status);
}
static inline int
iop_is_err_mcu_abort(unsigned long status, struct iop_adma_chan *chan)
{
return test_bit(5, &status);
}
static inline int
iop_is_err_int_tabort(unsigned long status, struct iop_adma_chan *chan)
{
return test_bit(4, &status);
}
static inline int
iop_is_err_int_mabort(unsigned long status, struct iop_adma_chan *chan)
{
return test_bit(3, &status);
}
static inline int
iop_is_err_pci_tabort(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
static inline int
iop_is_err_pci_mabort(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
static inline int
iop_is_err_split_tx(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
#endif /* _ADMA_H */

View File

@@ -166,12 +166,22 @@ static inline int iop13xx_cpu_id(void)
#define IOP13XX_INIT_I2C_1 (1 << 1)
#define IOP13XX_INIT_I2C_2 (1 << 2)
#define IQ81340_NUM_UART 2
#define IQ81340_NUM_I2C 3
#define IQ81340_NUM_PHYS_MAP_FLASH 1
#define IQ81340_MAX_PLAT_DEVICES (IQ81340_NUM_UART +\
IQ81340_NUM_I2C +\
IQ81340_NUM_PHYS_MAP_FLASH)
/* ADMA selection flags */
/* INIT_ADMA_DEFAULT = Rely on CONFIG_IOP13XX_ADMA* */
#define IOP13XX_INIT_ADMA_DEFAULT (0)
#define IOP13XX_INIT_ADMA_0 (1 << 0)
#define IOP13XX_INIT_ADMA_1 (1 << 1)
#define IOP13XX_INIT_ADMA_2 (1 << 2)
/* Platform devices */
#define IQ81340_NUM_UART 2
#define IQ81340_NUM_I2C 3
#define IQ81340_NUM_PHYS_MAP_FLASH 1
#define IQ81340_NUM_ADMA 3
#define IQ81340_MAX_PLAT_DEVICES (IQ81340_NUM_UART + \
IQ81340_NUM_I2C + \
IQ81340_NUM_PHYS_MAP_FLASH + \
IQ81340_NUM_ADMA)
/*========================== PMMR offsets for key registers ============*/
#define IOP13XX_ATU0_PMMR_OFFSET 0x00048000
@@ -444,22 +454,6 @@ static inline int iop13xx_cpu_id(void)
/*==============================ADMA UNITS===============================*/
#define IOP13XX_ADMA_PHYS_BASE(chan) IOP13XX_REG_ADDR32_PHYS((chan << 9))
#define IOP13XX_ADMA_UPPER_PA(chan) (IOP13XX_ADMA_PHYS_BASE(chan) + 0xc0)
#define IOP13XX_ADMA_OFFSET(chan, ofs) IOP13XX_REG_ADDR32((chan << 9) + (ofs))
#define IOP13XX_ADMA_ACCR(chan) IOP13XX_ADMA_OFFSET(chan, 0x0)
#define IOP13XX_ADMA_ACSR(chan) IOP13XX_ADMA_OFFSET(chan, 0x4)
#define IOP13XX_ADMA_ADAR(chan) IOP13XX_ADMA_OFFSET(chan, 0x8)
#define IOP13XX_ADMA_IIPCR(chan) IOP13XX_ADMA_OFFSET(chan, 0x18)
#define IOP13XX_ADMA_IIPAR(chan) IOP13XX_ADMA_OFFSET(chan, 0x1c)
#define IOP13XX_ADMA_IIPUAR(chan) IOP13XX_ADMA_OFFSET(chan, 0x20)
#define IOP13XX_ADMA_ANDAR(chan) IOP13XX_ADMA_OFFSET(chan, 0x24)
#define IOP13XX_ADMA_ADCR(chan) IOP13XX_ADMA_OFFSET(chan, 0x28)
#define IOP13XX_ADMA_CARMD(chan) IOP13XX_ADMA_OFFSET(chan, 0x2c)
#define IOP13XX_ADMA_ABCR(chan) IOP13XX_ADMA_OFFSET(chan, 0x30)
#define IOP13XX_ADMA_DLADR(chan) IOP13XX_ADMA_OFFSET(chan, 0x34)
#define IOP13XX_ADMA_DUADR(chan) IOP13XX_ADMA_OFFSET(chan, 0x38)
#define IOP13XX_ADMA_SLAR(src, chan) IOP13XX_ADMA_OFFSET(chan, 0x3c + (src <<3))
#define IOP13XX_ADMA_SUAR(src, chan) IOP13XX_ADMA_OFFSET(chan, 0x40 + (src <<3))
/*==============================XSI BRIDGE===============================*/
#define IOP13XX_XBG_BECSR IOP13XX_REG_ADDR32(0x178c)

View File

@@ -0,0 +1,5 @@
#ifndef IOP32X_ADMA_H
#define IOP32X_ADMA_H
#include <asm/hardware/iop3xx-adma.h>
#endif

View File

@@ -0,0 +1,5 @@
#ifndef IOP33X_ADMA_H
#define IOP33X_ADMA_H
#include <asm/hardware/iop3xx-adma.h>
#endif

View File

@@ -0,0 +1,892 @@
/*
* Copyright © 2006, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef _ADMA_H
#define _ADMA_H
#include <linux/types.h>
#include <linux/io.h>
#include <asm/hardware.h>
#include <asm/hardware/iop_adma.h>
/* Memory copy units */
#define DMA_CCR(chan) (chan->mmr_base + 0x0)
#define DMA_CSR(chan) (chan->mmr_base + 0x4)
#define DMA_DAR(chan) (chan->mmr_base + 0xc)
#define DMA_NDAR(chan) (chan->mmr_base + 0x10)
#define DMA_PADR(chan) (chan->mmr_base + 0x14)
#define DMA_PUADR(chan) (chan->mmr_base + 0x18)
#define DMA_LADR(chan) (chan->mmr_base + 0x1c)
#define DMA_BCR(chan) (chan->mmr_base + 0x20)
#define DMA_DCR(chan) (chan->mmr_base + 0x24)
/* Application accelerator unit */
#define AAU_ACR(chan) (chan->mmr_base + 0x0)
#define AAU_ASR(chan) (chan->mmr_base + 0x4)
#define AAU_ADAR(chan) (chan->mmr_base + 0x8)
#define AAU_ANDAR(chan) (chan->mmr_base + 0xc)
#define AAU_SAR(src, chan) (chan->mmr_base + (0x10 + ((src) << 2)))
#define AAU_DAR(chan) (chan->mmr_base + 0x20)
#define AAU_ABCR(chan) (chan->mmr_base + 0x24)
#define AAU_ADCR(chan) (chan->mmr_base + 0x28)
#define AAU_SAR_EDCR(src_edc) (chan->mmr_base + (0x02c + ((src_edc-4) << 2)))
#define AAU_EDCR0_IDX 8
#define AAU_EDCR1_IDX 17
#define AAU_EDCR2_IDX 26
#define DMA0_ID 0
#define DMA1_ID 1
#define AAU_ID 2
struct iop3xx_aau_desc_ctrl {
unsigned int int_en:1;
unsigned int blk1_cmd_ctrl:3;
unsigned int blk2_cmd_ctrl:3;
unsigned int blk3_cmd_ctrl:3;
unsigned int blk4_cmd_ctrl:3;
unsigned int blk5_cmd_ctrl:3;
unsigned int blk6_cmd_ctrl:3;
unsigned int blk7_cmd_ctrl:3;
unsigned int blk8_cmd_ctrl:3;
unsigned int blk_ctrl:2;
unsigned int dual_xor_en:1;
unsigned int tx_complete:1;
unsigned int zero_result_err:1;
unsigned int zero_result_en:1;
unsigned int dest_write_en:1;
};
struct iop3xx_aau_e_desc_ctrl {
unsigned int reserved:1;
unsigned int blk1_cmd_ctrl:3;
unsigned int blk2_cmd_ctrl:3;
unsigned int blk3_cmd_ctrl:3;
unsigned int blk4_cmd_ctrl:3;
unsigned int blk5_cmd_ctrl:3;
unsigned int blk6_cmd_ctrl:3;
unsigned int blk7_cmd_ctrl:3;
unsigned int blk8_cmd_ctrl:3;
unsigned int reserved2:7;
};
struct iop3xx_dma_desc_ctrl {
unsigned int pci_transaction:4;
unsigned int int_en:1;
unsigned int dac_cycle_en:1;
unsigned int mem_to_mem_en:1;
unsigned int crc_data_tx_en:1;
unsigned int crc_gen_en:1;
unsigned int crc_seed_dis:1;
unsigned int reserved:21;
unsigned int crc_tx_complete:1;
};
struct iop3xx_desc_dma {
u32 next_desc;
union {
u32 pci_src_addr;
u32 pci_dest_addr;
u32 src_addr;
};
union {
u32 upper_pci_src_addr;
u32 upper_pci_dest_addr;
};
union {
u32 local_pci_src_addr;
u32 local_pci_dest_addr;
u32 dest_addr;
};
u32 byte_count;
union {
u32 desc_ctrl;
struct iop3xx_dma_desc_ctrl desc_ctrl_field;
};
u32 crc_addr;
};
struct iop3xx_desc_aau {
u32 next_desc;
u32 src[4];
u32 dest_addr;
u32 byte_count;
union {
u32 desc_ctrl;
struct iop3xx_aau_desc_ctrl desc_ctrl_field;
};
union {
u32 src_addr;
u32 e_desc_ctrl;
struct iop3xx_aau_e_desc_ctrl e_desc_ctrl_field;
} src_edc[31];
};
struct iop3xx_aau_gfmr {
unsigned int gfmr1:8;
unsigned int gfmr2:8;
unsigned int gfmr3:8;
unsigned int gfmr4:8;
};
struct iop3xx_desc_pq_xor {
u32 next_desc;
u32 src[3];
union {
u32 data_mult1;
struct iop3xx_aau_gfmr data_mult1_field;
};
u32 dest_addr;
u32 byte_count;
union {
u32 desc_ctrl;
struct iop3xx_aau_desc_ctrl desc_ctrl_field;
};
union {
u32 src_addr;
u32 e_desc_ctrl;
struct iop3xx_aau_e_desc_ctrl e_desc_ctrl_field;
u32 data_multiplier;
struct iop3xx_aau_gfmr data_mult_field;
u32 reserved;
} src_edc_gfmr[19];
};
struct iop3xx_desc_dual_xor {
u32 next_desc;
u32 src0_addr;
u32 src1_addr;
u32 h_src_addr;
u32 d_src_addr;
u32 h_dest_addr;
u32 byte_count;
union {
u32 desc_ctrl;
struct iop3xx_aau_desc_ctrl desc_ctrl_field;
};
u32 d_dest_addr;
};
union iop3xx_desc {
struct iop3xx_desc_aau *aau;
struct iop3xx_desc_dma *dma;
struct iop3xx_desc_pq_xor *pq_xor;
struct iop3xx_desc_dual_xor *dual_xor;
void *ptr;
};
static inline int iop_adma_get_max_xor(void)
{
return 32;
}
static inline u32 iop_chan_get_current_descriptor(struct iop_adma_chan *chan)
{
int id = chan->device->id;
switch (id) {
case DMA0_ID:
case DMA1_ID:
return __raw_readl(DMA_DAR(chan));
case AAU_ID:
return __raw_readl(AAU_ADAR(chan));
default:
BUG();
}
return 0;
}
static inline void iop_chan_set_next_descriptor(struct iop_adma_chan *chan,
u32 next_desc_addr)
{
int id = chan->device->id;
switch (id) {
case DMA0_ID:
case DMA1_ID:
__raw_writel(next_desc_addr, DMA_NDAR(chan));
break;
case AAU_ID:
__raw_writel(next_desc_addr, AAU_ANDAR(chan));
break;
}
}
#define IOP_ADMA_STATUS_BUSY (1 << 10)
#define IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT (1024)
#define IOP_ADMA_XOR_MAX_BYTE_COUNT (16 * 1024 * 1024)
#define IOP_ADMA_MAX_BYTE_COUNT (16 * 1024 * 1024)
static inline int iop_chan_is_busy(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(DMA_CSR(chan));
return (status & IOP_ADMA_STATUS_BUSY) ? 1 : 0;
}
static inline int iop_desc_is_aligned(struct iop_adma_desc_slot *desc,
int num_slots)
{
/* num_slots will only ever be 1, 2, 4, or 8 */
return (desc->idx & (num_slots - 1)) ? 0 : 1;
}
/* to do: support large (i.e. > hw max) buffer sizes */
static inline int iop_chan_memcpy_slot_count(size_t len, int *slots_per_op)
{
*slots_per_op = 1;
return 1;
}
/* to do: support large (i.e. > hw max) buffer sizes */
static inline int iop_chan_memset_slot_count(size_t len, int *slots_per_op)
{
*slots_per_op = 1;
return 1;
}
static inline int iop3xx_aau_xor_slot_count(size_t len, int src_cnt,
int *slots_per_op)
{
const static int slot_count_table[] = { 0,
1, 1, 1, 1, /* 01 - 04 */
2, 2, 2, 2, /* 05 - 08 */
4, 4, 4, 4, /* 09 - 12 */
4, 4, 4, 4, /* 13 - 16 */
8, 8, 8, 8, /* 17 - 20 */
8, 8, 8, 8, /* 21 - 24 */
8, 8, 8, 8, /* 25 - 28 */
8, 8, 8, 8, /* 29 - 32 */
};
*slots_per_op = slot_count_table[src_cnt];
return *slots_per_op;
}
static inline int
iop_chan_interrupt_slot_count(int *slots_per_op, struct iop_adma_chan *chan)
{
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return iop_chan_memcpy_slot_count(0, slots_per_op);
case AAU_ID:
return iop3xx_aau_xor_slot_count(0, 2, slots_per_op);
default:
BUG();
}
return 0;
}
static inline int iop_chan_xor_slot_count(size_t len, int src_cnt,
int *slots_per_op)
{
int slot_cnt = iop3xx_aau_xor_slot_count(len, src_cnt, slots_per_op);
if (len <= IOP_ADMA_XOR_MAX_BYTE_COUNT)
return slot_cnt;
len -= IOP_ADMA_XOR_MAX_BYTE_COUNT;
while (len > IOP_ADMA_XOR_MAX_BYTE_COUNT) {
len -= IOP_ADMA_XOR_MAX_BYTE_COUNT;
slot_cnt += *slots_per_op;
}
if (len)
slot_cnt += *slots_per_op;
return slot_cnt;
}
/* zero sum on iop3xx is limited to 1k at a time so it requires multiple
* descriptors
*/
static inline int iop_chan_zero_sum_slot_count(size_t len, int src_cnt,
int *slots_per_op)
{
int slot_cnt = iop3xx_aau_xor_slot_count(len, src_cnt, slots_per_op);
if (len <= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT)
return slot_cnt;
len -= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
while (len > IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT) {
len -= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
slot_cnt += *slots_per_op;
}
if (len)
slot_cnt += *slots_per_op;
return slot_cnt;
}
static inline u32 iop_desc_get_dest_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return hw_desc.dma->dest_addr;
case AAU_ID:
return hw_desc.aau->dest_addr;
default:
BUG();
}
return 0;
}
static inline u32 iop_desc_get_byte_count(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return hw_desc.dma->byte_count;
case AAU_ID:
return hw_desc.aau->byte_count;
default:
BUG();
}
return 0;
}
/* translate the src_idx to a descriptor word index */
static inline int __desc_idx(int src_idx)
{
const static int desc_idx_table[] = { 0, 0, 0, 0,
0, 1, 2, 3,
5, 6, 7, 8,
9, 10, 11, 12,
14, 15, 16, 17,
18, 19, 20, 21,
23, 24, 25, 26,
27, 28, 29, 30,
};
return desc_idx_table[src_idx];
}
static inline u32 iop_desc_get_src_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
int src_idx)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return hw_desc.dma->src_addr;
case AAU_ID:
break;
default:
BUG();
}
if (src_idx < 4)
return hw_desc.aau->src[src_idx];
else
return hw_desc.aau->src_edc[__desc_idx(src_idx)].src_addr;
}
static inline void iop3xx_aau_desc_set_src_addr(struct iop3xx_desc_aau *hw_desc,
int src_idx, dma_addr_t addr)
{
if (src_idx < 4)
hw_desc->src[src_idx] = addr;
else
hw_desc->src_edc[__desc_idx(src_idx)].src_addr = addr;
}
static inline void
iop_desc_init_memcpy(struct iop_adma_desc_slot *desc, int int_en)
{
struct iop3xx_desc_dma *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop3xx_dma_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.mem_to_mem_en = 1;
u_desc_ctrl.field.pci_transaction = 0xe; /* memory read block */
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
hw_desc->upper_pci_src_addr = 0;
hw_desc->crc_addr = 0;
}
static inline void
iop_desc_init_memset(struct iop_adma_desc_slot *desc, int int_en)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop3xx_aau_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
u_desc_ctrl.field.blk1_cmd_ctrl = 0x2; /* memory block fill */
u_desc_ctrl.field.dest_write_en = 1;
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
}
static inline u32
iop3xx_desc_init_xor(struct iop3xx_desc_aau *hw_desc, int src_cnt, int int_en)
{
int i, shift;
u32 edcr;
union {
u32 value;
struct iop3xx_aau_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
switch (src_cnt) {
case 25 ... 32:
u_desc_ctrl.field.blk_ctrl = 0x3; /* use EDCR[2:0] */
edcr = 0;
shift = 1;
for (i = 24; i < src_cnt; i++) {
edcr |= (1 << shift);
shift += 3;
}
hw_desc->src_edc[AAU_EDCR2_IDX].e_desc_ctrl = edcr;
src_cnt = 24;
/* fall through */
case 17 ... 24:
if (!u_desc_ctrl.field.blk_ctrl) {
hw_desc->src_edc[AAU_EDCR2_IDX].e_desc_ctrl = 0;
u_desc_ctrl.field.blk_ctrl = 0x3; /* use EDCR[2:0] */
}
edcr = 0;
shift = 1;
for (i = 16; i < src_cnt; i++) {
edcr |= (1 << shift);
shift += 3;
}
hw_desc->src_edc[AAU_EDCR1_IDX].e_desc_ctrl = edcr;
src_cnt = 16;
/* fall through */
case 9 ... 16:
if (!u_desc_ctrl.field.blk_ctrl)
u_desc_ctrl.field.blk_ctrl = 0x2; /* use EDCR0 */
edcr = 0;
shift = 1;
for (i = 8; i < src_cnt; i++) {
edcr |= (1 << shift);
shift += 3;
}
hw_desc->src_edc[AAU_EDCR0_IDX].e_desc_ctrl = edcr;
src_cnt = 8;
/* fall through */
case 2 ... 8:
shift = 1;
for (i = 0; i < src_cnt; i++) {
u_desc_ctrl.value |= (1 << shift);
shift += 3;
}
if (!u_desc_ctrl.field.blk_ctrl && src_cnt > 4)
u_desc_ctrl.field.blk_ctrl = 0x1; /* use mini-desc */
}
u_desc_ctrl.field.dest_write_en = 1;
u_desc_ctrl.field.blk1_cmd_ctrl = 0x7; /* direct fill */
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
return u_desc_ctrl.value;
}
static inline void
iop_desc_init_xor(struct iop_adma_desc_slot *desc, int src_cnt, int int_en)
{
iop3xx_desc_init_xor(desc->hw_desc, src_cnt, int_en);
}
/* return the number of operations */
static inline int
iop_desc_init_zero_sum(struct iop_adma_desc_slot *desc, int src_cnt, int int_en)
{
int slot_cnt = desc->slot_cnt, slots_per_op = desc->slots_per_op;
struct iop3xx_desc_aau *hw_desc, *prev_hw_desc, *iter;
union {
u32 value;
struct iop3xx_aau_desc_ctrl field;
} u_desc_ctrl;
int i, j;
hw_desc = desc->hw_desc;
for (i = 0, j = 0; (slot_cnt -= slots_per_op) >= 0;
i += slots_per_op, j++) {
iter = iop_hw_desc_slot_idx(hw_desc, i);
u_desc_ctrl.value = iop3xx_desc_init_xor(iter, src_cnt, int_en);
u_desc_ctrl.field.dest_write_en = 0;
u_desc_ctrl.field.zero_result_en = 1;
u_desc_ctrl.field.int_en = int_en;
iter->desc_ctrl = u_desc_ctrl.value;
/* for the subsequent descriptors preserve the store queue
* and chain them together
*/
if (i) {
prev_hw_desc =
iop_hw_desc_slot_idx(hw_desc, i - slots_per_op);
prev_hw_desc->next_desc =
(u32) (desc->async_tx.phys + (i << 5));
}
}
return j;
}
static inline void
iop_desc_init_null_xor(struct iop_adma_desc_slot *desc, int src_cnt, int int_en)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc;
union {
u32 value;
struct iop3xx_aau_desc_ctrl field;
} u_desc_ctrl;
u_desc_ctrl.value = 0;
switch (src_cnt) {
case 25 ... 32:
u_desc_ctrl.field.blk_ctrl = 0x3; /* use EDCR[2:0] */
hw_desc->src_edc[AAU_EDCR2_IDX].e_desc_ctrl = 0;
/* fall through */
case 17 ... 24:
if (!u_desc_ctrl.field.blk_ctrl) {
hw_desc->src_edc[AAU_EDCR2_IDX].e_desc_ctrl = 0;
u_desc_ctrl.field.blk_ctrl = 0x3; /* use EDCR[2:0] */
}
hw_desc->src_edc[AAU_EDCR1_IDX].e_desc_ctrl = 0;
/* fall through */
case 9 ... 16:
if (!u_desc_ctrl.field.blk_ctrl)
u_desc_ctrl.field.blk_ctrl = 0x2; /* use EDCR0 */
hw_desc->src_edc[AAU_EDCR0_IDX].e_desc_ctrl = 0;
/* fall through */
case 1 ... 8:
if (!u_desc_ctrl.field.blk_ctrl && src_cnt > 4)
u_desc_ctrl.field.blk_ctrl = 0x1; /* use mini-desc */
}
u_desc_ctrl.field.dest_write_en = 0;
u_desc_ctrl.field.int_en = int_en;
hw_desc->desc_ctrl = u_desc_ctrl.value;
}
static inline void iop_desc_set_byte_count(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
u32 byte_count)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
hw_desc.dma->byte_count = byte_count;
break;
case AAU_ID:
hw_desc.aau->byte_count = byte_count;
break;
default:
BUG();
}
}
static inline void
iop_desc_init_interrupt(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
iop_desc_init_memcpy(desc, 1);
hw_desc.dma->byte_count = 0;
hw_desc.dma->dest_addr = 0;
hw_desc.dma->src_addr = 0;
break;
case AAU_ID:
iop_desc_init_null_xor(desc, 2, 1);
hw_desc.aau->byte_count = 0;
hw_desc.aau->dest_addr = 0;
hw_desc.aau->src[0] = 0;
hw_desc.aau->src[1] = 0;
break;
default:
BUG();
}
}
static inline void
iop_desc_set_zero_sum_byte_count(struct iop_adma_desc_slot *desc, u32 len)
{
int slots_per_op = desc->slots_per_op;
struct iop3xx_desc_aau *hw_desc = desc->hw_desc, *iter;
int i = 0;
if (len <= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT) {
hw_desc->byte_count = len;
} else {
do {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iter->byte_count = IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
len -= IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT;
i += slots_per_op;
} while (len > IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT);
if (len) {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iter->byte_count = len;
}
}
}
static inline void iop_desc_set_dest_addr(struct iop_adma_desc_slot *desc,
struct iop_adma_chan *chan,
dma_addr_t addr)
{
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
hw_desc.dma->dest_addr = addr;
break;
case AAU_ID:
hw_desc.aau->dest_addr = addr;
break;
default:
BUG();
}
}
static inline void iop_desc_set_memcpy_src_addr(struct iop_adma_desc_slot *desc,
dma_addr_t addr)
{
struct iop3xx_desc_dma *hw_desc = desc->hw_desc;
hw_desc->src_addr = addr;
}
static inline void
iop_desc_set_zero_sum_src_addr(struct iop_adma_desc_slot *desc, int src_idx,
dma_addr_t addr)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc, *iter;
int slot_cnt = desc->slot_cnt, slots_per_op = desc->slots_per_op;
int i;
for (i = 0; (slot_cnt -= slots_per_op) >= 0;
i += slots_per_op, addr += IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT) {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iop3xx_aau_desc_set_src_addr(iter, src_idx, addr);
}
}
static inline void iop_desc_set_xor_src_addr(struct iop_adma_desc_slot *desc,
int src_idx, dma_addr_t addr)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc, *iter;
int slot_cnt = desc->slot_cnt, slots_per_op = desc->slots_per_op;
int i;
for (i = 0; (slot_cnt -= slots_per_op) >= 0;
i += slots_per_op, addr += IOP_ADMA_XOR_MAX_BYTE_COUNT) {
iter = iop_hw_desc_slot_idx(hw_desc, i);
iop3xx_aau_desc_set_src_addr(iter, src_idx, addr);
}
}
static inline void iop_desc_set_next_desc(struct iop_adma_desc_slot *desc,
u32 next_desc_addr)
{
/* hw_desc->next_desc is the same location for all channels */
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
BUG_ON(hw_desc.dma->next_desc);
hw_desc.dma->next_desc = next_desc_addr;
}
static inline u32 iop_desc_get_next_desc(struct iop_adma_desc_slot *desc)
{
/* hw_desc->next_desc is the same location for all channels */
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
return hw_desc.dma->next_desc;
}
static inline void iop_desc_clear_next_desc(struct iop_adma_desc_slot *desc)
{
/* hw_desc->next_desc is the same location for all channels */
union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, };
hw_desc.dma->next_desc = 0;
}
static inline void iop_desc_set_block_fill_val(struct iop_adma_desc_slot *desc,
u32 val)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc;
hw_desc->src[0] = val;
}
static inline int iop_desc_get_zero_result(struct iop_adma_desc_slot *desc)
{
struct iop3xx_desc_aau *hw_desc = desc->hw_desc;
struct iop3xx_aau_desc_ctrl desc_ctrl = hw_desc->desc_ctrl_field;
BUG_ON(!(desc_ctrl.tx_complete && desc_ctrl.zero_result_en));
return desc_ctrl.zero_result_err;
}
static inline void iop_chan_append(struct iop_adma_chan *chan)
{
u32 dma_chan_ctrl;
/* workaround dropped interrupts on 3xx */
mod_timer(&chan->cleanup_watchdog, jiffies + msecs_to_jiffies(3));
dma_chan_ctrl = __raw_readl(DMA_CCR(chan));
dma_chan_ctrl |= 0x2;
__raw_writel(dma_chan_ctrl, DMA_CCR(chan));
}
static inline void iop_chan_idle(int busy, struct iop_adma_chan *chan)
{
if (!busy)
del_timer(&chan->cleanup_watchdog);
}
static inline u32 iop_chan_get_status(struct iop_adma_chan *chan)
{
return __raw_readl(DMA_CSR(chan));
}
static inline void iop_chan_disable(struct iop_adma_chan *chan)
{
u32 dma_chan_ctrl = __raw_readl(DMA_CCR(chan));
dma_chan_ctrl &= ~1;
__raw_writel(dma_chan_ctrl, DMA_CCR(chan));
}
static inline void iop_chan_enable(struct iop_adma_chan *chan)
{
u32 dma_chan_ctrl = __raw_readl(DMA_CCR(chan));
dma_chan_ctrl |= 1;
__raw_writel(dma_chan_ctrl, DMA_CCR(chan));
}
static inline void iop_adma_device_clear_eot_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(DMA_CSR(chan));
status &= (1 << 9);
__raw_writel(status, DMA_CSR(chan));
}
static inline void iop_adma_device_clear_eoc_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(DMA_CSR(chan));
status &= (1 << 8);
__raw_writel(status, DMA_CSR(chan));
}
static inline void iop_adma_device_clear_err_status(struct iop_adma_chan *chan)
{
u32 status = __raw_readl(DMA_CSR(chan));
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
status &= (1 << 5) | (1 << 3) | (1 << 2) | (1 << 1);
break;
case AAU_ID:
status &= (1 << 5);
break;
default:
BUG();
}
__raw_writel(status, DMA_CSR(chan));
}
static inline int
iop_is_err_int_parity(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
static inline int
iop_is_err_mcu_abort(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
static inline int
iop_is_err_int_tabort(unsigned long status, struct iop_adma_chan *chan)
{
return 0;
}
static inline int
iop_is_err_int_mabort(unsigned long status, struct iop_adma_chan *chan)
{
return test_bit(5, &status);
}
static inline int
iop_is_err_pci_tabort(unsigned long status, struct iop_adma_chan *chan)
{
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return test_bit(2, &status);
default:
return 0;
}
}
static inline int
iop_is_err_pci_mabort(unsigned long status, struct iop_adma_chan *chan)
{
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return test_bit(3, &status);
default:
return 0;
}
}
static inline int
iop_is_err_split_tx(unsigned long status, struct iop_adma_chan *chan)
{
switch (chan->device->id) {
case DMA0_ID:
case DMA1_ID:
return test_bit(1, &status);
default:
return 0;
}
}
#endif /* _ADMA_H */

View File

@@ -144,24 +144,9 @@ extern int init_atu;
#define IOP3XX_IAR (volatile u32 *)IOP3XX_REG_ADDR(0x0380)
/* DMA Controller */
#define IOP3XX_DMA0_CCR (volatile u32 *)IOP3XX_REG_ADDR(0x0400)
#define IOP3XX_DMA0_CSR (volatile u32 *)IOP3XX_REG_ADDR(0x0404)
#define IOP3XX_DMA0_DAR (volatile u32 *)IOP3XX_REG_ADDR(0x040c)
#define IOP3XX_DMA0_NDAR (volatile u32 *)IOP3XX_REG_ADDR(0x0410)
#define IOP3XX_DMA0_PADR (volatile u32 *)IOP3XX_REG_ADDR(0x0414)
#define IOP3XX_DMA0_PUADR (volatile u32 *)IOP3XX_REG_ADDR(0x0418)
#define IOP3XX_DMA0_LADR (volatile u32 *)IOP3XX_REG_ADDR(0x041c)
#define IOP3XX_DMA0_BCR (volatile u32 *)IOP3XX_REG_ADDR(0x0420)
#define IOP3XX_DMA0_DCR (volatile u32 *)IOP3XX_REG_ADDR(0x0424)
#define IOP3XX_DMA1_CCR (volatile u32 *)IOP3XX_REG_ADDR(0x0440)
#define IOP3XX_DMA1_CSR (volatile u32 *)IOP3XX_REG_ADDR(0x0444)
#define IOP3XX_DMA1_DAR (volatile u32 *)IOP3XX_REG_ADDR(0x044c)
#define IOP3XX_DMA1_NDAR (volatile u32 *)IOP3XX_REG_ADDR(0x0450)
#define IOP3XX_DMA1_PADR (volatile u32 *)IOP3XX_REG_ADDR(0x0454)
#define IOP3XX_DMA1_PUADR (volatile u32 *)IOP3XX_REG_ADDR(0x0458)
#define IOP3XX_DMA1_LADR (volatile u32 *)IOP3XX_REG_ADDR(0x045c)
#define IOP3XX_DMA1_BCR (volatile u32 *)IOP3XX_REG_ADDR(0x0460)
#define IOP3XX_DMA1_DCR (volatile u32 *)IOP3XX_REG_ADDR(0x0464)
#define IOP3XX_DMA_PHYS_BASE(chan) (IOP3XX_PERIPHERAL_PHYS_BASE + \
(0x400 + (chan << 6)))
#define IOP3XX_DMA_UPPER_PA(chan) (IOP3XX_DMA_PHYS_BASE(chan) + 0x27)
/* Peripheral bus interface */
#define IOP3XX_PBCR (volatile u32 *)IOP3XX_REG_ADDR(0x0680)
@@ -210,48 +195,8 @@ extern int init_atu;
#define IOP_TMR_RATIO_1_1 0x00
/* Application accelerator unit */
#define IOP3XX_AAU_ACR (volatile u32 *)IOP3XX_REG_ADDR(0x0800)
#define IOP3XX_AAU_ASR (volatile u32 *)IOP3XX_REG_ADDR(0x0804)
#define IOP3XX_AAU_ADAR (volatile u32 *)IOP3XX_REG_ADDR(0x0808)
#define IOP3XX_AAU_ANDAR (volatile u32 *)IOP3XX_REG_ADDR(0x080c)
#define IOP3XX_AAU_SAR1 (volatile u32 *)IOP3XX_REG_ADDR(0x0810)
#define IOP3XX_AAU_SAR2 (volatile u32 *)IOP3XX_REG_ADDR(0x0814)
#define IOP3XX_AAU_SAR3 (volatile u32 *)IOP3XX_REG_ADDR(0x0818)
#define IOP3XX_AAU_SAR4 (volatile u32 *)IOP3XX_REG_ADDR(0x081c)
#define IOP3XX_AAU_DAR (volatile u32 *)IOP3XX_REG_ADDR(0x0820)
#define IOP3XX_AAU_ABCR (volatile u32 *)IOP3XX_REG_ADDR(0x0824)
#define IOP3XX_AAU_ADCR (volatile u32 *)IOP3XX_REG_ADDR(0x0828)
#define IOP3XX_AAU_SAR5 (volatile u32 *)IOP3XX_REG_ADDR(0x082c)
#define IOP3XX_AAU_SAR6 (volatile u32 *)IOP3XX_REG_ADDR(0x0830)
#define IOP3XX_AAU_SAR7 (volatile u32 *)IOP3XX_REG_ADDR(0x0834)
#define IOP3XX_AAU_SAR8 (volatile u32 *)IOP3XX_REG_ADDR(0x0838)
#define IOP3XX_AAU_EDCR0 (volatile u32 *)IOP3XX_REG_ADDR(0x083c)
#define IOP3XX_AAU_SAR9 (volatile u32 *)IOP3XX_REG_ADDR(0x0840)
#define IOP3XX_AAU_SAR10 (volatile u32 *)IOP3XX_REG_ADDR(0x0844)
#define IOP3XX_AAU_SAR11 (volatile u32 *)IOP3XX_REG_ADDR(0x0848)
#define IOP3XX_AAU_SAR12 (volatile u32 *)IOP3XX_REG_ADDR(0x084c)
#define IOP3XX_AAU_SAR13 (volatile u32 *)IOP3XX_REG_ADDR(0x0850)
#define IOP3XX_AAU_SAR14 (volatile u32 *)IOP3XX_REG_ADDR(0x0854)
#define IOP3XX_AAU_SAR15 (volatile u32 *)IOP3XX_REG_ADDR(0x0858)
#define IOP3XX_AAU_SAR16 (volatile u32 *)IOP3XX_REG_ADDR(0x085c)
#define IOP3XX_AAU_EDCR1 (volatile u32 *)IOP3XX_REG_ADDR(0x0860)
#define IOP3XX_AAU_SAR17 (volatile u32 *)IOP3XX_REG_ADDR(0x0864)
#define IOP3XX_AAU_SAR18 (volatile u32 *)IOP3XX_REG_ADDR(0x0868)
#define IOP3XX_AAU_SAR19 (volatile u32 *)IOP3XX_REG_ADDR(0x086c)
#define IOP3XX_AAU_SAR20 (volatile u32 *)IOP3XX_REG_ADDR(0x0870)
#define IOP3XX_AAU_SAR21 (volatile u32 *)IOP3XX_REG_ADDR(0x0874)
#define IOP3XX_AAU_SAR22 (volatile u32 *)IOP3XX_REG_ADDR(0x0878)
#define IOP3XX_AAU_SAR23 (volatile u32 *)IOP3XX_REG_ADDR(0x087c)
#define IOP3XX_AAU_SAR24 (volatile u32 *)IOP3XX_REG_ADDR(0x0880)
#define IOP3XX_AAU_EDCR2 (volatile u32 *)IOP3XX_REG_ADDR(0x0884)
#define IOP3XX_AAU_SAR25 (volatile u32 *)IOP3XX_REG_ADDR(0x0888)
#define IOP3XX_AAU_SAR26 (volatile u32 *)IOP3XX_REG_ADDR(0x088c)
#define IOP3XX_AAU_SAR27 (volatile u32 *)IOP3XX_REG_ADDR(0x0890)
#define IOP3XX_AAU_SAR28 (volatile u32 *)IOP3XX_REG_ADDR(0x0894)
#define IOP3XX_AAU_SAR29 (volatile u32 *)IOP3XX_REG_ADDR(0x0898)
#define IOP3XX_AAU_SAR30 (volatile u32 *)IOP3XX_REG_ADDR(0x089c)
#define IOP3XX_AAU_SAR31 (volatile u32 *)IOP3XX_REG_ADDR(0x08a0)
#define IOP3XX_AAU_SAR32 (volatile u32 *)IOP3XX_REG_ADDR(0x08a4)
#define IOP3XX_AAU_PHYS_BASE (IOP3XX_PERIPHERAL_PHYS_BASE + 0x800)
#define IOP3XX_AAU_UPPER_PA (IOP3XX_AAU_PHYS_BASE + 0xa7)
/* I2C bus interface unit */
#define IOP3XX_ICR0 (volatile u32 *)IOP3XX_REG_ADDR(0x1680)
@@ -329,6 +274,9 @@ static inline void write_tisr(u32 val)
asm volatile("mcr p6, 0, %0, c6, c1, 0" : : "r" (val));
}
extern struct platform_device iop3xx_dma_0_channel;
extern struct platform_device iop3xx_dma_1_channel;
extern struct platform_device iop3xx_aau_channel;
extern struct platform_device iop3xx_i2c0_device;
extern struct platform_device iop3xx_i2c1_device;

View File

@@ -0,0 +1,118 @@
/*
* Copyright © 2006, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef IOP_ADMA_H
#define IOP_ADMA_H
#include <linux/types.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#define IOP_ADMA_SLOT_SIZE 32
#define IOP_ADMA_THRESHOLD 4
/**
* struct iop_adma_device - internal representation of an ADMA device
* @pdev: Platform device
* @id: HW ADMA Device selector
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @dma_desc_pool_virt: base of DMA descriptor region (CPU address)
* @common: embedded struct dma_device
*/
struct iop_adma_device {
struct platform_device *pdev;
int id;
dma_addr_t dma_desc_pool;
void *dma_desc_pool_virt;
struct dma_device common;
};
/**
* struct iop_adma_chan - internal representation of an ADMA device
* @pending: allows batching of hardware operations
* @completed_cookie: identifier for the most recently completed operation
* @lock: serializes enqueue/dequeue operations to the slot pool
* @mmr_base: memory mapped register base
* @chain: device chain view of the descriptors
* @device: parent device
* @common: common dmaengine channel object members
* @last_used: place holder for allocation to continue from where it left off
* @all_slots: complete domain of slots usable by the channel
* @cleanup_watchdog: workaround missed interrupts on iop3xx
* @slots_allocated: records the actual size of the descriptor slot pool
* @irq_tasklet: bottom half where iop_adma_slot_cleanup runs
*/
struct iop_adma_chan {
int pending;
dma_cookie_t completed_cookie;
spinlock_t lock; /* protects the descriptor slot pool */
void __iomem *mmr_base;
struct list_head chain;
struct iop_adma_device *device;
struct dma_chan common;
struct iop_adma_desc_slot *last_used;
struct list_head all_slots;
struct timer_list cleanup_watchdog;
int slots_allocated;
struct tasklet_struct irq_tasklet;
};
/**
* struct iop_adma_desc_slot - IOP-ADMA software descriptor
* @slot_node: node on the iop_adma_chan.all_slots list
* @chain_node: node on the op_adma_chan.chain list
* @hw_desc: virtual address of the hardware descriptor chain
* @phys: hardware address of the hardware descriptor chain
* @group_head: first operation in a transaction
* @slot_cnt: total slots used in an transaction (group of operations)
* @slots_per_op: number of slots per operation
* @idx: pool index
* @unmap_src_cnt: number of xor sources
* @unmap_len: transaction bytecount
* @async_tx: support for the async_tx api
* @group_list: list of slots that make up a multi-descriptor transaction
* for example transfer lengths larger than the supported hw max
* @xor_check_result: result of zero sum
* @crc32_result: result crc calculation
*/
struct iop_adma_desc_slot {
struct list_head slot_node;
struct list_head chain_node;
void *hw_desc;
struct iop_adma_desc_slot *group_head;
u16 slot_cnt;
u16 slots_per_op;
u16 idx;
u16 unmap_src_cnt;
size_t unmap_len;
struct dma_async_tx_descriptor async_tx;
union {
u32 *xor_check_result;
u32 *crc32_result;
};
};
struct iop_adma_platform_data {
int hw_id;
dma_cap_mask_t cap_mask;
size_t pool_size;
};
#define to_iop_sw_desc(addr_hw_desc) \
container_of(addr_hw_desc, struct iop_adma_desc_slot, hw_desc)
#define iop_hw_desc_slot_idx(hw_desc, idx) \
( (void *) (((unsigned long) hw_desc) + ((idx) << 5)) )
#endif

156
include/linux/async_tx.h Normal file
View File

@@ -0,0 +1,156 @@
/*
* Copyright © 2006, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef _ASYNC_TX_H_
#define _ASYNC_TX_H_
#include <linux/dmaengine.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
/**
* dma_chan_ref - object used to manage dma channels received from the
* dmaengine core.
* @chan - the channel being tracked
* @node - node for the channel to be placed on async_tx_master_list
* @rcu - for list_del_rcu
* @count - number of times this channel is listed in the pool
* (for channels with multiple capabiities)
*/
struct dma_chan_ref {
struct dma_chan *chan;
struct list_head node;
struct rcu_head rcu;
atomic_t count;
};
/**
* async_tx_flags - modifiers for the async_* calls
* @ASYNC_TX_XOR_ZERO_DST: this flag must be used for xor operations where the
* the destination address is not a source. The asynchronous case handles this
* implicitly, the synchronous case needs to zero the destination block.
* @ASYNC_TX_XOR_DROP_DST: this flag must be used if the destination address is
* also one of the source addresses. In the synchronous case the destination
* address is an implied source, whereas the asynchronous case it must be listed
* as a source. The destination address must be the first address in the source
* array.
* @ASYNC_TX_ASSUME_COHERENT: skip cache maintenance operations
* @ASYNC_TX_ACK: immediately ack the descriptor, precludes setting up a
* dependency chain
* @ASYNC_TX_DEP_ACK: ack the dependency descriptor. Useful for chaining.
* @ASYNC_TX_KMAP_SRC: if the transaction is to be performed synchronously
* take an atomic mapping (KM_USER0) on the source page(s)
* @ASYNC_TX_KMAP_DST: if the transaction is to be performed synchronously
* take an atomic mapping (KM_USER0) on the dest page(s)
*/
enum async_tx_flags {
ASYNC_TX_XOR_ZERO_DST = (1 << 0),
ASYNC_TX_XOR_DROP_DST = (1 << 1),
ASYNC_TX_ASSUME_COHERENT = (1 << 2),
ASYNC_TX_ACK = (1 << 3),
ASYNC_TX_DEP_ACK = (1 << 4),
ASYNC_TX_KMAP_SRC = (1 << 5),
ASYNC_TX_KMAP_DST = (1 << 6),
};
#ifdef CONFIG_DMA_ENGINE
void async_tx_issue_pending_all(void);
enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
void async_tx_run_dependencies(struct dma_async_tx_descriptor *tx);
struct dma_chan *
async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
enum dma_transaction_type tx_type);
#else
static inline void async_tx_issue_pending_all(void)
{
do { } while (0);
}
static inline enum dma_status
dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
{
return DMA_SUCCESS;
}
static inline void
async_tx_run_dependencies(struct dma_async_tx_descriptor *tx,
struct dma_chan *host_chan)
{
do { } while (0);
}
static inline struct dma_chan *
async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
enum dma_transaction_type tx_type)
{
return NULL;
}
#endif
/**
* async_tx_sync_epilog - actions to take if an operation is run synchronously
* @flags: async_tx flags
* @depend_tx: transaction depends on depend_tx
* @cb_fn: function to call when the transaction completes
* @cb_fn_param: parameter to pass to the callback routine
*/
static inline void
async_tx_sync_epilog(unsigned long flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param)
{
if (cb_fn)
cb_fn(cb_fn_param);
if (depend_tx && (flags & ASYNC_TX_DEP_ACK))
async_tx_ack(depend_tx);
}
void
async_tx_submit(struct dma_chan *chan, struct dma_async_tx_descriptor *tx,
enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
struct dma_async_tx_descriptor *
async_xor(struct page *dest, struct page **src_list, unsigned int offset,
int src_cnt, size_t len, enum async_tx_flags flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
struct dma_async_tx_descriptor *
async_xor_zero_sum(struct page *dest, struct page **src_list,
unsigned int offset, int src_cnt, size_t len,
u32 *result, enum async_tx_flags flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
struct dma_async_tx_descriptor *
async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
unsigned int src_offset, size_t len, enum async_tx_flags flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
struct dma_async_tx_descriptor *
async_memset(struct page *dest, int val, unsigned int offset,
size_t len, enum async_tx_flags flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
struct dma_async_tx_descriptor *
async_trigger_callback(enum async_tx_flags flags,
struct dma_async_tx_descriptor *depend_tx,
dma_async_tx_callback cb_fn, void *cb_fn_param);
#endif /* _ASYNC_TX_H_ */

View File

@@ -21,28 +21,39 @@
#ifndef DMAENGINE_H
#define DMAENGINE_H
#ifdef CONFIG_DMA_ENGINE
#include <linux/device.h>
#include <linux/uio.h>
#include <linux/kref.h>
#include <linux/completion.h>
#include <linux/rcupdate.h>
#include <linux/dma-mapping.h>
/**
* enum dma_event - resource PNP/power managment events
* enum dma_state - resource PNP/power managment state
* @DMA_RESOURCE_SUSPEND: DMA device going into low power state
* @DMA_RESOURCE_RESUME: DMA device returning to full power
* @DMA_RESOURCE_ADDED: DMA device added to the system
* @DMA_RESOURCE_AVAILABLE: DMA device available to the system
* @DMA_RESOURCE_REMOVED: DMA device removed from the system
*/
enum dma_event {
enum dma_state {
DMA_RESOURCE_SUSPEND,
DMA_RESOURCE_RESUME,
DMA_RESOURCE_ADDED,
DMA_RESOURCE_AVAILABLE,
DMA_RESOURCE_REMOVED,
};
/**
* enum dma_state_client - state of the channel in the client
* @DMA_ACK: client would like to use, or was using this channel
* @DMA_DUP: client has already seen this channel, or is not using this channel
* @DMA_NAK: client does not want to see any more channels
*/
enum dma_state_client {
DMA_ACK,
DMA_DUP,
DMA_NAK,
};
/**
* typedef dma_cookie_t - an opaque DMA cookie
*
@@ -64,6 +75,31 @@ enum dma_status {
DMA_ERROR,
};
/**
* enum dma_transaction_type - DMA transaction types/indexes
*/
enum dma_transaction_type {
DMA_MEMCPY,
DMA_XOR,
DMA_PQ_XOR,
DMA_DUAL_XOR,
DMA_PQ_UPDATE,
DMA_ZERO_SUM,
DMA_PQ_ZERO_SUM,
DMA_MEMSET,
DMA_MEMCPY_CRC32C,
DMA_INTERRUPT,
};
/* last transaction type for creation of the capabilities mask */
#define DMA_TX_TYPE_END (DMA_INTERRUPT + 1)
/**
* dma_cap_mask_t - capabilities bitmap modeled after cpumask_t.
* See linux/cpumask.h
*/
typedef struct { DECLARE_BITMAP(bits, DMA_TX_TYPE_END); } dma_cap_mask_t;
/**
* struct dma_chan_percpu - the per-CPU part of struct dma_chan
* @refcount: local_t used for open-coded "bigref" counting
@@ -80,7 +116,6 @@ struct dma_chan_percpu {
/**
* struct dma_chan - devices supply DMA channels, clients use them
* @client: ptr to the client user of this chan, will be %NULL when unused
* @device: ptr to the dma device who supplies this channel, always !%NULL
* @cookie: last cookie value returned to client
* @chan_id: channel ID for sysfs
@@ -88,12 +123,10 @@ struct dma_chan_percpu {
* @refcount: kref, used in "bigref" slow-mode
* @slow_ref: indicates that the DMA channel is free
* @rcu: the DMA channel's RCU head
* @client_node: used to add this to the client chan list
* @device_node: used to add this to the device chan list
* @local: per-cpu pointer to a struct dma_chan_percpu
*/
struct dma_chan {
struct dma_client *client;
struct dma_device *device;
dma_cookie_t cookie;
@@ -105,11 +138,11 @@ struct dma_chan {
int slow_ref;
struct rcu_head rcu;
struct list_head client_node;
struct list_head device_node;
struct dma_chan_percpu *local;
};
void dma_chan_cleanup(struct kref *kref);
static inline void dma_chan_get(struct dma_chan *chan)
@@ -134,169 +167,206 @@ static inline void dma_chan_put(struct dma_chan *chan)
/*
* typedef dma_event_callback - function pointer to a DMA event callback
* For each channel added to the system this routine is called for each client.
* If the client would like to use the channel it returns '1' to signal (ack)
* the dmaengine core to take out a reference on the channel and its
* corresponding device. A client must not 'ack' an available channel more
* than once. When a channel is removed all clients are notified. If a client
* is using the channel it must 'ack' the removal. A client must not 'ack' a
* removed channel more than once.
* @client - 'this' pointer for the client context
* @chan - channel to be acted upon
* @state - available or removed
*/
typedef void (*dma_event_callback) (struct dma_client *client,
struct dma_chan *chan, enum dma_event event);
struct dma_client;
typedef enum dma_state_client (*dma_event_callback) (struct dma_client *client,
struct dma_chan *chan, enum dma_state state);
/**
* struct dma_client - info on the entity making use of DMA services
* @event_callback: func ptr to call when something happens
* @chan_count: number of chans allocated
* @chans_desired: number of chans requested. Can be +/- chan_count
* @lock: protects access to the channels list
* @channels: the list of DMA channels allocated
* @cap_mask: only return channels that satisfy the requested capabilities
* a value of zero corresponds to any capability
* @global_node: list_head for global dma_client_list
*/
struct dma_client {
dma_event_callback event_callback;
unsigned int chan_count;
unsigned int chans_desired;
spinlock_t lock;
struct list_head channels;
dma_cap_mask_t cap_mask;
struct list_head global_node;
};
typedef void (*dma_async_tx_callback)(void *dma_async_param);
/**
* struct dma_async_tx_descriptor - async transaction descriptor
* ---dma generic offload fields---
* @cookie: tracking cookie for this transaction, set to -EBUSY if
* this tx is sitting on a dependency list
* @ack: the descriptor can not be reused until the client acknowledges
* receipt, i.e. has has a chance to establish any dependency chains
* @phys: physical address of the descriptor
* @tx_list: driver common field for operations that require multiple
* descriptors
* @chan: target channel for this operation
* @tx_submit: set the prepared descriptor(s) to be executed by the engine
* @tx_set_dest: set a destination address in a hardware descriptor
* @tx_set_src: set a source address in a hardware descriptor
* @callback: routine to call after this operation is complete
* @callback_param: general parameter to pass to the callback routine
* ---async_tx api specific fields---
* @depend_list: at completion this list of transactions are submitted
* @depend_node: allow this transaction to be executed after another
* transaction has completed, possibly on another channel
* @parent: pointer to the next level up in the dependency chain
* @lock: protect the dependency list
*/
struct dma_async_tx_descriptor {
dma_cookie_t cookie;
int ack;
dma_addr_t phys;
struct list_head tx_list;
struct dma_chan *chan;
dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
void (*tx_set_dest)(dma_addr_t addr,
struct dma_async_tx_descriptor *tx, int index);
void (*tx_set_src)(dma_addr_t addr,
struct dma_async_tx_descriptor *tx, int index);
dma_async_tx_callback callback;
void *callback_param;
struct list_head depend_list;
struct list_head depend_node;
struct dma_async_tx_descriptor *parent;
spinlock_t lock;
};
/**
* struct dma_device - info on the entity supplying DMA services
* @chancnt: how many DMA channels are supported
* @channels: the list of struct dma_chan
* @global_node: list_head for global dma_device_list
* @cap_mask: one or more dma_capability flags
* @max_xor: maximum number of xor sources, 0 if no capability
* @refcount: reference count
* @done: IO completion struct
* @dev_id: unique device ID
* @dev: struct device reference for dma mapping api
* @device_alloc_chan_resources: allocate resources and return the
* number of allocated descriptors
* @device_free_chan_resources: release DMA channel's resources
* @device_memcpy_buf_to_buf: memcpy buf pointer to buf pointer
* @device_memcpy_buf_to_pg: memcpy buf pointer to struct page
* @device_memcpy_pg_to_pg: memcpy struct page/offset to struct page/offset
* @device_memcpy_complete: poll the status of an IOAT DMA transaction
* @device_memcpy_issue_pending: push appended descriptors to hardware
* @device_prep_dma_memcpy: prepares a memcpy operation
* @device_prep_dma_xor: prepares a xor operation
* @device_prep_dma_zero_sum: prepares a zero_sum operation
* @device_prep_dma_memset: prepares a memset operation
* @device_prep_dma_interrupt: prepares an end of chain interrupt operation
* @device_dependency_added: async_tx notifies the channel about new deps
* @device_issue_pending: push pending transactions to hardware
*/
struct dma_device {
unsigned int chancnt;
struct list_head channels;
struct list_head global_node;
dma_cap_mask_t cap_mask;
int max_xor;
struct kref refcount;
struct completion done;
int dev_id;
struct device *dev;
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
dma_cookie_t (*device_memcpy_buf_to_buf)(struct dma_chan *chan,
void *dest, void *src, size_t len);
dma_cookie_t (*device_memcpy_buf_to_pg)(struct dma_chan *chan,
struct page *page, unsigned int offset, void *kdata,
size_t len);
dma_cookie_t (*device_memcpy_pg_to_pg)(struct dma_chan *chan,
struct page *dest_pg, unsigned int dest_off,
struct page *src_pg, unsigned int src_off, size_t len);
enum dma_status (*device_memcpy_complete)(struct dma_chan *chan,
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
struct dma_chan *chan, size_t len, int int_en);
struct dma_async_tx_descriptor *(*device_prep_dma_xor)(
struct dma_chan *chan, unsigned int src_cnt, size_t len,
int int_en);
struct dma_async_tx_descriptor *(*device_prep_dma_zero_sum)(
struct dma_chan *chan, unsigned int src_cnt, size_t len,
u32 *result, int int_en);
struct dma_async_tx_descriptor *(*device_prep_dma_memset)(
struct dma_chan *chan, int value, size_t len, int int_en);
struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)(
struct dma_chan *chan);
void (*device_dependency_added)(struct dma_chan *chan);
enum dma_status (*device_is_tx_complete)(struct dma_chan *chan,
dma_cookie_t cookie, dma_cookie_t *last,
dma_cookie_t *used);
void (*device_memcpy_issue_pending)(struct dma_chan *chan);
void (*device_issue_pending)(struct dma_chan *chan);
};
/* --- public DMA engine API --- */
struct dma_client *dma_async_client_register(dma_event_callback event_callback);
void dma_async_client_register(struct dma_client *client);
void dma_async_client_unregister(struct dma_client *client);
void dma_async_client_chan_request(struct dma_client *client,
unsigned int number);
/**
* dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses
* @chan: DMA channel to offload copy to
* @dest: destination address (virtual)
* @src: source address (virtual)
* @len: length
*
* Both @dest and @src must be mappable to a bus address according to the
* DMA mapping API rules for streaming mappings.
* Both @dest and @src must stay memory resident (kernel memory or locked
* user space pages).
*/
static inline dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan,
void *dest, void *src, size_t len)
{
int cpu = get_cpu();
per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
per_cpu_ptr(chan->local, cpu)->memcpy_count++;
put_cpu();
return chan->device->device_memcpy_buf_to_buf(chan, dest, src, len);
}
/**
* dma_async_memcpy_buf_to_pg - offloaded copy from address to page
* @chan: DMA channel to offload copy to
* @page: destination page
* @offset: offset in page to copy to
* @kdata: source address (virtual)
* @len: length
*
* Both @page/@offset and @kdata must be mappable to a bus address according
* to the DMA mapping API rules for streaming mappings.
* Both @page/@offset and @kdata must stay memory resident (kernel memory or
* locked user space pages)
*/
static inline dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan,
struct page *page, unsigned int offset, void *kdata, size_t len)
{
int cpu = get_cpu();
per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
per_cpu_ptr(chan->local, cpu)->memcpy_count++;
put_cpu();
return chan->device->device_memcpy_buf_to_pg(chan, page, offset,
kdata, len);
}
/**
* dma_async_memcpy_pg_to_pg - offloaded copy from page to page
* @chan: DMA channel to offload copy to
* @dest_pg: destination page
* @dest_off: offset in page to copy to
* @src_pg: source page
* @src_off: offset in page to copy from
* @len: length
*
* Both @dest_page/@dest_off and @src_page/@src_off must be mappable to a bus
* address according to the DMA mapping API rules for streaming mappings.
* Both @dest_page/@dest_off and @src_page/@src_off must stay memory resident
* (kernel memory or locked user space pages).
*/
static inline dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan,
void dma_async_client_chan_request(struct dma_client *client);
dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan,
void *dest, void *src, size_t len);
dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan,
struct page *page, unsigned int offset, void *kdata, size_t len);
dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan,
struct page *dest_pg, unsigned int dest_off, struct page *src_pg,
unsigned int src_off, size_t len)
{
int cpu = get_cpu();
per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
per_cpu_ptr(chan->local, cpu)->memcpy_count++;
put_cpu();
unsigned int src_off, size_t len);
void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
struct dma_chan *chan);
return chan->device->device_memcpy_pg_to_pg(chan, dest_pg, dest_off,
src_pg, src_off, len);
static inline void
async_tx_ack(struct dma_async_tx_descriptor *tx)
{
tx->ack = 1;
}
#define first_dma_cap(mask) __first_dma_cap(&(mask))
static inline int __first_dma_cap(const dma_cap_mask_t *srcp)
{
return min_t(int, DMA_TX_TYPE_END,
find_first_bit(srcp->bits, DMA_TX_TYPE_END));
}
#define next_dma_cap(n, mask) __next_dma_cap((n), &(mask))
static inline int __next_dma_cap(int n, const dma_cap_mask_t *srcp)
{
return min_t(int, DMA_TX_TYPE_END,
find_next_bit(srcp->bits, DMA_TX_TYPE_END, n+1));
}
#define dma_cap_set(tx, mask) __dma_cap_set((tx), &(mask))
static inline void
__dma_cap_set(enum dma_transaction_type tx_type, dma_cap_mask_t *dstp)
{
set_bit(tx_type, dstp->bits);
}
#define dma_has_cap(tx, mask) __dma_has_cap((tx), &(mask))
static inline int
__dma_has_cap(enum dma_transaction_type tx_type, dma_cap_mask_t *srcp)
{
return test_bit(tx_type, srcp->bits);
}
#define for_each_dma_cap_mask(cap, mask) \
for ((cap) = first_dma_cap(mask); \
(cap) < DMA_TX_TYPE_END; \
(cap) = next_dma_cap((cap), (mask)))
/**
* dma_async_memcpy_issue_pending - flush pending copies to HW
* dma_async_issue_pending - flush pending transactions to HW
* @chan: target DMA channel
*
* This allows drivers to push copies to HW in batches,
* reducing MMIO writes where possible.
*/
static inline void dma_async_memcpy_issue_pending(struct dma_chan *chan)
static inline void dma_async_issue_pending(struct dma_chan *chan)
{
return chan->device->device_memcpy_issue_pending(chan);
return chan->device->device_issue_pending(chan);
}
#define dma_async_memcpy_issue_pending(chan) dma_async_issue_pending(chan)
/**
* dma_async_memcpy_complete - poll for transaction completion
* dma_async_is_tx_complete - poll for transaction completion
* @chan: DMA channel
* @cookie: transaction identifier to check status of
* @last: returns last completed cookie, can be NULL
@@ -306,12 +376,15 @@ static inline void dma_async_memcpy_issue_pending(struct dma_chan *chan)
* internal state and can be used with dma_async_is_complete() to check
* the status of multiple cookies without re-checking hardware state.
*/
static inline enum dma_status dma_async_memcpy_complete(struct dma_chan *chan,
static inline enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
{
return chan->device->device_memcpy_complete(chan, cookie, last, used);
return chan->device->device_is_tx_complete(chan, cookie, last, used);
}
#define dma_async_memcpy_complete(chan, cookie, last, used)\
dma_async_is_tx_complete(chan, cookie, last, used)
/**
* dma_async_is_complete - test a cookie against chan state
* @cookie: transaction identifier to test status of
@@ -334,6 +407,7 @@ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie,
return DMA_IN_PROGRESS;
}
enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie);
/* --- DMA device --- */
@@ -362,5 +436,4 @@ dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov,
struct dma_pinned_list *pinned_list, struct page *page,
unsigned int offset, size_t len);
#endif /* CONFIG_DMA_ENGINE */
#endif /* DMAENGINE_H */

View File

@@ -479,6 +479,9 @@
#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361
#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252
#define PCI_VENDOR_ID_UNISYS 0x1018
#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C
#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */
#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005

View File

@@ -116,13 +116,46 @@
* attach a request to an active stripe (add_stripe_bh())
* lockdev attach-buffer unlockdev
* handle a stripe (handle_stripe())
* lockstripe clrSTRIPE_HANDLE ... (lockdev check-buffers unlockdev) .. change-state .. record io needed unlockstripe schedule io
* lockstripe clrSTRIPE_HANDLE ...
* (lockdev check-buffers unlockdev) ..
* change-state ..
* record io/ops needed unlockstripe schedule io/ops
* release an active stripe (release_stripe())
* lockdev if (!--cnt) { if STRIPE_HANDLE, add to handle_list else add to inactive-list } unlockdev
*
* The refcount counts each thread that have activated the stripe,
* plus raid5d if it is handling it, plus one for each active request
* on a cached buffer.
* on a cached buffer, and plus one if the stripe is undergoing stripe
* operations.
*
* Stripe operations are performed outside the stripe lock,
* the stripe operations are:
* -copying data between the stripe cache and user application buffers
* -computing blocks to save a disk access, or to recover a missing block
* -updating the parity on a write operation (reconstruct write and
* read-modify-write)
* -checking parity correctness
* -running i/o to disk
* These operations are carried out by raid5_run_ops which uses the async_tx
* api to (optionally) offload operations to dedicated hardware engines.
* When requesting an operation handle_stripe sets the pending bit for the
* operation and increments the count. raid5_run_ops is then run whenever
* the count is non-zero.
* There are some critical dependencies between the operations that prevent some
* from being requested while another is in flight.
* 1/ Parity check operations destroy the in cache version of the parity block,
* so we prevent parity dependent operations like writes and compute_blocks
* from starting while a check is in progress. Some dma engines can perform
* the check without damaging the parity block, in these cases the parity
* block is re-marked up to date (assuming the check was successful) and is
* not re-read from disk.
* 2/ When a write operation is requested we immediately lock the affected
* blocks, and mark them as not up to date. This causes new read requests
* to be held off, as well as parity checks and compute block operations.
* 3/ Once a compute block operation has been requested handle_stripe treats
* that block as if it is up to date. raid5_run_ops guaruntees that any
* operation that is dependent on the compute block result is initiated after
* the compute block completes.
*/
struct stripe_head {
@@ -136,15 +169,46 @@ struct stripe_head {
spinlock_t lock;
int bm_seq; /* sequence number for bitmap flushes */
int disks; /* disks in stripe */
/* stripe_operations
* @pending - pending ops flags (set for request->issue->complete)
* @ack - submitted ops flags (set for issue->complete)
* @complete - completed ops flags (set for complete)
* @target - STRIPE_OP_COMPUTE_BLK target
* @count - raid5_runs_ops is set to run when this is non-zero
*/
struct stripe_operations {
unsigned long pending;
unsigned long ack;
unsigned long complete;
int target;
int count;
u32 zero_sum_result;
} ops;
struct r5dev {
struct bio req;
struct bio_vec vec;
struct page *page;
struct bio *toread, *towrite, *written;
struct bio *toread, *read, *towrite, *written;
sector_t sector; /* sector of this page */
unsigned long flags;
} dev[1]; /* allocated with extra space depending of RAID geometry */
};
/* stripe_head_state - collects and tracks the dynamic state of a stripe_head
* for handle_stripe. It is only valid under spin_lock(sh->lock);
*/
struct stripe_head_state {
int syncing, expanding, expanded;
int locked, uptodate, to_read, to_write, failed, written;
int to_fill, compute, req_compute, non_overwrite;
int failed_num;
};
/* r6_state - extra state data only relevant to r6 */
struct r6_state {
int p_failed, q_failed, qd_idx, failed_num[2];
};
/* Flags */
#define R5_UPTODATE 0 /* page contains current data */
#define R5_LOCKED 1 /* IO has been submitted on "req" */
@@ -158,6 +222,15 @@ struct stripe_head {
#define R5_ReWrite 9 /* have tried to over-write the readerror */
#define R5_Expanded 10 /* This block now has post-expand data */
#define R5_Wantcompute 11 /* compute_block in progress treat as
* uptodate
*/
#define R5_Wantfill 12 /* dev->toread contains a bio that needs
* filling
*/
#define R5_Wantprexor 13 /* distinguish blocks ready for rmw from
* other "towrites"
*/
/*
* Write method
*/
@@ -179,6 +252,24 @@ struct stripe_head {
#define STRIPE_EXPANDING 9
#define STRIPE_EXPAND_SOURCE 10
#define STRIPE_EXPAND_READY 11
/*
* Operations flags (in issue order)
*/
#define STRIPE_OP_BIOFILL 0
#define STRIPE_OP_COMPUTE_BLK 1
#define STRIPE_OP_PREXOR 2
#define STRIPE_OP_BIODRAIN 3
#define STRIPE_OP_POSTXOR 4
#define STRIPE_OP_CHECK 5
#define STRIPE_OP_IO 6
/* modifiers to the base operations
* STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back
* STRIPE_OP_MOD_DMA_CHECK - parity is not corrupted by the check
*/
#define STRIPE_OP_MOD_REPAIR_PD 7
#define STRIPE_OP_MOD_DMA_CHECK 8
/*
* Plugging:
*

View File

@@ -3,9 +3,10 @@
#include <linux/raid/md.h>
#define MAX_XOR_BLOCKS 5
#define MAX_XOR_BLOCKS 4
extern void xor_block(unsigned int count, unsigned int bytes, void **ptr);
extern void xor_blocks(unsigned int count, unsigned int bytes,
void *dest, void **srcs);
struct xor_block_template {
struct xor_block_template *next;