tdma.c 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/*
 * Provide TDMA helper functions used by cipher and hash algorithm
 * implementations.
 *
 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
 * Author: Arnaud Ebalard <arno@natisbad.org>
 *
 * This work is based on an initial version written by
 * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "cesa.h"

bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *iter,
					struct mv_cesa_sg_dma_iter *sgiter,
					unsigned int len)
{
	if (!sgiter->sg)
		return false;

	sgiter->op_offset += len;
	sgiter->offset += len;
	if (sgiter->offset == sg_dma_len(sgiter->sg)) {
		if (sg_is_last(sgiter->sg))
			return false;
		sgiter->offset = 0;
		sgiter->sg = sg_next(sgiter->sg);
	}

	if (sgiter->op_offset == iter->op_len)
		return false;

	return true;
}

void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq)
{
	struct mv_cesa_engine *engine = dreq->base.engine;

44
	writel_relaxed(0, engine->regs + CESA_SA_CFG);
45 46

	mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
47 48 49 50 51 52 53 54 55
	writel_relaxed(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
		       CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
		       engine->regs + CESA_TDMA_CONTROL);

	writel_relaxed(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
		       CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
		       engine->regs + CESA_SA_CFG);
	writel_relaxed(dreq->chain.first->cur_dma,
		       engine->regs + CESA_TDMA_NEXT_ADDR);
56 57
	BUG_ON(readl(engine->regs + CESA_SA_CMD) &
	       CESA_SA_CMD_EN_CESA_SA_ACCL0);
58 59 60 61 62 63 64 65 66
	writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
}

void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq)
{
	struct mv_cesa_tdma_desc *tdma;

	for (tdma = dreq->chain.first; tdma;) {
		struct mv_cesa_tdma_desc *old_tdma = tdma;
67
		u32 type = tdma->flags & CESA_TDMA_TYPE_MSK;
68

69
		if (type == CESA_TDMA_OP)
70 71 72 73 74
			dma_pool_free(cesa_dev->dma->op_pool, tdma->op,
				      le32_to_cpu(tdma->src));

		tdma = tdma->next;
		dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
75
			      old_tdma->cur_dma);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	}

	dreq->chain.first = NULL;
	dreq->chain.last = NULL;
}

void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
			 struct mv_cesa_engine *engine)
{
	struct mv_cesa_tdma_desc *tdma;

	for (tdma = dreq->chain.first; tdma; tdma = tdma->next) {
		if (tdma->flags & CESA_TDMA_DST_IN_SRAM)
			tdma->dst = cpu_to_le32(tdma->dst + engine->sram_dma);

		if (tdma->flags & CESA_TDMA_SRC_IN_SRAM)
			tdma->src = cpu_to_le32(tdma->src + engine->sram_dma);

94
		if ((tdma->flags & CESA_TDMA_TYPE_MSK) == CESA_TDMA_OP)
95 96 97 98 99 100 101 102 103 104
			mv_cesa_adjust_op(engine, tdma->op);
	}
}

static struct mv_cesa_tdma_desc *
mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
{
	struct mv_cesa_tdma_desc *new_tdma = NULL;
	dma_addr_t dma_handle;

105 106
	new_tdma = dma_pool_zalloc(cesa_dev->dma->tdma_desc_pool, flags,
				   &dma_handle);
107 108 109
	if (!new_tdma)
		return ERR_PTR(-ENOMEM);

110
	new_tdma->cur_dma = dma_handle;
111
	if (chain->last) {
112
		chain->last->next_dma = cpu_to_le32(dma_handle);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		chain->last->next = new_tdma;
	} else {
		chain->first = new_tdma;
	}

	chain->last = new_tdma;

	return new_tdma;
}

struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
					const struct mv_cesa_op_ctx *op_templ,
					bool skip_ctx,
					gfp_t flags)
{
	struct mv_cesa_tdma_desc *tdma;
	struct mv_cesa_op_ctx *op;
	dma_addr_t dma_handle;
131
	unsigned int size;
132 133 134 135 136 137 138 139 140 141 142

	tdma = mv_cesa_dma_add_desc(chain, flags);
	if (IS_ERR(tdma))
		return ERR_CAST(tdma);

	op = dma_pool_alloc(cesa_dev->dma->op_pool, flags, &dma_handle);
	if (!op)
		return ERR_PTR(-ENOMEM);

	*op = *op_templ;

143 144
	size = skip_ctx ? sizeof(op->desc) : sizeof(*op);

145 146
	tdma = chain->last;
	tdma->op = op;
147
	tdma->byte_cnt = cpu_to_le32(size | BIT(31));
148
	tdma->src = cpu_to_le32(dma_handle);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;

	return op;
}

int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
				  dma_addr_t dst, dma_addr_t src, u32 size,
				  u32 flags, gfp_t gfp_flags)
{
	struct mv_cesa_tdma_desc *tdma;

	tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
	if (IS_ERR(tdma))
		return PTR_ERR(tdma);

164
	tdma->byte_cnt = cpu_to_le32(size | BIT(31));
165 166 167 168 169 170 171 172 173
	tdma->src = src;
	tdma->dst = dst;

	flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
	tdma->flags = flags | CESA_TDMA_DATA;

	return 0;
}

174
int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain, gfp_t flags)
175 176 177 178 179 180 181 182 183 184
{
	struct mv_cesa_tdma_desc *tdma;

	tdma = mv_cesa_dma_add_desc(chain, flags);
	if (IS_ERR(tdma))
		return PTR_ERR(tdma);

	return 0;
}

185
int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, gfp_t flags)
186 187 188 189 190 191 192
{
	struct mv_cesa_tdma_desc *tdma;

	tdma = mv_cesa_dma_add_desc(chain, flags);
	if (IS_ERR(tdma))
		return PTR_ERR(tdma);

193
	tdma->byte_cnt = cpu_to_le32(BIT(31));
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228

	return 0;
}

int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
				 struct mv_cesa_dma_iter *dma_iter,
				 struct mv_cesa_sg_dma_iter *sgiter,
				 gfp_t gfp_flags)
{
	u32 flags = sgiter->dir == DMA_TO_DEVICE ?
		    CESA_TDMA_DST_IN_SRAM : CESA_TDMA_SRC_IN_SRAM;
	unsigned int len;

	do {
		dma_addr_t dst, src;
		int ret;

		len = mv_cesa_req_dma_iter_transfer_len(dma_iter, sgiter);
		if (sgiter->dir == DMA_TO_DEVICE) {
			dst = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
			src = sg_dma_address(sgiter->sg) + sgiter->offset;
		} else {
			dst = sg_dma_address(sgiter->sg) + sgiter->offset;
			src = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
		}

		ret = mv_cesa_dma_add_data_transfer(chain, dst, src, len,
						    flags, gfp_flags);
		if (ret)
			return ret;

	} while (mv_cesa_req_dma_iter_next_transfer(dma_iter, sgiter, len));

	return 0;
}