On Wed, Jun 06, 2012 at 01:01:54AM +0200, Martin Mueller wrote:
>
> here is mv_cesa TDMA support for the kirkwood target. I only adapted
> the patches from Phil Sutter on the linux-crypto list and adjusted the
> kernel modules Makefile.
I had accidently deleted a line from package/kernel/modules/crypto.mk,
so here is the new version.
Index: target/linux/kirkwood/patches-3.3/300-mv_cesa-tdma.patch
===================================================================
--- target/linux/kirkwood/patches-3.3/300-mv_cesa-tdma.patch (revision 0)
+++ target/linux/kirkwood/patches-3.3/300-mv_cesa-tdma.patch (revision 0)
@@ -0,0 +1,1495 @@
+--- a/arch/arm/mach-kirkwood/common.c
++++ b/arch/arm/mach-kirkwood/common.c
+@@ -268,9 +268,42 @@ void __init kirkwood_uart1_init(void)
+ /*****************************************************************************
+ * Cryptographic Engines and Security Accelerator (CESA)
+ ****************************************************************************/
++static struct resource kirkwood_tdma_res[] = {
++ {
++ .name = "regs deco",
++ .start = CRYPTO_PHYS_BASE + 0xA00,
++ .end = CRYPTO_PHYS_BASE + 0xA24,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .name = "regs control and error",
++ .start = CRYPTO_PHYS_BASE + 0x800,
++ .end = CRYPTO_PHYS_BASE + 0x8CF,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .name = "crypto error",
++ .start = IRQ_KIRKWOOD_TDMA_ERR,
++ .end = IRQ_KIRKWOOD_TDMA_ERR,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static u64 mv_tdma_dma_mask = 0xffffffffUL;
++
++static struct platform_device kirkwood_tdma_device = {
++ .name = "mv_tdma",
++ .id = -1,
++ .dev = {
++ .dma_mask = &mv_tdma_dma_mask,
++ .coherent_dma_mask = 0xffffffff,
++ },
++ .num_resources = ARRAY_SIZE(kirkwood_tdma_res),
++ .resource = kirkwood_tdma_res,
++};
++
+ void __init kirkwood_crypto_init(void)
+ {
+ kirkwood_clk_ctrl |= CGC_CRYPTO;
++ platform_device_register(&kirkwood_tdma_device);
+ orion_crypto_init(CRYPTO_PHYS_BASE, KIRKWOOD_SRAM_PHYS_BASE,
+ KIRKWOOD_SRAM_SIZE, IRQ_KIRKWOOD_CRYPTO);
+ }
+--- a/arch/arm/mach-kirkwood/include/mach/irqs.h
++++ b/arch/arm/mach-kirkwood/include/mach/irqs.h
+@@ -51,6 +51,7 @@
+ #define IRQ_KIRKWOOD_GPIO_HIGH_16_23 41
+ #define IRQ_KIRKWOOD_GE00_ERR 46
+ #define IRQ_KIRKWOOD_GE01_ERR 47
++#define IRQ_KIRKWOOD_TDMA_ERR 49
+ #define IRQ_KIRKWOOD_RTC 53
+
+ /*
+--- a/arch/arm/plat-orion/common.c
++++ b/arch/arm/plat-orion/common.c
+@@ -911,9 +911,15 @@ static struct resource orion_crypto_reso
+ },
+ };
+
++static u64 mv_crypto_dmamask = DMA_BIT_MASK(32);
++
+ static struct platform_device orion_crypto = {
+ .name = "mv_crypto",
+ .id = -1,
++ .dev = {
++ .dma_mask = &mv_crypto_dmamask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
+ };
+
+ void __init orion_crypto_init(unsigned long mapbase,
+--- /dev/null
++++ b/drivers/crypto/dma_desclist.h
+@@ -0,0 +1,79 @@
++#ifndef __DMA_DESCLIST__
++#define __DMA_DESCLIST__
++
++struct dma_desc {
++ void *virt;
++ dma_addr_t phys;
++};
++
++struct dma_desclist {
++ struct dma_pool *itempool;
++ struct dma_desc *desclist;
++ unsigned long length;
++ unsigned long usage;
++};
++
++#define DESCLIST_ITEM(dl, x) ((dl).desclist[(x)].virt)
++#define DESCLIST_ITEM_DMA(dl, x) ((dl).desclist[(x)].phys)
++#define DESCLIST_FULL(dl) ((dl).length == (dl).usage)
++
++static inline int
++init_dma_desclist(struct dma_desclist *dl, struct device *dev,
++ size_t size, size_t align, size_t boundary)
++{
++#define STRX(x) #x
++#define STR(x) STRX(x)
++ dl->itempool = dma_pool_create(
++ "DMA Desclist Pool at "__FILE__"("STR(__LINE__)")",
++ dev, size, align, boundary);
++#undef STR
++#undef STRX
++ if (!dl->itempool)
++ return 1;
++ dl->desclist = NULL;
++ dl->length = dl->usage = 0;
++ return 0;
++}
++
++static inline int
++set_dma_desclist_size(struct dma_desclist *dl, unsigned long nelem)
++{
++ /* need to increase size first if requested */
++ if (nelem > dl->length) {
++ struct dma_desc *newmem;
++ int newsize = nelem * sizeof(struct dma_desc);
++
++ newmem = krealloc(dl->desclist, newsize, GFP_KERNEL);
++ if (!newmem)
++ return -ENOMEM;
++ dl->desclist = newmem;
++ }
++
++ /* allocate/free dma descriptors, adjusting dl->length on the go */
++ for (; dl->length < nelem; dl->length++) {
++ DESCLIST_ITEM(*dl, dl->length) = dma_pool_alloc(dl->itempool,
++ GFP_KERNEL, &DESCLIST_ITEM_DMA(*dl,
dl->length));
++ if (!DESCLIST_ITEM(*dl, dl->length))
++ return -ENOMEM;
++ }
++ for (; dl->length > nelem; dl->length--)
++ dma_pool_free(dl->itempool, DESCLIST_ITEM(*dl, dl->length - 1),
++ DESCLIST_ITEM_DMA(*dl, dl->length - 1));
++
++ /* ignore size decreases but those to zero */
++ if (!nelem) {
++ kfree(dl->desclist);
++ dl->desclist = 0;
++ }
++ return 0;
++}
++
++static inline void
++fini_dma_desclist(struct dma_desclist *dl)
++{
++ set_dma_desclist_size(dl, 0);
++ dma_pool_destroy(dl->itempool);
++ dl->length = dl->usage = 0;
++}
++
++#endif /* __DMA_DESCLIST__ */
+--- a/drivers/crypto/Kconfig
++++ b/drivers/crypto/Kconfig
+@@ -167,6 +167,10 @@ config CRYPTO_GHASH_S390
+
+ It is available as of z196.
+
++config CRYPTO_DEV_MV_TDMA
++ tristate
++ default no
++
+ config CRYPTO_DEV_MV_CESA
+ tristate "Marvell's Cryptographic Engine"
+ depends on PLAT_ORION
+@@ -175,6 +179,7 @@ config CRYPTO_DEV_MV_CESA
+ select CRYPTO_HASH2
+ select CRYPTO_BLKCIPHER2
+ select CRYPTO_HASH
++ select CRYPTO_DEV_MV_TDMA
+ help
+ This driver allows you to utilize the Cryptographic Engines and
+ Security Accelerator (CESA) which can be found on the Marvell Orion
+--- a/drivers/crypto/Makefile
++++ b/drivers/crypto/Makefile
+@@ -4,6 +4,7 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-
+ obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
+ n2_crypto-y := n2_core.o n2_asm.o
+ obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
++obj-$(CONFIG_CRYPTO_DEV_MV_TDMA) += mv_tdma.o
+ obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
+ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
+ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
+--- a/drivers/crypto/mv_cesa.c
++++ b/drivers/crypto/mv_cesa.c
+@@ -9,6 +9,8 @@
+ #include <crypto/aes.h>
+ #include <crypto/algapi.h>
+ #include <linux/crypto.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmapool.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/kthread.h>
+@@ -20,9 +22,17 @@
+ #include <crypto/sha.h>
+
+ #include "mv_cesa.h"
++#include "mv_tdma.h"
++#include "dma_desclist.h"
+
+ #define MV_CESA "MV-CESA:"
+ #define MAX_HW_HASH_SIZE 0xFFFF
++#define MV_CESA_EXPIRE 500 /* msec */
++
++#define MV_DMA_INIT_POOLSIZE 16
++#define MV_DMA_ALIGN 16
++
++static int count_sgs(struct scatterlist *, unsigned int);
+
+ /*
+ * STM:
+@@ -42,13 +52,12 @@ enum engine_status {
+
+ /**
+ * struct req_progress - used for every crypt request
+- * @src_sg_it: sg iterator for src
+- * @dst_sg_it: sg iterator for dst
++ * @src_sg: sg list for src
++ * @dst_sg: sg list for dst
+ * @sg_src_left: bytes left in src to process (scatter list)
+ * @src_start: offset to add to src start position (scatter
list)
+ * @crypt_len: length of current hw crypt/hash process
+ * @hw_nbytes: total bytes to process in hw for this request
+- * @copy_back: whether to copy data back (crypt) or not (hash)
+ * @sg_dst_left: bytes left dst to process in this scatter list
+ * @dst_start: offset to add to dst start position (scatter
list)
+ * @hw_processed_bytes: number of bytes processed by hw (request).
+@@ -58,10 +67,9 @@ enum engine_status {
+ * track of progress within current scatterlist.
+ */
+ struct req_progress {
+- struct sg_mapping_iter src_sg_it;
+- struct sg_mapping_iter dst_sg_it;
++ struct scatterlist *src_sg;
++ struct scatterlist *dst_sg;
+ void (*complete) (void);
+- void (*process) (int is_first);
+
+ /* src mostly */
+ int sg_src_left;
+@@ -69,15 +77,34 @@ struct req_progress {
+ int crypt_len;
+ int hw_nbytes;
+ /* dst mostly */
+- int copy_back;
+ int sg_dst_left;
+ int dst_start;
+ int hw_processed_bytes;
+ };
+
++struct sec_accel_sram {
++ struct sec_accel_config op;
++ union {
++ struct {
++ u32 key[8];
++ u32 iv[4];
++ } crypt;
++ struct {
++ u32 ivi[5];
++ u32 ivo[5];
++ } hash;
++ } type;
++#define sa_key type.crypt.key
++#define sa_iv type.crypt.iv
++#define sa_ivi type.hash.ivi
++#define sa_ivo type.hash.ivo
++} __attribute__((packed));
++
+ struct crypto_priv {
++ struct device *dev;
+ void __iomem *reg;
+ void __iomem *sram;
++ u32 sram_phys;
+ int irq;
+ struct task_struct *queue_th;
+
+@@ -85,16 +112,25 @@ struct crypto_priv {
+ spinlock_t lock;
+ struct crypto_queue queue;
+ enum engine_status eng_st;
++ struct timer_list completion_timer;
+ struct crypto_async_request *cur_req;
+ struct req_progress p;
+ int max_req_size;
+ int sram_size;
+ int has_sha1;
+ int has_hmac_sha1;
++
++ struct sec_accel_sram sa_sram;
++ dma_addr_t sa_sram_dma;
++
++ struct dma_desclist desclist;
+ };
+
+ static struct crypto_priv *cpg;
+
++#define ITEM(x) ((u32 *)DESCLIST_ITEM(cpg->desclist, x))
++#define ITEM_DMA(x) DESCLIST_ITEM_DMA(cpg->desclist, x)
++
+ struct mv_ctx {
+ u8 aes_enc_key[AES_KEY_LEN];
+ u32 aes_dec_key[8];
+@@ -129,13 +165,69 @@ struct mv_req_hash_ctx {
+ u64 count;
+ u32 state[SHA1_DIGEST_SIZE / 4];
+ u8 buffer[SHA1_BLOCK_SIZE];
++ dma_addr_t buffer_dma;
+ int first_hash; /* marks that we don't have previous state */
+ int last_chunk; /* marks that this is the 'final' request */
+ int extra_bytes; /* unprocessed bytes in buffer */
++ int digestsize; /* size of the digest */
+ enum hash_op op;
+ int count_add;
++ dma_addr_t result_dma;
+ };
+
++static void mv_completion_timer_callback(unsigned long unused)
++{
++ int active = readl(cpg->reg + SEC_ACCEL_CMD) & SEC_CMD_EN_SEC_ACCL0;
++
++ printk(KERN_ERR MV_CESA
++ "completion timer expired (CESA %sactive), cleaning up.\n",
++ active ? "" : "in");
++
++ del_timer(&cpg->completion_timer);
++ writel(SEC_CMD_DISABLE_SEC, cpg->reg + SEC_ACCEL_CMD);
++ while(readl(cpg->reg + SEC_ACCEL_CMD) & SEC_CMD_DISABLE_SEC)
++ printk(KERN_INFO MV_CESA "%s: waiting for engine finishing\n",
__func__);
++ cpg->eng_st = ENGINE_W_DEQUEUE;
++ wake_up_process(cpg->queue_th);
++}
++
++static void mv_setup_timer(void)
++{
++ setup_timer(&cpg->completion_timer, &mv_completion_timer_callback, 0);
++ mod_timer(&cpg->completion_timer,
++ jiffies + msecs_to_jiffies(MV_CESA_EXPIRE));
++}
++
++static inline void mv_tdma_u32_copy(dma_addr_t dst, u32 val)
++{
++ if (unlikely(DESCLIST_FULL(cpg->desclist)) &&
++ set_dma_desclist_size(&cpg->desclist, cpg->desclist.length << 1)) {
++ printk(KERN_ERR MV_CESA "resizing poolsize to %lu failed\n",
++ cpg->desclist.length << 1);
++ return;
++ }
++ *ITEM(cpg->desclist.usage) = val;
++ mv_tdma_memcpy(dst, ITEM_DMA(cpg->desclist.usage), sizeof(u32));
++ cpg->desclist.usage++;
++}
++
++static inline bool
++mv_dma_map_sg(struct scatterlist *sg, int nbytes, enum dma_data_direction dir)
++{
++ int nents = count_sgs(sg, nbytes);
++
++ if (nbytes && dma_map_sg(cpg->dev, sg, nents, dir) != nents)
++ return false;
++ return true;
++}
++
++static inline void
++mv_dma_unmap_sg(struct scatterlist *sg, int nbytes, enum dma_data_direction
dir)
++{
++ if (nbytes)
++ dma_unmap_sg(cpg->dev, sg, count_sgs(sg, nbytes), dir);
++}
++
+ static void compute_aes_dec_key(struct mv_ctx *ctx)
+ {
+ struct crypto_aes_ctx gen_aes_key;
+@@ -185,19 +277,19 @@ static int mv_setkey_aes(struct crypto_a
+
+ static void copy_src_to_buf(struct req_progress *p, char *dbuf, int len)
+ {
+- int ret;
+ void *sbuf;
+ int copy_len;
+
+ while (len) {
+ if (!p->sg_src_left) {
+- ret = sg_miter_next(&p->src_sg_it);
+- BUG_ON(!ret);
+- p->sg_src_left = p->src_sg_it.length;
++ /* next sg please */
++ p->src_sg = sg_next(p->src_sg);
++ BUG_ON(!p->src_sg);
++ p->sg_src_left = p->src_sg->length;
+ p->src_start = 0;
+ }
+
+- sbuf = p->src_sg_it.addr + p->src_start;
++ sbuf = sg_virt(p->src_sg) + p->src_start;
+
+ copy_len = min(p->sg_src_left, len);
+ memcpy(dbuf, sbuf, copy_len);
+@@ -210,73 +302,123 @@ static void copy_src_to_buf(struct req_p
+ }
+ }
+
++static void dma_copy_src_to_buf(struct req_progress *p, dma_addr_t dbuf, int
len)
++{
++ dma_addr_t sbuf;
++ int copy_len;
++
++ while (len) {
++ if (!p->sg_src_left) {
++ /* next sg please */
++ p->src_sg = sg_next(p->src_sg);
++ BUG_ON(!p->src_sg);
++ p->sg_src_left = sg_dma_len(p->src_sg);
++ p->src_start = 0;
++ }
++
++ sbuf = sg_dma_address(p->src_sg) + p->src_start;
++
++ copy_len = min(p->sg_src_left, len);
++ mv_tdma_memcpy(dbuf, sbuf, copy_len);
++
++ p->src_start += copy_len;
++ p->sg_src_left -= copy_len;
++
++ len -= copy_len;
++ dbuf += copy_len;
++ }
++}
++
++static void dma_copy_buf_to_dst(struct req_progress *p, dma_addr_t sbuf, int
len)
++{
++ dma_addr_t dbuf;
++ int copy_len;
++
++ while (len) {
++ if (!p->sg_dst_left) {
++ /* next sg please */
++ p->dst_sg = sg_next(p->dst_sg);
++ BUG_ON(!p->dst_sg);
++ p->sg_dst_left = sg_dma_len(p->dst_sg);
++ p->dst_start = 0;
++ }
++
++ dbuf = sg_dma_address(p->dst_sg) + p->dst_start;
++
++ copy_len = min(p->sg_dst_left, len);
++ mv_tdma_memcpy(dbuf, sbuf, copy_len);
++
++ p->dst_start += copy_len;
++ p->sg_dst_left -= copy_len;
++
++ len -= copy_len;
++ sbuf += copy_len;
++ }
++}
++
+ static void setup_data_in(void)
+ {
+ struct req_progress *p = &cpg->p;
+ int data_in_sram =
+ min(p->hw_nbytes - p->hw_processed_bytes, cpg->max_req_size);
+- copy_src_to_buf(p, cpg->sram + SRAM_DATA_IN_START + p->crypt_len,
++ dma_copy_src_to_buf(p, cpg->sram_phys + SRAM_DATA_IN_START +
p->crypt_len,
+ data_in_sram - p->crypt_len);
+ p->crypt_len = data_in_sram;
+ }
+
+-static void mv_process_current_q(int first_block)
++static void mv_init_crypt_config(struct ablkcipher_request *req)
+ {
+- struct ablkcipher_request *req = ablkcipher_request_cast(cpg->cur_req);
+ struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
+- struct sec_accel_config op;
++ struct sec_accel_config *op = &cpg->sa_sram.op;
+
+ switch (req_ctx->op) {
+ case COP_AES_ECB:
+- op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_ECB;
++ op->config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES |
CFG_ENC_MODE_ECB;
+ break;
+ case COP_AES_CBC:
+ default:
+- op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_CBC;
+- op.enc_iv = ENC_IV_POINT(SRAM_DATA_IV) |
++ op->config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES |
CFG_ENC_MODE_CBC;
++ op->enc_iv = ENC_IV_POINT(SRAM_DATA_IV) |
+ ENC_IV_BUF_POINT(SRAM_DATA_IV_BUF);
+- if (first_block)
+- memcpy(cpg->sram + SRAM_DATA_IV, req->info, 16);
++ memcpy(cpg->sa_sram.sa_iv, req->info, 16);
+ break;
+ }
+ if (req_ctx->decrypt) {
+- op.config |= CFG_DIR_DEC;
+- memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_dec_key,
+- AES_KEY_LEN);
++ op->config |= CFG_DIR_DEC;
++ memcpy(cpg->sa_sram.sa_key, ctx->aes_dec_key, AES_KEY_LEN);
+ } else {
+- op.config |= CFG_DIR_ENC;
+- memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_enc_key,
+- AES_KEY_LEN);
++ op->config |= CFG_DIR_ENC;
++ memcpy(cpg->sa_sram.sa_key, ctx->aes_enc_key, AES_KEY_LEN);
+ }
+
+ switch (ctx->key_len) {
+ case AES_KEYSIZE_128:
+- op.config |= CFG_AES_LEN_128;
++ op->config |= CFG_AES_LEN_128;
+ break;
+ case AES_KEYSIZE_192:
+- op.config |= CFG_AES_LEN_192;
++ op->config |= CFG_AES_LEN_192;
+ break;
+ case AES_KEYSIZE_256:
+- op.config |= CFG_AES_LEN_256;
++ op->config |= CFG_AES_LEN_256;
+ break;
+ }
+- op.enc_p = ENC_P_SRC(SRAM_DATA_IN_START) |
++ op->enc_p = ENC_P_SRC(SRAM_DATA_IN_START) |
+ ENC_P_DST(SRAM_DATA_OUT_START);
+- op.enc_key_p = SRAM_DATA_KEY_P;
+-
+- setup_data_in();
+- op.enc_len = cpg->p.crypt_len;
+- memcpy(cpg->sram + SRAM_CONFIG, &op,
+- sizeof(struct sec_accel_config));
++ op->enc_key_p = SRAM_DATA_KEY_P;
++ op->enc_len = cpg->p.crypt_len;
+
+- /* GO */
+- writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
++ dma_sync_single_for_device(cpg->dev, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram), DMA_TO_DEVICE);
++ mv_tdma_memcpy(cpg->sram_phys + SRAM_CONFIG, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram));
++}
+
+- /*
+- * XXX: add timer if the interrupt does not occur for some mystery
+- * reason
+- */
++static void mv_update_crypt_config(void)
++{
++ /* update the enc_len field only */
++ mv_tdma_u32_copy(cpg->sram_phys + SRAM_CONFIG + 2 * sizeof(u32),
++ (u32)cpg->p.crypt_len);
+ }
+
+ static void mv_crypto_algo_completion(void)
+@@ -284,8 +426,12 @@ static void mv_crypto_algo_completion(vo
+ struct ablkcipher_request *req = ablkcipher_request_cast(cpg->cur_req);
+ struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
+
+- sg_miter_stop(&cpg->p.src_sg_it);
+- sg_miter_stop(&cpg->p.dst_sg_it);
++ if (req->src == req->dst) {
++ mv_dma_unmap_sg(req->src, req->nbytes, DMA_BIDIRECTIONAL);
++ } else {
++ mv_dma_unmap_sg(req->src, req->nbytes, DMA_TO_DEVICE);
++ mv_dma_unmap_sg(req->dst, req->nbytes, DMA_FROM_DEVICE);
++ }
+
+ if (req_ctx->op != COP_AES_CBC)
+ return ;
+@@ -293,37 +439,33 @@ static void mv_crypto_algo_completion(vo
+ memcpy(req->info, cpg->sram + SRAM_DATA_IV_BUF, 16);
+ }
+
+-static void mv_process_hash_current(int first_block)
++static void mv_init_hash_config(struct ahash_request *req)
+ {
+- struct ahash_request *req = ahash_request_cast(cpg->cur_req);
+ const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm);
+ struct mv_req_hash_ctx *req_ctx = ahash_request_ctx(req);
+ struct req_progress *p = &cpg->p;
+- struct sec_accel_config op = { 0 };
++ struct sec_accel_config *op = &cpg->sa_sram.op;
+ int is_last;
+
+ switch (req_ctx->op) {
+ case COP_SHA1:
+ default:
+- op.config = CFG_OP_MAC_ONLY | CFG_MACM_SHA1;
++ op->config = CFG_OP_MAC_ONLY | CFG_MACM_SHA1;
+ break;
+ case COP_HMAC_SHA1:
+- op.config = CFG_OP_MAC_ONLY | CFG_MACM_HMAC_SHA1;
+- memcpy(cpg->sram + SRAM_HMAC_IV_IN,
++ op->config = CFG_OP_MAC_ONLY | CFG_MACM_HMAC_SHA1;
++ memcpy(cpg->sa_sram.sa_ivi,
+ tfm_ctx->ivs, sizeof(tfm_ctx->ivs));
+ break;
+ }
+
+- op.mac_src_p =
+- MAC_SRC_DATA_P(SRAM_DATA_IN_START) | MAC_SRC_TOTAL_LEN((u32)
+- req_ctx->
+- count);
+-
+- setup_data_in();
++ op->mac_src_p =
++ MAC_SRC_DATA_P(SRAM_DATA_IN_START) |
++ MAC_SRC_TOTAL_LEN((u32)req_ctx->count);
+
+- op.mac_digest =
++ op->mac_digest =
+ MAC_DIGEST_P(SRAM_DIGEST_BUF) | MAC_FRAG_LEN(p->crypt_len);
+- op.mac_iv =
++ op->mac_iv =
+ MAC_INNER_IV_P(SRAM_HMAC_IV_IN) |
+ MAC_OUTER_IV_P(SRAM_HMAC_IV_OUT);
+
+@@ -332,35 +474,59 @@ static void mv_process_hash_current(int
+ && (req_ctx->count <= MAX_HW_HASH_SIZE);
+ if (req_ctx->first_hash) {
+ if (is_last)
+- op.config |= CFG_NOT_FRAG;
++ op->config |= CFG_NOT_FRAG;
+ else
+- op.config |= CFG_FIRST_FRAG;
++ op->config |= CFG_FIRST_FRAG;
+
+ req_ctx->first_hash = 0;
+ } else {
+ if (is_last)
+- op.config |= CFG_LAST_FRAG;
++ op->config |= CFG_LAST_FRAG;
+ else
+- op.config |= CFG_MID_FRAG;
++ op->config |= CFG_MID_FRAG;
+
+- if (first_block) {
+- writel(req_ctx->state[0], cpg->reg +
DIGEST_INITIAL_VAL_A);
+- writel(req_ctx->state[1], cpg->reg +
DIGEST_INITIAL_VAL_B);
+- writel(req_ctx->state[2], cpg->reg +
DIGEST_INITIAL_VAL_C);
+- writel(req_ctx->state[3], cpg->reg +
DIGEST_INITIAL_VAL_D);
+- writel(req_ctx->state[4], cpg->reg +
DIGEST_INITIAL_VAL_E);
+- }
++ writel(req_ctx->state[0], cpg->reg + DIGEST_INITIAL_VAL_A);
++ writel(req_ctx->state[1], cpg->reg + DIGEST_INITIAL_VAL_B);
++ writel(req_ctx->state[2], cpg->reg + DIGEST_INITIAL_VAL_C);
++ writel(req_ctx->state[3], cpg->reg + DIGEST_INITIAL_VAL_D);
++ writel(req_ctx->state[4], cpg->reg + DIGEST_INITIAL_VAL_E);
+ }
+
+- memcpy(cpg->sram + SRAM_CONFIG, &op, sizeof(struct sec_accel_config));
++ dma_sync_single_for_device(cpg->dev, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram), DMA_TO_DEVICE);
++ mv_tdma_memcpy(cpg->sram_phys + SRAM_CONFIG, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram));
++}
+
+- /* GO */
+- writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
++static void mv_update_hash_config(struct ahash_request *req)
++{
++ struct mv_req_hash_ctx *req_ctx = ahash_request_ctx(req);
++ struct req_progress *p = &cpg->p;
++ int is_last;
++ u32 val;
++
++ /* update only the config (for changed fragment state) and
++ * mac_digest (for changed frag len) fields */
+
+- /*
+- * XXX: add timer if the interrupt does not occur for some mystery
+- * reason
+- */
++ switch (req_ctx->op) {
++ case COP_SHA1:
++ default:
++ val = CFG_OP_MAC_ONLY | CFG_MACM_SHA1;
++ break;
++ case COP_HMAC_SHA1:
++ val = CFG_OP_MAC_ONLY | CFG_MACM_HMAC_SHA1;
++ break;
++ }
++
++ is_last = req_ctx->last_chunk
++ && (p->hw_processed_bytes + p->crypt_len >= p->hw_nbytes)
++ && (req_ctx->count <= MAX_HW_HASH_SIZE);
++
++ val |= is_last ? CFG_LAST_FRAG : CFG_MID_FRAG;
++ mv_tdma_u32_copy(cpg->sram_phys + SRAM_CONFIG, val);
++
++ val = MAC_DIGEST_P(SRAM_DIGEST_BUF) | MAC_FRAG_LEN(p->crypt_len);
++ mv_tdma_u32_copy(cpg->sram_phys + SRAM_CONFIG + 6 * sizeof(u32), val);
+ }
+
+ static inline int mv_hash_import_sha1_ctx(const struct mv_req_hash_ctx *ctx,
+@@ -404,6 +570,15 @@ out:
+ return rc;
+ }
+
++static void mv_save_digest_state(struct mv_req_hash_ctx *ctx)
++{
++ ctx->state[0] = readl(cpg->reg + DIGEST_INITIAL_VAL_A);
++ ctx->state[1] = readl(cpg->reg + DIGEST_INITIAL_VAL_B);
++ ctx->state[2] = readl(cpg->reg + DIGEST_INITIAL_VAL_C);
++ ctx->state[3] = readl(cpg->reg + DIGEST_INITIAL_VAL_D);
++ ctx->state[4] = readl(cpg->reg + DIGEST_INITIAL_VAL_E);
++}
++
+ static void mv_hash_algo_completion(void)
+ {
+ struct ahash_request *req = ahash_request_cast(cpg->cur_req);
+@@ -411,72 +586,39 @@ static void mv_hash_algo_completion(void
+
+ if (ctx->extra_bytes)
+ copy_src_to_buf(&cpg->p, ctx->buffer, ctx->extra_bytes);
+- sg_miter_stop(&cpg->p.src_sg_it);
+
+ if (likely(ctx->last_chunk)) {
+- if (likely(ctx->count <= MAX_HW_HASH_SIZE)) {
+- memcpy(req->result, cpg->sram + SRAM_DIGEST_BUF,
+- crypto_ahash_digestsize(crypto_ahash_reqtfm
+- (req)));
+- } else
++ dma_unmap_single(cpg->dev, ctx->result_dma,
++ ctx->digestsize, DMA_FROM_DEVICE);
++
++ dma_unmap_single(cpg->dev, ctx->buffer_dma,
++ SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
++
++ if (unlikely(ctx->count > MAX_HW_HASH_SIZE)) {
++ mv_save_digest_state(ctx);
+ mv_hash_final_fallback(req);
++ }
+ } else {
+- ctx->state[0] = readl(cpg->reg + DIGEST_INITIAL_VAL_A);
+- ctx->state[1] = readl(cpg->reg + DIGEST_INITIAL_VAL_B);
+- ctx->state[2] = readl(cpg->reg + DIGEST_INITIAL_VAL_C);
+- ctx->state[3] = readl(cpg->reg + DIGEST_INITIAL_VAL_D);
+- ctx->state[4] = readl(cpg->reg + DIGEST_INITIAL_VAL_E);
++ mv_save_digest_state(ctx);
+ }
++
++ mv_dma_unmap_sg(req->src, req->nbytes, DMA_TO_DEVICE);
+ }
+
+ static void dequeue_complete_req(void)
+ {
+ struct crypto_async_request *req = cpg->cur_req;
+- void *buf;
+- int ret;
+- cpg->p.hw_processed_bytes += cpg->p.crypt_len;
+- if (cpg->p.copy_back) {
+- int need_copy_len = cpg->p.crypt_len;
+- int sram_offset = 0;
+- do {
+- int dst_copy;
+-
+- if (!cpg->p.sg_dst_left) {
+- ret = sg_miter_next(&cpg->p.dst_sg_it);
+- BUG_ON(!ret);
+- cpg->p.sg_dst_left = cpg->p.dst_sg_it.length;
+- cpg->p.dst_start = 0;
+- }
+-
+- buf = cpg->p.dst_sg_it.addr;
+- buf += cpg->p.dst_start;
+-
+- dst_copy = min(need_copy_len, cpg->p.sg_dst_left);
+
+- memcpy(buf,
+- cpg->sram + SRAM_DATA_OUT_START + sram_offset,
+- dst_copy);
+- sram_offset += dst_copy;
+- cpg->p.sg_dst_left -= dst_copy;
+- need_copy_len -= dst_copy;
+- cpg->p.dst_start += dst_copy;
+- } while (need_copy_len > 0);
+- }
+-
+- cpg->p.crypt_len = 0;
++ mv_tdma_clear();
++ cpg->desclist.usage = 0;
+
+ BUG_ON(cpg->eng_st != ENGINE_W_DEQUEUE);
+- if (cpg->p.hw_processed_bytes < cpg->p.hw_nbytes) {
+- /* process next scatter list entry */
+- cpg->eng_st = ENGINE_BUSY;
+- cpg->p.process(0);
+- } else {
+- cpg->p.complete();
+- cpg->eng_st = ENGINE_IDLE;
+- local_bh_disable();
+- req->complete(req, 0);
+- local_bh_enable();
+- }
++
++ cpg->p.complete();
++ cpg->eng_st = ENGINE_IDLE;
++ local_bh_disable();
++ req->complete(req, 0);
++ local_bh_enable();
+ }
+
+ static int count_sgs(struct scatterlist *sl, unsigned int total_bytes)
+@@ -499,33 +641,68 @@ static int count_sgs(struct scatterlist
+ static void mv_start_new_crypt_req(struct ablkcipher_request *req)
+ {
+ struct req_progress *p = &cpg->p;
+- int num_sgs;
+
+ cpg->cur_req = &req->base;
+ memset(p, 0, sizeof(struct req_progress));
+ p->hw_nbytes = req->nbytes;
+ p->complete = mv_crypto_algo_completion;
+- p->process = mv_process_current_q;
+- p->copy_back = 1;
+
+- num_sgs = count_sgs(req->src, req->nbytes);
+- sg_miter_start(&p->src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG);
++ /* assume inplace request */
++ if (req->src == req->dst) {
++ if (!mv_dma_map_sg(req->src, req->nbytes, DMA_BIDIRECTIONAL))
++ return;
++ } else {
++ if (!mv_dma_map_sg(req->src, req->nbytes, DMA_TO_DEVICE))
++ return;
++
++ if (!mv_dma_map_sg(req->dst, req->nbytes, DMA_FROM_DEVICE)) {
++ mv_dma_unmap_sg(req->src, req->nbytes, DMA_TO_DEVICE);
++ return;
++ }
++ }
++
++ p->src_sg = req->src;
++ p->dst_sg = req->dst;
++ if (req->nbytes) {
++ BUG_ON(!req->src);
++ BUG_ON(!req->dst);
++ p->sg_src_left = sg_dma_len(req->src);
++ p->sg_dst_left = sg_dma_len(req->dst);
++ }
++
++ setup_data_in();
++ mv_init_crypt_config(req);
++ mv_tdma_separator();
++ dma_copy_buf_to_dst(&cpg->p, cpg->sram_phys + SRAM_DATA_OUT_START,
cpg->p.crypt_len);
++ cpg->p.hw_processed_bytes += cpg->p.crypt_len;
++ while (cpg->p.hw_processed_bytes < cpg->p.hw_nbytes) {
++ cpg->p.crypt_len = 0;
++
++ setup_data_in();
++ mv_update_crypt_config();
++ mv_tdma_separator();
++ dma_copy_buf_to_dst(&cpg->p, cpg->sram_phys +
SRAM_DATA_OUT_START, cpg->p.crypt_len);
++ cpg->p.hw_processed_bytes += cpg->p.crypt_len;
++ }
+
+- num_sgs = count_sgs(req->dst, req->nbytes);
+- sg_miter_start(&p->dst_sg_it, req->dst, num_sgs, SG_MITER_TO_SG);
+
+- mv_process_current_q(1);
++ /* GO */
++ mv_setup_timer();
++ mv_tdma_trigger();
++ writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
+ }
+
+ static void mv_start_new_hash_req(struct ahash_request *req)
+ {
+ struct req_progress *p = &cpg->p;
+ struct mv_req_hash_ctx *ctx = ahash_request_ctx(req);
+- int num_sgs, hw_bytes, old_extra_bytes, rc;
++ int hw_bytes, old_extra_bytes, rc;
++
+ cpg->cur_req = &req->base;
+ memset(p, 0, sizeof(struct req_progress));
+ hw_bytes = req->nbytes + ctx->extra_bytes;
+ old_extra_bytes = ctx->extra_bytes;
++ ctx->digestsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
+
+ ctx->extra_bytes = hw_bytes % SHA1_BLOCK_SIZE;
+ if (ctx->extra_bytes != 0
+@@ -534,25 +711,13 @@ static void mv_start_new_hash_req(struct
+ else
+ ctx->extra_bytes = 0;
+
+- num_sgs = count_sgs(req->src, req->nbytes);
+- sg_miter_start(&p->src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG);
+-
+- if (hw_bytes) {
+- p->hw_nbytes = hw_bytes;
+- p->complete = mv_hash_algo_completion;
+- p->process = mv_process_hash_current;
+-
+- if (unlikely(old_extra_bytes)) {
+- memcpy(cpg->sram + SRAM_DATA_IN_START, ctx->buffer,
+- old_extra_bytes);
+- p->crypt_len = old_extra_bytes;
++ if (unlikely(!hw_bytes)) { /* too little data for CESA */
++ if (req->nbytes) {
++ p->src_sg = req->src;
++ p->sg_src_left = req->src->length;
++ copy_src_to_buf(p, ctx->buffer + old_extra_bytes,
++ req->nbytes);
+ }
+-
+- mv_process_hash_current(1);
+- } else {
+- copy_src_to_buf(p, ctx->buffer + old_extra_bytes,
+- ctx->extra_bytes - old_extra_bytes);
+- sg_miter_stop(&p->src_sg_it);
+ if (ctx->last_chunk)
+ rc = mv_hash_final_fallback(req);
+ else
+@@ -561,7 +726,60 @@ static void mv_start_new_hash_req(struct
+ local_bh_disable();
+ req->base.complete(&req->base, rc);
+ local_bh_enable();
++ return;
++ }
++
++ if (likely(req->nbytes)) {
++ BUG_ON(!req->src);
++
++ if (!mv_dma_map_sg(req->src, req->nbytes, DMA_TO_DEVICE)) {
++ printk(KERN_ERR "%s: out of memory\n", __func__);
++ return;
++ }
++ p->sg_src_left = sg_dma_len(req->src);
++ p->src_sg = req->src;
++ }
++
++ p->hw_nbytes = hw_bytes;
++ p->complete = mv_hash_algo_completion;
++
++ if (unlikely(old_extra_bytes)) {
++ dma_sync_single_for_device(cpg->dev, ctx->buffer_dma,
++ SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
++ mv_tdma_memcpy(cpg->sram_phys + SRAM_DATA_IN_START,
++ ctx->buffer_dma, old_extra_bytes);
++ p->crypt_len = old_extra_bytes;
++ }
++
++ setup_data_in();
++ mv_init_hash_config(req);
++ mv_tdma_separator();
++ cpg->p.hw_processed_bytes += cpg->p.crypt_len;
++ while (cpg->p.hw_processed_bytes < cpg->p.hw_nbytes) {
++ cpg->p.crypt_len = 0;
++
++ setup_data_in();
++ mv_update_hash_config(req);
++ mv_tdma_separator();
++ cpg->p.hw_processed_bytes += cpg->p.crypt_len;
++ }
++ if (req->result) {
++ ctx->result_dma = dma_map_single(cpg->dev, req->result,
++ ctx->digestsize, DMA_FROM_DEVICE);
++ mv_tdma_memcpy(ctx->result_dma,
++ cpg->sram_phys + SRAM_DIGEST_BUF,
++ ctx->digestsize);
++ } else {
++ /* XXX: this fixes some ugly register fuckup bug in the tdma
engine
++ * (no need to sync since the data is ignored anyway) */
++ mv_tdma_memcpy(cpg->sa_sram_dma,
++ cpg->sram_phys + SRAM_CONFIG, 1);
+ }
++
++ /* GO */
++ mv_setup_timer();
++ mv_tdma_trigger();
++ writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
+ }
+
+ static int queue_manag(void *data)
+@@ -684,6 +902,8 @@ static void mv_init_hash_req_ctx(struct
+ ctx->first_hash = 1;
+ ctx->last_chunk = is_last;
+ ctx->count_add = count_add;
++ ctx->buffer_dma = dma_map_single(cpg->dev, ctx->buffer,
++ SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
+ }
+
+ static void mv_update_hash_req_ctx(struct mv_req_hash_ctx *ctx, int is_last,
+@@ -883,11 +1103,14 @@ irqreturn_t crypto_int(int irq, void *pr
+ u32 val;
+
+ val = readl(cpg->reg + SEC_ACCEL_INT_STATUS);
+- if (!(val & SEC_INT_ACCEL0_DONE))
++ if (!(val & SEC_INT_ACC0_IDMA_DONE))
+ return IRQ_NONE;
+
+- val &= ~SEC_INT_ACCEL0_DONE;
+- writel(val, cpg->reg + FPGA_INT_STATUS);
++ if (!del_timer(&cpg->completion_timer)) {
++ printk(KERN_WARNING MV_CESA
++ "got an interrupt but no pending timer?\n");
++ }
++ val &= ~SEC_INT_ACC0_IDMA_DONE;
+ writel(val, cpg->reg + SEC_ACCEL_INT_STATUS);
+ BUG_ON(cpg->eng_st != ENGINE_BUSY);
+ cpg->eng_st = ENGINE_W_DEQUEUE;
+@@ -1022,6 +1245,7 @@ static int mv_probe(struct platform_devi
+ }
+ cp->sram_size = resource_size(res);
+ cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE;
++ cp->sram_phys = res->start;
+ cp->sram = ioremap(res->start, cp->sram_size);
+ if (!cp->sram) {
+ ret = -ENOMEM;
+@@ -1037,6 +1261,7 @@ static int mv_probe(struct platform_devi
+
+ platform_set_drvdata(pdev, cp);
+ cpg = cp;
++ cpg->dev = &pdev->dev;
+
+ cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto");
+ if (IS_ERR(cp->queue_th)) {
+@@ -1049,15 +1274,30 @@ static int mv_probe(struct platform_devi
+ if (ret)
+ goto err_thread;
+
+- writel(SEC_INT_ACCEL0_DONE, cpg->reg + SEC_ACCEL_INT_MASK);
+- writel(SEC_CFG_STOP_DIG_ERR, cpg->reg + SEC_ACCEL_CFG);
++ writel(0, cpg->reg + SEC_ACCEL_INT_STATUS);
++ writel(SEC_INT_ACC0_IDMA_DONE, cpg->reg + SEC_ACCEL_INT_MASK);
++ writel((SEC_CFG_STOP_DIG_ERR | SEC_CFG_CH0_W_IDMA | SEC_CFG_MP_CHAIN |
++ SEC_CFG_ACT_CH0_IDMA), cpg->reg + SEC_ACCEL_CFG);
+ writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0);
+
++ cp->sa_sram_dma = dma_map_single(&pdev->dev, &cp->sa_sram,
++ sizeof(struct sec_accel_sram), DMA_TO_DEVICE);
++
++ if (init_dma_desclist(&cpg->desclist, &pdev->dev,
++ sizeof(u32), MV_DMA_ALIGN, 0)) {
++ ret = -ENOMEM;
++ goto err_mapping;
++ }
++ if (set_dma_desclist_size(&cpg->desclist, MV_DMA_INIT_POOLSIZE)) {
++ printk(KERN_ERR MV_CESA "failed to initialise poolsize\n");
++ goto err_pool;
++ }
++
+ ret = crypto_register_alg(&mv_aes_alg_ecb);
+ if (ret) {
+ printk(KERN_WARNING MV_CESA
+ "Could not register aes-ecb driver\n");
+- goto err_irq;
++ goto err_pool;
+ }
+
+ ret = crypto_register_alg(&mv_aes_alg_cbc);
+@@ -1084,7 +1324,11 @@ static int mv_probe(struct platform_devi
+ return 0;
+ err_unreg_ecb:
+ crypto_unregister_alg(&mv_aes_alg_ecb);
+-err_irq:
++err_pool:
++ fini_dma_desclist(&cpg->desclist);
++err_mapping:
++ dma_unmap_single(&pdev->dev, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram), DMA_TO_DEVICE);
+ free_irq(irq, cp);
+ err_thread:
+ kthread_stop(cp->queue_th);
+@@ -1111,6 +1355,9 @@ static int mv_remove(struct platform_dev
+ crypto_unregister_ahash(&mv_hmac_sha1_alg);
+ kthread_stop(cp->queue_th);
+ free_irq(cp->irq, cp);
++ dma_unmap_single(&pdev->dev, cpg->sa_sram_dma,
++ sizeof(struct sec_accel_sram), DMA_TO_DEVICE);
++ fini_dma_desclist(&cpg->desclist);
+ memset(cp->sram, 0, cp->sram_size);
+ iounmap(cp->sram);
+ iounmap(cp->reg);
+--- a/drivers/crypto/mv_cesa.h
++++ b/drivers/crypto/mv_cesa.h
+@@ -24,18 +24,12 @@
+ #define SEC_CFG_CH1_W_IDMA (1 << 8)
+ #define SEC_CFG_ACT_CH0_IDMA (1 << 9)
+ #define SEC_CFG_ACT_CH1_IDMA (1 << 10)
++#define SEC_CFG_MP_CHAIN (1 << 11)
+
+ #define SEC_ACCEL_STATUS 0xde0c
+ #define SEC_ST_ACT_0 (1 << 0)
+ #define SEC_ST_ACT_1 (1 << 1)
+
+-/*
+- * FPGA_INT_STATUS looks like a FPGA leftover and is documented only in Errata
+- * 4.12. It looks like that it was part of an IRQ-controller in FPGA and
+- * someone forgot to remove it while switching to the core and moving to
+- * SEC_ACCEL_INT_STATUS.
+- */
+-#define FPGA_INT_STATUS 0xdd68
+ #define SEC_ACCEL_INT_STATUS 0xde20
+ #define SEC_INT_AUTH_DONE (1 << 0)
+ #define SEC_INT_DES_E_DONE (1 << 1)
+--- /dev/null
++++ b/drivers/crypto/mv_tdma.c
+@@ -0,0 +1,340 @@
++/*
++ * Support for Marvell's TDMA engine found on Kirkwood chips,
++ * used exclusively by the CESA crypto accelerator.
++ *
++ * Based on unpublished code for IDMA written by Sebastian Siewior.
++ *
++ * Copyright (C) 2012 Phil Sutter <phil.sutter <at> viprinet.com>
++ * License: GPLv2
++ */
++
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmapool.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++
++#include "mv_tdma.h"
++#include "dma_desclist.h"
++
++#define MV_TDMA "MV-TDMA: "
++
++#define MV_DMA_INIT_POOLSIZE 16
++#define MV_DMA_ALIGN 16
++
++struct tdma_desc {
++ u32 count;
++ u32 src;
++ u32 dst;
++ u32 next;
++} __attribute__((packed));
++
++struct tdma_priv {
++ struct device *dev;
++ void __iomem *reg;
++ int irq;
++ /* protecting the dma descriptors and stuff */
++ spinlock_t lock;
++ struct dma_desclist desclist;
++} tpg;
++
++#define ITEM(x) ((struct tdma_desc
*)DESCLIST_ITEM(tpg.desclist, x))
++#define ITEM_DMA(x) DESCLIST_ITEM_DMA(tpg.desclist, x)
++
++static inline void wait_for_tdma_idle(void)
++{
++ while (readl(tpg.reg + TDMA_CTRL) & TDMA_CTRL_ACTIVE)
++ mdelay(100);
++}
++
++static inline void switch_tdma_engine(bool state)
++{
++ u32 val = readl(tpg.reg + TDMA_CTRL);
++
++ val |= ( state * TDMA_CTRL_ENABLE);
++ val &= ~(!state * TDMA_CTRL_ENABLE);
++
++ writel(val, tpg.reg + TDMA_CTRL);
++}
++
++static struct tdma_desc *get_new_last_desc(void)
++{
++ if (unlikely(DESCLIST_FULL(tpg.desclist)) &&
++ set_dma_desclist_size(&tpg.desclist, tpg.desclist.length << 1)) {
++ printk(KERN_ERR MV_TDMA "failed to increase DMA pool to %lu\n",
++ tpg.desclist.length << 1);
++ return NULL;
++ }
++
++ if (likely(tpg.desclist.usage))
++ ITEM(tpg.desclist.usage - 1)->next =
++ ITEM_DMA(tpg.desclist.usage);
++
++ return ITEM(tpg.desclist.usage++);
++}
++
++static inline void mv_tdma_desc_dump(void)
++{
++ struct tdma_desc *tmp;
++ int i;
++
++ if (!tpg.desclist.usage) {
++ printk(KERN_WARNING MV_TDMA "DMA descriptor list is empty\n");
++ return;
++ }
++
++ printk(KERN_WARNING MV_TDMA "DMA descriptor list:\n");
++ for (i = 0; i < tpg.desclist.usage; i++) {
++ tmp = ITEM(i);
++ printk(KERN_WARNING MV_TDMA "entry %d at 0x%x: dma addr 0x%x, "
++ "src 0x%x, dst 0x%x, count %u, own %d, next 0x%x", i,
++ (u32)tmp, ITEM_DMA(i) , tmp->src, tmp->dst,
++ tmp->count & ~TDMA_OWN_BIT, !!(tmp->count &
TDMA_OWN_BIT),
++ tmp->next);
++ }
++}
++
++static inline void mv_tdma_reg_dump(void)
++{
++#define PRINTREG(offset) \
++ printk(KERN_WARNING MV_TDMA "tpg.reg + " #offset " = 0x%x\n", \
++ readl(tpg.reg + offset))
++
++ PRINTREG(TDMA_CTRL);
++ PRINTREG(TDMA_BYTE_COUNT);
++ PRINTREG(TDMA_SRC_ADDR);
++ PRINTREG(TDMA_DST_ADDR);
++ PRINTREG(TDMA_NEXT_DESC);
++ PRINTREG(TDMA_CURR_DESC);
++
++#undef PRINTREG
++}
++
++void mv_tdma_clear(void)
++{
++ if (!tpg.dev)
++ return;
++
++ spin_lock(&tpg.lock);
++
++ /* make sure tdma is idle */
++ wait_for_tdma_idle();
++ switch_tdma_engine(0);
++ wait_for_tdma_idle();
++
++ /* clear descriptor registers */
++ writel(0, tpg.reg + TDMA_BYTE_COUNT);
++ writel(0, tpg.reg + TDMA_CURR_DESC);
++ writel(0, tpg.reg + TDMA_NEXT_DESC);
++
++ tpg.desclist.usage = 0;
++
++ switch_tdma_engine(1);
++
++ /* finally free system lock again */
++ spin_unlock(&tpg.lock);
++}
++EXPORT_SYMBOL_GPL(mv_tdma_clear);
++
++void mv_tdma_trigger(void)
++{
++ if (!tpg.dev)
++ return;
++
++ spin_lock(&tpg.lock);
++
++ writel(ITEM_DMA(0), tpg.reg + TDMA_NEXT_DESC);
++
++ spin_unlock(&tpg.lock);
++}
++EXPORT_SYMBOL_GPL(mv_tdma_trigger);
++
++void mv_tdma_separator(void)
++{
++ struct tdma_desc *tmp;
++
++ if (!tpg.dev)
++ return;
++
++ spin_lock(&tpg.lock);
++
++ tmp = get_new_last_desc();
++ memset(tmp, 0, sizeof(*tmp));
++
++ spin_unlock(&tpg.lock);
++}
++EXPORT_SYMBOL_GPL(mv_tdma_separator);
++
++void mv_tdma_memcpy(dma_addr_t dst, dma_addr_t src, unsigned int size)
++{
++ struct tdma_desc *tmp;
++
++ if (!tpg.dev)
++ return;
++
++ spin_lock(&tpg.lock);
++
++ tmp = get_new_last_desc();
++ tmp->count = size | TDMA_OWN_BIT;
++ tmp->src = src;
++ tmp->dst = dst;
++ tmp->next = 0;
++
++ spin_unlock(&tpg.lock);
++}
++EXPORT_SYMBOL_GPL(mv_tdma_memcpy);
++
++irqreturn_t tdma_int(int irq, void *priv)
++{
++ u32 val;
++
++ val = readl(tpg.reg + TDMA_ERR_CAUSE);
++
++ if (val & TDMA_INT_MISS)
++ printk(KERN_ERR MV_TDMA "%s: miss!\n", __func__);
++ if (val & TDMA_INT_DOUBLE_HIT)
++ printk(KERN_ERR MV_TDMA "%s: double hit!\n", __func__);
++ if (val & TDMA_INT_BOTH_HIT)
++ printk(KERN_ERR MV_TDMA "%s: both hit!\n", __func__);
++ if (val & TDMA_INT_DATA_ERROR)
++ printk(KERN_ERR MV_TDMA "%s: data error!\n", __func__);
++ if (val) {
++ mv_tdma_reg_dump();
++ mv_tdma_desc_dump();
++ }
++
++ switch_tdma_engine(0);
++ wait_for_tdma_idle();
++
++ /* clear descriptor registers */
++ writel(0, tpg.reg + TDMA_BYTE_COUNT);
++ writel(0, tpg.reg + TDMA_SRC_ADDR);
++ writel(0, tpg.reg + TDMA_DST_ADDR);
++ writel(0, tpg.reg + TDMA_CURR_DESC);
++
++ /* clear error cause register */
++ writel(0, tpg.reg + TDMA_ERR_CAUSE);
++
++ /* initialize control register (also enables engine) */
++ writel(TDMA_CTRL_INIT_VALUE, tpg.reg + TDMA_CTRL);
++ wait_for_tdma_idle();
++
++ return (val ? IRQ_HANDLED : IRQ_NONE);
++}
++
++static int mv_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ int rc;
++
++ if (tpg.dev) {
++ printk(KERN_ERR MV_TDMA "second TDMA device?!\n");
++ return -ENXIO;
++ }
++ tpg.dev = &pdev->dev;
++
++ res = platform_get_resource_byname(pdev,
++ IORESOURCE_MEM, "regs control and error");
++ if (!res)
++ return -ENXIO;
++
++ if (!(tpg.reg = ioremap(res->start, resource_size(res))))
++ return -ENOMEM;
++
++ tpg.irq = platform_get_irq(pdev, 0);
++ if (tpg.irq < 0 || tpg.irq == NO_IRQ) {
++ rc = -ENXIO;
++ goto out_unmap_reg;
++ }
++
++ if (init_dma_desclist(&tpg.desclist, tpg.dev,
++ sizeof(struct tdma_desc), MV_DMA_ALIGN, 0)) {
++ rc = -ENOMEM;
++ goto out_free_irq;
++ }
++ if (set_dma_desclist_size(&tpg.desclist, MV_DMA_INIT_POOLSIZE)) {
++ rc = -ENOMEM;
++ goto out_free_desclist;
++ }
++
++ platform_set_drvdata(pdev, &tpg);
++
++ switch_tdma_engine(0);
++ wait_for_tdma_idle();
++
++ /* clear descriptor registers */
++ writel(0, tpg.reg + TDMA_BYTE_COUNT);
++ writel(0, tpg.reg + TDMA_SRC_ADDR);
++ writel(0, tpg.reg + TDMA_DST_ADDR);
++ writel(0, tpg.reg + TDMA_CURR_DESC);
++
++ /* have an ear for occurring errors */
++ writel(TDMA_INT_ALL, tpg.reg + TDMA_ERR_MASK);
++ writel(0, tpg.reg + TDMA_ERR_CAUSE);
++
++ /* initialize control register (also enables engine) */
++ writel(TDMA_CTRL_INIT_VALUE, tpg.reg + TDMA_CTRL);
++ wait_for_tdma_idle();
++
++ if (request_irq(tpg.irq, tdma_int, IRQF_DISABLED,
++ dev_name(tpg.dev), &tpg)) {
++ rc = -ENXIO;
++ goto out_free_all;
++ }
++
++ spin_lock_init(&tpg.lock);
++
++ printk(KERN_INFO MV_TDMA "up and running, IRQ %d\n", tpg.irq);
++ return 0;
++out_free_all:
++ switch_tdma_engine(0);
++ platform_set_drvdata(pdev, NULL);
++out_free_desclist:
++ fini_dma_desclist(&tpg.desclist);
++out_free_irq:
++ free_irq(tpg.irq, &tpg);
++out_unmap_reg:
++ iounmap(tpg.reg);
++ tpg.dev = NULL;
++ return rc;
++}
++
++static int mv_remove(struct platform_device *pdev)
++{
++ switch_tdma_engine(0);
++ platform_set_drvdata(pdev, NULL);
++ fini_dma_desclist(&tpg.desclist);
++ free_irq(tpg.irq, &tpg);
++ iounmap(tpg.reg);
++ tpg.dev = NULL;
++ return 0;
++}
++
++static struct platform_driver marvell_tdma = {
++ .probe = mv_probe,
++ .remove = mv_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "mv_tdma",
++ },
++};
++MODULE_ALIAS("platform:mv_tdma");
++
++static int __init mv_tdma_init(void)
++{
++ return platform_driver_register(&marvell_tdma);
++}
++module_init(mv_tdma_init);
++
++static void __exit mv_tdma_exit(void)
++{
++ platform_driver_unregister(&marvell_tdma);
++}
++module_exit(mv_tdma_exit);
++
++MODULE_AUTHOR("Phil Sutter <phil.sutter <at> viprinet.com>");
++MODULE_DESCRIPTION("Support for Marvell's TDMA engine");
++MODULE_LICENSE("GPL");
++
+--- /dev/null
++++ b/drivers/crypto/mv_tdma.h
+@@ -0,0 +1,50 @@
++#ifndef _MV_TDMA_H
++#define _MV_TDMA_H
++
++/* TDMA_CTRL register bits */
++#define TDMA_CTRL_DST_BURST(x) (x)
++#define TDMA_CTRL_DST_BURST_32 TDMA_CTRL_DST_BURST(3)
++#define TDMA_CTRL_DST_BURST_128 TDMA_CTRL_DST_BURST(4)
++#define TDMA_CTRL_OUTST_RD_EN (1 << 4)
++#define TDMA_CTRL_SRC_BURST(x) (x << 6)
++#define TDMA_CTRL_SRC_BURST_32 TDMA_CTRL_SRC_BURST(3)
++#define TDMA_CTRL_SRC_BURST_128 TDMA_CTRL_SRC_BURST(4)
++#define TDMA_CTRL_NO_CHAIN_MODE (1 << 9)
++#define TDMA_CTRL_NO_BYTE_SWAP (1 << 11)
++#define TDMA_CTRL_ENABLE (1 << 12)
++#define TDMA_CTRL_FETCH_ND (1 << 13)
++#define TDMA_CTRL_ACTIVE (1 << 14)
++
++#define TDMA_CTRL_INIT_VALUE ( \
++ TDMA_CTRL_DST_BURST_128 | TDMA_CTRL_SRC_BURST_128 | \
++ TDMA_CTRL_NO_BYTE_SWAP | TDMA_CTRL_ENABLE \
++)
++
++/* TDMA_ERR_CAUSE bits */
++#define TDMA_INT_MISS (1 << 0)
++#define TDMA_INT_DOUBLE_HIT (1 << 1)
++#define TDMA_INT_BOTH_HIT (1 << 2)
++#define TDMA_INT_DATA_ERROR (1 << 3)
++#define TDMA_INT_ALL 0x0f
++
++/* offsets of registers, starting at "regs control and error" */
++#define TDMA_BYTE_COUNT 0x00
++#define TDMA_SRC_ADDR 0x10
++#define TDMA_DST_ADDR 0x20
++#define TDMA_NEXT_DESC 0x30
++#define TDMA_CTRL 0x40
++#define TDMA_CURR_DESC 0x70
++#define TDMA_ERR_CAUSE 0xc8
++#define TDMA_ERR_MASK 0xcc
++
++/* Owner bit in TDMA_BYTE_COUNT and descriptors' count field, used
++ * to signal TDMA in descriptor chain when input data is complete. */
++#define TDMA_OWN_BIT (1 << 31)
++
++extern void mv_tdma_memcpy(dma_addr_t, dma_addr_t, unsigned int);
++extern void mv_tdma_separator(void);
++extern void mv_tdma_clear(void);
++extern void mv_tdma_trigger(void);
++
++
++#endif /* _MV_TDMA_H */
Index: package/kernel/modules/crypto.mk
===================================================================
--- package/kernel/modules/crypto.mk (revision 32032)
+++ package/kernel/modules/crypto.mk (working copy)
@@ -482,8 +482,11 @@
TITLE:=Marvell crypto engine
DEPENDS:=+kmod-crypto-manager +kmod-crypto-aes @TARGET_kirkwood||TARGET_orion
KCONFIG:=CONFIG_CRYPTO_DEV_MV_CESA
- FILES:=$(LINUX_DIR)/drivers/crypto/mv_cesa.ko
- AUTOLOAD:=$(call AutoLoad,09,mv_cesa)
+ FILES:=$(LINUX_DIR)/drivers/crypto/mv_cesa.ko \
+ $(LINUX_DIR)/drivers/crypto/mv_tdma.ko
+ AUTOLOAD:=$(call AutoLoad,09, \
+ mv_tdma \
+ mv_cesa)
$(call AddDepends/crypto)
endef
bye
MM
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?
_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel