summaryrefslogtreecommitdiff
path: root/drivers/crypto/stmp3xxx_dcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/crypto/stmp3xxx_dcp.c')
-rw-r--r--drivers/crypto/stmp3xxx_dcp.c1401
1 files changed, 1401 insertions, 0 deletions
diff --git a/drivers/crypto/stmp3xxx_dcp.c b/drivers/crypto/stmp3xxx_dcp.c
new file mode 100644
index 000000000000..ff02ec807ec0
--- /dev/null
+++ b/drivers/crypto/stmp3xxx_dcp.c
@@ -0,0 +1,1401 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/*
+ * Based on geode-aes.c
+ * Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/crypto.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <asm/cacheflush.h>
+
+#include "stmp3xxx_dcp.h"
+
+/* SHA1 is busted */
+#define DISABLE_SHA1
+
+struct stmp3xxx_dcp {
+ struct device *dev;
+ spinlock_t lock;
+ struct mutex op_mutex[STMP3XXX_DCP_NUM_CHANNELS];
+ struct completion op_wait[STMP3XXX_DCP_NUM_CHANNELS];
+ int wait[STMP3XXX_DCP_NUM_CHANNELS];
+ int dcp_vmi_irq;
+ int dcp_irq;
+};
+
+/* cipher flags */
+#define STMP3XXX_DCP_ENC 0x0001
+#define STMP3XXX_DCP_DEC 0x0002
+#define STMP3XXX_DCP_ECB 0x0004
+#define STMP3XXX_DCP_CBC 0x0008
+#define STMP3XXX_DCP_CBC_INIT 0x0010
+#define STMP3XXX_DCP_OTPKEY 0x0020
+
+/* hash flags */
+#define STMP3XXX_DCP_INIT 0x0001
+#define STMP3XXX_DCP_UPDATE 0x0002
+#define STMP3XXX_DCP_FINAL 0x0004
+
+#define STMP3XXX_DCP_AES 0x1000
+#define STMP3XXX_DCP_SHA1 0x2000
+#define STMP3XXX_DCP_CRC32 0x3000
+#define STMP3XXX_DCP_COPY 0x4000
+#define STMP3XXX_DCP_FILL 0x5000
+#define STMP3XXX_DCP_MODE_MASK 0xf000
+
+struct stmp3xxx_dcp_op {
+
+ unsigned int flags;
+
+ void *src;
+ dma_addr_t src_phys;
+
+ void *dst;
+ dma_addr_t dst_phys;
+
+ int len;
+
+ /* the key contains the IV for block modes */
+ union {
+ struct {
+ u8 key[2 * AES_KEYSIZE_128]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t key_phys;
+ int keylen;
+ } cipher;
+ struct {
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t digest_phys;
+ int digestlen;
+ int init;
+ } hash;
+ };
+
+ union {
+ struct crypto_blkcipher *blk;
+ struct crypto_cipher *cip;
+ struct crypto_hash *hash;
+ } fallback;
+
+ struct stmp3xxx_dcp_hw_packet pkt
+ __attribute__ ((__aligned__(32)));
+};
+
+struct stmp3xxx_dcp_hash_coherent_block {
+ struct stmp3xxx_dcp_hw_packet pkt[2]
+ __attribute__ ((__aligned__(32)));
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ u8 rembuf[32];
+};
+
+struct stmp3xxx_dcp_hash_op {
+
+ unsigned int flags;
+
+ void *src;
+ dma_addr_t src_phys;
+
+ void *dst;
+ dma_addr_t dst_phys;
+
+ int len;
+
+ /* the key contains the IV for block modes */
+ union {
+ struct {
+ u8 key[2 * AES_KEYSIZE_128]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t key_phys;
+ int keylen;
+ } cipher;
+ struct {
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t digest_phys;
+ int digestlen;
+ int init;
+ } hash;
+ };
+
+ union {
+ struct crypto_blkcipher *blk;
+ struct crypto_cipher *cip;
+ struct crypto_hash *hash;
+ } fallback;
+
+ struct stmp3xxx_dcp_hash_coherent_block *hw;
+ dma_addr_t hw_phys;
+};
+
+/* only one */
+static struct stmp3xxx_dcp *global_sdcp;
+
+static void dcp_perform_op(struct stmp3xxx_dcp_op *op)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct mutex *mutex;
+ struct stmp3xxx_dcp_hw_packet *pkt;
+ int chan;
+ u32 pkt1, pkt2;
+ unsigned long timeout;
+ dma_addr_t pkt_phys;
+ u32 stat;
+
+ pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE | BM_DCP_PACKET1_INTERRUPT;
+
+ switch (op->flags & STMP3XXX_DCP_MODE_MASK) {
+
+ case STMP3XXX_DCP_AES:
+
+ chan = CIPHER_CHAN;
+
+ /* key is at the payload */
+ pkt1 |= BM_DCP_PACKET1_ENABLE_CIPHER;
+ if ((op->flags & STMP3XXX_DCP_OTPKEY) == 0)
+ pkt1 |= BM_DCP_PACKET1_PAYLOAD_KEY;
+ if (op->flags & STMP3XXX_DCP_ENC)
+ pkt1 |= BM_DCP_PACKET1_CIPHER_ENCRYPT;
+ if (op->flags & STMP3XXX_DCP_CBC_INIT)
+ pkt1 |= BM_DCP_PACKET1_CIPHER_INIT;
+
+ pkt2 = BF_DCP_PACKET2_CIPHER_CFG(0) |
+ BF_DCP_PACKET2_KEY_SELECT(0) |
+ BF_DCP_PACKET2_CIPHER_SELECT(
+ BV_DCP_PACKET2_CIPHER_SELECT__AES128);
+
+ if (op->flags & STMP3XXX_DCP_ECB)
+ pkt2 |= BF_DCP_PACKET2_CIPHER_MODE(
+ BV_DCP_PACKET2_CIPHER_MODE__ECB);
+ else if (op->flags & STMP3XXX_DCP_CBC)
+ pkt2 |= BF_DCP_PACKET2_CIPHER_MODE(
+ BV_DCP_PACKET2_CIPHER_MODE__CBC);
+
+ break;
+
+ case STMP3XXX_DCP_SHA1:
+
+ chan = HASH_CHAN;
+
+ pkt1 |= BM_DCP_PACKET1_ENABLE_HASH;
+ if (op->flags & STMP3XXX_DCP_INIT)
+ pkt1 |= BM_DCP_PACKET1_HASH_INIT;
+ if (op->flags & STMP3XXX_DCP_FINAL) {
+ pkt1 |= BM_DCP_PACKET1_HASH_TERM;
+ BUG_ON(op->hash.digest == NULL);
+ }
+
+ pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ break;
+
+ default:
+ dev_err(sdcp->dev, "Unsupported mode\n");
+ return;
+ }
+
+ mutex = &sdcp->op_mutex[chan];
+ pkt = &op->pkt;
+
+ pkt->pNext = 0;
+ pkt->pkt1 = pkt1;
+ pkt->pkt2 = pkt2;
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = (u32)op->dst_phys;
+ pkt->size = op->len;
+ pkt->pPayload = chan == CIPHER_CHAN ?
+ (u32)op->cipher.key_phys : (u32)op->hash.digest_phys;
+ pkt->stat = 0;
+
+ pkt_phys = dma_map_single(sdcp->dev, pkt, sizeof(*pkt),
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, pkt_phys)) {
+ dev_err(sdcp->dev, "Unable to map packet descriptor\n");
+ return;
+ }
+
+ /* submit the work */
+ mutex_lock(mutex);
+
+ HW_DCP_CHnSTAT_CLR(chan, -1);
+
+ /* Load the work packet pointer and bump the channel semaphore */
+ HW_DCP_CHnCMDPTR_WR(chan, (u32)pkt_phys);
+
+ /* XXX wake from interrupt instead of looping */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ sdcp->wait[chan] = 0;
+ HW_DCP_CHnSEMA_WR(chan, BF_DCP_CHnSEMA_INCREMENT(1));
+ while (time_before(jiffies, timeout) && sdcp->wait[chan] == 0)
+ cpu_relax();
+
+ if (!time_before(jiffies, timeout)) {
+ dev_err(sdcp->dev, "Timeout while waiting STAT 0x%08x\n",
+ HW_DCP_STAT_RD());
+ goto out;
+ }
+
+ stat = HW_DCP_CHnSTAT_RD(chan);
+ if ((stat & 0xff) != 0)
+ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
+ HW_DCP_CHnSTAT_RD(chan) & 0xff);
+out:
+ mutex_unlock(mutex);
+
+ dma_unmap_single(sdcp->dev, pkt_phys, sizeof(*pkt), DMA_TO_DEVICE);
+}
+
+static int dcp_aes_setkey_cip(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
+
+ op->cipher.keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->cipher.key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
+ CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_cipher_setkey(op->fallback.cip, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static void dcp_aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_encrypt_one(op->fallback.cip, out, in);
+ return;
+ }
+
+ op->src = (void *) in;
+ op->dst = (void *) out;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC | STMP3XXX_DCP_ECB;
+ op->len = AES_KEYSIZE_128;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ goto err_unmap_src;
+ }
+
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ goto err_unmap_dst;
+ }
+
+ /* perform the operation */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+err_unmap_dst:
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
+err_unmap_src:
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static void dcp_aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_decrypt_one(op->fallback.cip, out, in);
+ return;
+ }
+
+ op->src = (void *) in;
+ op->dst = (void *) out;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC | STMP3XXX_DCP_ECB;
+ op->len = AES_KEYSIZE_128;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ goto err_unmap_src;
+ }
+
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ goto err_unmap_dst;
+ }
+
+ /* perform the operation */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+err_unmap_dst:
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
+err_unmap_src:
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static int fallback_init_cip(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.cip = crypto_alloc_cipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.cip)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.cip);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_cip(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_cipher(op->fallback.cip);
+ op->fallback.cip = NULL;
+}
+
+static struct crypto_alg dcp_aes_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "dcp-aes",
+ .cra_priority = 300,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_cip,
+ .cra_exit = fallback_exit_cip,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = dcp_aes_setkey_cip,
+ .cia_encrypt = dcp_aes_encrypt,
+ .cia_decrypt = dcp_aes_decrypt
+ }
+ }
+};
+
+static int dcp_aes_setkey_blk(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
+
+ op->cipher.keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->cipher.key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
+ CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_blkcipher_setkey(op->fallback.blk, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static int fallback_blk_dec(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_decrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
+}
+
+static int fallback_blk_enc(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
+}
+
+static int fallback_init_blk(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.blk = crypto_alloc_blkcipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.blk)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.blk);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_blk(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_blkcipher(op->fallback.blk);
+ op->fallback.blk = NULL;
+}
+
+static int
+dcp_aes_ecb_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC |
+ STMP3XXX_DCP_ECB;
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+
+ return err;
+}
+
+static int
+dcp_aes_ecb_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, ret;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ err = blkcipher_walk_virt(desc, &walk);
+
+ err = 0;
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC |
+ STMP3XXX_DCP_ECB;
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+
+ return err;
+}
+
+
+static struct crypto_alg dcp_aes_ecb_alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "dcp-ecb-aes",
+ .cra_priority = 400,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_ecb_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = dcp_aes_setkey_blk,
+ .encrypt = dcp_aes_ecb_encrypt,
+ .decrypt = dcp_aes_ecb_decrypt
+ }
+ }
+};
+
+static int
+dcp_aes_cbc_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, blockno;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ blockno = 0;
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC |
+ STMP3XXX_DCP_CBC;
+ if (blockno == 0) {
+ op->flags |= STMP3XXX_DCP_CBC_INIT;
+ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
+ AES_KEYSIZE_128);
+ }
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* key (+iv) needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+
+ blockno++;
+ }
+
+ return err;
+}
+
+static int
+dcp_aes_cbc_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, ret, blockno;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ blockno = 0;
+
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC |
+ STMP3XXX_DCP_CBC;
+ if (blockno == 0) {
+ op->flags |= STMP3XXX_DCP_CBC_INIT;
+ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
+ AES_KEYSIZE_128);
+ }
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+
+ blockno++;
+ }
+
+ return err;
+}
+
+static struct crypto_alg dcp_aes_cbc_alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "dcp-cbc-aes",
+ .cra_priority = 400,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_cbc_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = dcp_aes_setkey_blk,
+ .encrypt = dcp_aes_cbc_encrypt,
+ .decrypt = dcp_aes_cbc_decrypt,
+ .ivsize = AES_KEYSIZE_128,
+ }
+ }
+};
+
+#ifndef DISABLE_SHA1
+
+static void dcp_perform_hash_op(struct stmp3xxx_dcp_hash_op *op)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct mutex *mutex;
+ int chan;
+ struct stmp3xxx_dcp_hw_packet *pkt;
+ unsigned long timeout;
+ u32 stat;
+ int size1, descno;
+
+ BUG_ON((op->flags & STMP3XXX_DCP_MODE_MASK) != STMP3XXX_DCP_SHA1);
+
+ chan = HASH_CHAN;
+
+ switch (op->flags & (STMP3XXX_DCP_INIT | STMP3XXX_DCP_UPDATE |
+ STMP3XXX_DCP_FINAL)) {
+
+ case STMP3XXX_DCP_INIT | STMP3XXX_DCP_UPDATE:
+
+ BUG_ON(op->len <= 1);
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_HASH_INIT |
+ BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = 0;
+ pkt->size = op->len - 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ /* save last byte */
+ op->hw->rembuf[1] = ((u8 *)op->src)[op->len - 1];
+
+ descno = 1;
+ break;
+
+ case STMP3XXX_DCP_UPDATE:
+
+ BUG_ON(op->len <= 1);
+
+ op->hw->rembuf[0] = op->hw->rembuf[1];
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_CHAIN_CONTIGUOUS |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, rembuf);
+ pkt->pDst = 0;
+ pkt->size = 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ pkt = &op->hw->pkt[1];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = 0;
+ pkt->size = op->len - 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ /* save last byte */
+ op->hw->rembuf[1] = ((u8 *)op->src)[op->len - 1];
+
+ descno = 2;
+
+ break;
+
+ case STMP3XXX_DCP_FINAL:
+
+ op->hw->rembuf[0] = op->hw->rembuf[1];
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_HASH_TERM |
+ BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, rembuf);
+ pkt->pDst = 0;
+ pkt->size = 1;
+ pkt->pPayload = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, digest);
+ pkt->stat = 0;
+
+ descno = 1;
+
+ break;
+
+ /* all others BUG */
+ default:
+ BUG();
+ return;
+ }
+
+ mutex = &sdcp->op_mutex[chan];
+
+ /* submit the work */
+ mutex_lock(mutex);
+
+ HW_DCP_CHnSTAT_CLR(chan, -1);
+
+ mb();
+ /* Load the work packet pointer and bump the channel semaphore */
+ HW_DCP_CHnCMDPTR_WR(chan, (u32)op->hw_phys +
+ offsetof(struct stmp3xxx_dcp_hash_coherent_block, pkt[0]));
+
+ /* XXX wake from interrupt instead of looping */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ HW_DCP_CHnSEMA_WR(chan, BF_DCP_CHnSEMA_INCREMENT(descno));
+
+ while (time_before(jiffies, timeout) &&
+ ((HW_DCP_CHnSEMA_RD(chan) >> 16) & 0xff) != 0)
+ cpu_relax();
+
+ if (!time_before(jiffies, timeout)) {
+ dev_err(sdcp->dev, "Timeout while waiting STAT 0x%08x\n",
+ HW_DCP_STAT_RD());
+ goto out;
+ }
+
+ stat = HW_DCP_CHnSTAT_RD(chan);
+ if ((stat & 0xff) != 0)
+ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
+ HW_DCP_CHnSTAT_RD(chan) & 0xff);
+
+out:
+ mutex_unlock(mutex);
+
+ mdelay(500);
+}
+
+static int fallback_init_hash(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+
+ op->fallback.hash = crypto_alloc_hash(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(op->fallback.hash)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.hash);
+ }
+
+ op->hw = dma_alloc_coherent(sdcp->dev, sizeof(*op->hw), &op->hw_phys,
+ GFP_KERNEL);
+ if (op->hw == NULL) {
+ printk(KERN_ERR "Error allocating coherent block for %s\n",
+ name);
+ crypto_free_hash(op->fallback.hash);
+ op->fallback.hash = NULL;
+ return -ENOMEM;
+ }
+ memset(op->hw, 0, sizeof(*op->hw));
+
+ return 0;
+}
+
+static void fallback_exit_hash(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+
+ dma_free_coherent(sdcp->dev, sizeof(*op->hw), op->hw, op->hw_phys);
+ crypto_free_hash(op->fallback.hash);
+ op->fallback.hash = NULL;
+}
+
+static void dcp_sha1_init(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+
+ op->hash.init = 1;
+ memset(op->hw->rembuf, 0, sizeof(op->hw->rembuf));
+ memset(op->hw->digest, 0, sizeof(op->hw->digest));
+}
+
+static void dcp_sha1_update(struct crypto_tfm *tfm,
+ const uint8_t *data, unsigned int length)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+
+ op->src = (void *)data;
+ op->dst = NULL;
+ op->flags = STMP3XXX_DCP_SHA1 | STMP3XXX_DCP_UPDATE;
+ if (op->hash.init) {
+ op->hash.init = 0;
+ op->flags |= STMP3XXX_DCP_INIT;
+ }
+ op->len = length;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+ op->dst_phys = 0;
+
+ /* perform! */
+ dcp_perform_hash_op(op);
+
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static void dcp_sha1_final(struct crypto_tfm *tfm, uint8_t *out)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ int i;
+ const uint8_t *digest;
+
+ op->src = NULL;
+ op->dst = NULL;
+ op->flags = STMP3XXX_DCP_SHA1 | STMP3XXX_DCP_FINAL;
+ op->len = 0;
+
+ /* perform! */
+ dcp_perform_hash_op(op);
+
+ /* hardware reverses the digest (for some unexplicable reason) */
+ digest = op->hw->digest + SHA1_DIGEST_SIZE;
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++)
+ *out++ = *--digest;
+ memcpy(out, op->hw->digest, SHA1_DIGEST_SIZE);
+}
+
+static struct crypto_alg dcp_sha1_alg = {
+ .cra_name = "sha1",
+ .cra_driver_name = "dcp-sha1",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_hash_op),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_sha1_alg.cra_list),
+ .cra_init = fallback_init_hash,
+ .cra_exit = fallback_exit_hash,
+ .cra_u = {
+ .digest = {
+ .dia_digestsize = SHA1_DIGEST_SIZE,
+ .dia_init = dcp_sha1_init,
+ .dia_update = dcp_sha1_update,
+ .dia_final = dcp_sha1_final,
+ }
+ }
+};
+#endif
+
+static irqreturn_t dcp_common_irq(int irq, void *context)
+{
+ struct stmp3xxx_dcp *sdcp = context;
+ u32 msk;
+
+ /* check */
+ msk = HW_DCP_STAT_RD() & BF_DCP_STAT_IRQ(0x0f);
+ if (msk == 0)
+ return IRQ_NONE;
+
+ /* clear this channel */
+ HW_DCP_STAT_CLR(msk);
+ if (msk & BF_DCP_STAT_IRQ(0x01))
+ sdcp->wait[0]++;
+ if (msk & BF_DCP_STAT_IRQ(0x02))
+ sdcp->wait[1]++;
+ if (msk & BF_DCP_STAT_IRQ(0x04))
+ sdcp->wait[2]++;
+ if (msk & BF_DCP_STAT_IRQ(0x08))
+ sdcp->wait[3]++;
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dcp_vmi_irq(int irq, void *context)
+{
+ return dcp_common_irq(irq, context);
+}
+
+static irqreturn_t dcp_irq(int irq, void *context)
+{
+ return dcp_common_irq(irq, context);
+}
+
+static int stmp3xxx_dcp_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp *sdcp = NULL;
+ struct resource *r;
+ int i, ret;
+
+ if (global_sdcp != NULL) {
+ dev_err(&pdev->dev, "Only one instance allowed\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* allocate memory */
+ sdcp = kzalloc(sizeof(*sdcp), GFP_KERNEL);
+ if (sdcp == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate structure\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ sdcp->dev = &pdev->dev;
+ spin_lock_init(&sdcp->lock);
+
+ for (i = 0; i < STMP3XXX_DCP_NUM_CHANNELS; i++) {
+ mutex_init(&sdcp->op_mutex[i]);
+ init_completion(&sdcp->op_wait[i]);
+ }
+
+ platform_set_drvdata(pdev, sdcp);
+
+ /* Soft reset and remove the clock gate */
+ HW_DCP_CTRL_SET(BM_DCP_CTRL_SFTRST);
+
+ /* At 24Mhz, it takes no more than 4 clocks (160 ns) Maximum for
+ * the part to reset, reading the register twice should
+ * be sufficient to get 4 clks delay.
+ */
+ HW_DCP_CTRL_RD();
+ HW_DCP_CTRL_RD();
+
+ HW_DCP_CTRL_CLR(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE);
+
+ /* Initialize control registers */
+ HW_DCP_CTRL_WR(STMP3XXX_DCP_CTRL_INIT);
+ HW_DCP_CHANNELCTRL_WR(STMP3XXX_DCP_CHANNELCTRL_INIT);
+
+ /* We do not enable context switching. Give the context
+ * buffer pointer an illegal address so if context switching is
+ * inadvertantly enabled, the dcp will return an error instead of
+ * trashing good memory. The dcp dma cannot access rom, so any rom
+ * address will do.
+ */
+ HW_DCP_CONTEXT_WR(0xFFFF0000);
+
+ for (i = 0; i < STMP3XXX_DCP_NUM_CHANNELS; i++)
+ HW_DCP_CHnSTAT_CLR(i, -1);
+ HW_DCP_STAT_CLR(-1);
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
+ ret = -EIO;
+ goto err_unregister_sha1;
+ }
+ sdcp->dcp_vmi_irq = r->start;
+ ret = request_irq(sdcp->dcp_vmi_irq, dcp_vmi_irq, 0, "stmp3xxx-dcp",
+ sdcp);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't request_irq (0)\n");
+ goto err_unregister_sha1;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
+ ret = -EIO;
+ goto err_free_irq0;
+ }
+ sdcp->dcp_irq = r->start;
+ ret = request_irq(sdcp->dcp_irq, dcp_irq, 0, "stmp3xxx-dcp", sdcp);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't request_irq (1)\n");
+ goto err_free_irq0;
+ }
+
+ global_sdcp = sdcp;
+
+ ret = crypto_register_alg(&dcp_aes_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes crypto\n");
+ goto err_kfree;
+ }
+
+ ret = crypto_register_alg(&dcp_aes_ecb_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes ecb crypto\n");
+ goto err_unregister_aes;
+ }
+
+ ret = crypto_register_alg(&dcp_aes_cbc_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes cbc crypto\n");
+ goto err_unregister_aes_ecb;
+ }
+
+#ifndef DISABLE_SHA1
+ ret = crypto_register_alg(&dcp_sha1_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes cbc crypto\n");
+ goto err_unregister_aes_cbc;
+ }
+#endif
+
+ dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
+ return 0;
+
+err_free_irq0:
+ free_irq(sdcp->dcp_vmi_irq, sdcp);
+err_unregister_sha1:
+#ifndef DISABLE_SHA1
+ crypto_unregister_alg(&dcp_sha1_alg);
+#endif
+err_unregister_aes_cbc:
+ crypto_unregister_alg(&dcp_aes_cbc_alg);
+err_unregister_aes_ecb:
+ crypto_unregister_alg(&dcp_aes_ecb_alg);
+err_unregister_aes:
+ crypto_unregister_alg(&dcp_aes_alg);
+err_kfree:
+ kfree(sdcp);
+err:
+
+ return ret;
+}
+
+static int stmp3xxx_dcp_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp *sdcp;
+
+ sdcp = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(sdcp->dcp_irq, sdcp);
+ free_irq(sdcp->dcp_vmi_irq, sdcp);
+#ifndef DISABLE_SHA1
+ crypto_unregister_alg(&dcp_sha1_alg);
+#endif
+ crypto_unregister_alg(&dcp_aes_cbc_alg);
+ crypto_unregister_alg(&dcp_aes_ecb_alg);
+ crypto_unregister_alg(&dcp_aes_alg);
+ kfree(sdcp);
+ global_sdcp = NULL;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int stmp3xxx_dcp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int stmp3xxx_dcp_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_dcp_suspend NULL
+#define stmp3xxx_dcp_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_dcp_driver = {
+ .probe = stmp3xxx_dcp_probe,
+ .remove = stmp3xxx_dcp_remove,
+ .suspend = stmp3xxx_dcp_suspend,
+ .resume = stmp3xxx_dcp_resume,
+ .driver = {
+ .name = "stmp3xxx-dcp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+stmp3xxx_dcp_init(void)
+{
+ return platform_driver_register(&stmp3xxx_dcp_driver);
+}
+
+static void __exit
+stmp3xxx_dcp_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_dcp_driver);
+}
+
+MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3XXX DCP Crypto Driver");
+MODULE_LICENSE("GPL");
+
+module_init(stmp3xxx_dcp_init);
+module_exit(stmp3xxx_dcp_exit);