mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-02-15 05:25:03 +00:00
Add functionality of using OTP key for crypto into dcp driver Signed-off-by: Anish Trivedi <anish@freescale.com>
1697 lines
43 KiB
C
1697 lines
43 KiB
C
/*
|
|
* Copyright (C) 2008-2010 Freescale Semiconductor, Inc.
|
|
*/
|
|
|
|
/*
|
|
* 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/errno.h>
|
|
#include <linux/sysdev.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/fs.h>
|
|
#include <crypto/algapi.h>
|
|
#include <crypto/aes.h>
|
|
#include <crypto/sha.h>
|
|
#include <crypto/hash.h>
|
|
#include <crypto/internal/hash.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <mach/hardware.h>
|
|
#include "dcp.h"
|
|
#include "dcp_bootstream_ioctl.h"
|
|
|
|
/* Following data only used by DCP bootstream interface */
|
|
struct dcpboot_dma_area {
|
|
struct dcp_hw_packet hw_packet;
|
|
uint16_t block[16];
|
|
};
|
|
|
|
struct dcp {
|
|
struct device *dev;
|
|
spinlock_t lock;
|
|
struct mutex op_mutex[DCP_NUM_CHANNELS];
|
|
struct completion op_wait[DCP_NUM_CHANNELS];
|
|
int wait[DCP_NUM_CHANNELS];
|
|
int dcp_vmi_irq;
|
|
int dcp_irq;
|
|
u32 dcp_regs_base;
|
|
|
|
/* Following buffers used in hashing to meet 64-byte len alignment */
|
|
char *buf1;
|
|
char *buf2;
|
|
dma_addr_t buf1_phys;
|
|
dma_addr_t buf2_phys;
|
|
struct dcp_hash_coherent_block *buf1_desc;
|
|
struct dcp_hash_coherent_block *buf2_desc;
|
|
struct dcp_hash_coherent_block *user_buf_desc;
|
|
|
|
/* Following data only used by DCP bootstream interface */
|
|
struct dcpboot_dma_area *dcpboot_dma_area;
|
|
dma_addr_t dcpboot_dma_area_phys;
|
|
};
|
|
|
|
/* cipher flags */
|
|
#define DCP_ENC 0x0001
|
|
#define DCP_DEC 0x0002
|
|
#define DCP_ECB 0x0004
|
|
#define DCP_CBC 0x0008
|
|
#define DCP_CBC_INIT 0x0010
|
|
#define DCP_OTPKEY 0x0020
|
|
|
|
/* hash flags */
|
|
#define DCP_INIT 0x0001
|
|
#define DCP_UPDATE 0x0002
|
|
#define DCP_FINAL 0x0004
|
|
|
|
#define DCP_AES 0x1000
|
|
#define DCP_SHA1 0x2000
|
|
#define DCP_CRC32 0x3000
|
|
#define DCP_COPY 0x4000
|
|
#define DCP_FILL 0x5000
|
|
#define DCP_MODE_MASK 0xf000
|
|
|
|
struct 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[SHA256_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 dcp_hw_packet pkt
|
|
__attribute__ ((__aligned__(32)));
|
|
};
|
|
|
|
struct dcp_hash_coherent_block {
|
|
struct dcp_hw_packet pkt[1]
|
|
__attribute__ ((__aligned__(32)));
|
|
u8 digest[SHA256_DIGEST_SIZE]
|
|
__attribute__ ((__aligned__(32)));
|
|
unsigned int len;
|
|
dma_addr_t src_phys;
|
|
void *src;
|
|
void *dst;
|
|
dma_addr_t my_phys;
|
|
u32 hash_sel;
|
|
struct dcp_hash_coherent_block *next;
|
|
};
|
|
|
|
struct dcp_hash_op {
|
|
|
|
unsigned int flags;
|
|
|
|
/* 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[SHA256_DIGEST_SIZE]
|
|
__attribute__ ((__aligned__(32)));
|
|
dma_addr_t digest_phys;
|
|
int digestlen;
|
|
int init;
|
|
} hash;
|
|
};
|
|
|
|
u32 length;
|
|
struct dcp_hash_coherent_block *head_desc;
|
|
struct dcp_hash_coherent_block *tail_desc;
|
|
};
|
|
|
|
/* only one */
|
|
static struct dcp *global_sdcp;
|
|
|
|
static void dcp_perform_op(struct dcp_op *op)
|
|
{
|
|
struct dcp *sdcp = global_sdcp;
|
|
struct mutex *mutex;
|
|
struct 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 & DCP_MODE_MASK) {
|
|
|
|
case DCP_AES:
|
|
|
|
chan = CIPHER_CHAN;
|
|
|
|
/* key is at the payload */
|
|
pkt1 |= BM_DCP_PACKET1_ENABLE_CIPHER;
|
|
if ((op->flags & DCP_OTPKEY) == 0)
|
|
pkt1 |= BM_DCP_PACKET1_PAYLOAD_KEY;
|
|
if (op->flags & DCP_ENC)
|
|
pkt1 |= BM_DCP_PACKET1_CIPHER_ENCRYPT;
|
|
if (op->flags & DCP_CBC_INIT)
|
|
pkt1 |= BM_DCP_PACKET1_CIPHER_INIT;
|
|
|
|
pkt2 = BF(0, DCP_PACKET2_CIPHER_CFG) |
|
|
BF(0, DCP_PACKET2_KEY_SELECT) |
|
|
BF(BV_DCP_PACKET2_CIPHER_SELECT__AES128,
|
|
DCP_PACKET2_CIPHER_SELECT);
|
|
|
|
if (op->flags & DCP_ECB)
|
|
pkt2 |= BF(BV_DCP_PACKET2_CIPHER_MODE__ECB,
|
|
DCP_PACKET2_CIPHER_MODE);
|
|
else if (op->flags & DCP_CBC)
|
|
pkt2 |= BF(BV_DCP_PACKET2_CIPHER_MODE__CBC,
|
|
DCP_PACKET2_CIPHER_MODE);
|
|
|
|
break;
|
|
|
|
case DCP_SHA1:
|
|
|
|
chan = HASH_CHAN;
|
|
|
|
pkt1 |= BM_DCP_PACKET1_ENABLE_HASH;
|
|
if (op->flags & DCP_INIT)
|
|
pkt1 |= BM_DCP_PACKET1_HASH_INIT;
|
|
if (op->flags & DCP_FINAL) {
|
|
pkt1 |= BM_DCP_PACKET1_HASH_TERM;
|
|
BUG_ON(op->hash.digest == NULL);
|
|
}
|
|
|
|
pkt2 = BF(BV_DCP_PACKET2_HASH_SELECT__SHA1,
|
|
DCP_PACKET2_HASH_SELECT);
|
|
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);
|
|
|
|
__raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(chan));
|
|
|
|
/* Load the work packet pointer and bump the channel semaphore */
|
|
__raw_writel((u32)pkt_phys, sdcp->dcp_regs_base +
|
|
HW_DCP_CHnCMDPTR(chan));
|
|
|
|
/* XXX wake from interrupt instead of looping */
|
|
timeout = jiffies + msecs_to_jiffies(1000);
|
|
|
|
sdcp->wait[chan] = 0;
|
|
__raw_writel(BF(1, DCP_CHnSEMA_INCREMENT), sdcp->dcp_regs_base
|
|
+ HW_DCP_CHnSEMA(chan));
|
|
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",
|
|
__raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT));
|
|
goto out;
|
|
}
|
|
|
|
stat = __raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(chan));
|
|
if ((stat & 0xff) != 0)
|
|
dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
|
|
__raw_readl(sdcp->dcp_regs_base +
|
|
HW_DCP_CHnSTAT(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 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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_ENC | 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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_DEC | 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 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 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 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 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 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 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 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 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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_DEC |
|
|
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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_ENC |
|
|
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 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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_DEC |
|
|
DCP_CBC;
|
|
if (blockno == 0) {
|
|
op->flags |= 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 dcp *sdcp = global_sdcp;
|
|
struct 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 = DCP_AES | DCP_ENC |
|
|
DCP_CBC;
|
|
if (blockno == 0) {
|
|
op->flags |= 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 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,
|
|
}
|
|
}
|
|
};
|
|
|
|
static int dcp_perform_hash_op(
|
|
struct dcp_hash_coherent_block *input,
|
|
u32 num_desc, bool init, bool terminate)
|
|
{
|
|
struct dcp *sdcp = global_sdcp;
|
|
int chan;
|
|
struct dcp_hw_packet *pkt;
|
|
struct dcp_hash_coherent_block *hw;
|
|
unsigned long timeout;
|
|
u32 stat;
|
|
int descno, mapped;
|
|
|
|
chan = HASH_CHAN;
|
|
|
|
hw = input;
|
|
pkt = hw->pkt;
|
|
|
|
for (descno = 0; descno < num_desc; descno++) {
|
|
|
|
if (descno != 0) {
|
|
|
|
/* set next ptr and CHAIN bit in last packet */
|
|
pkt->pNext = hw->next->my_phys + offsetof(
|
|
struct dcp_hash_coherent_block,
|
|
pkt[0]);
|
|
pkt->pkt1 |= BM_DCP_PACKET1_CHAIN;
|
|
|
|
/* iterate to next descriptor */
|
|
hw = hw->next;
|
|
pkt = hw->pkt;
|
|
}
|
|
|
|
pkt->pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
|
|
BM_DCP_PACKET1_ENABLE_HASH;
|
|
|
|
if (init && descno == 0)
|
|
pkt->pkt1 |= BM_DCP_PACKET1_HASH_INIT;
|
|
|
|
pkt->pkt2 = BF(hw->hash_sel,
|
|
DCP_PACKET2_HASH_SELECT);
|
|
|
|
/* no need to flush buf1 or buf2, which are uncached */
|
|
if (hw->src != sdcp->buf1 && hw->src != sdcp->buf2) {
|
|
|
|
/* we have to flush the cache for the buffer */
|
|
hw->src_phys = dma_map_single(sdcp->dev,
|
|
hw->src, hw->len, DMA_TO_DEVICE);
|
|
|
|
if (dma_mapping_error(sdcp->dev, hw->src_phys)) {
|
|
dev_err(sdcp->dev, "Unable to map source\n");
|
|
|
|
/* unmap any previous mapped buffers */
|
|
for (mapped = 0, hw = input; mapped < descno;
|
|
mapped++) {
|
|
|
|
if (mapped != 0)
|
|
hw = hw->next;
|
|
if (hw->src != sdcp->buf1 &&
|
|
hw->src != sdcp->buf2)
|
|
dma_unmap_single(sdcp->dev,
|
|
hw->src_phys, hw->len,
|
|
DMA_TO_DEVICE);
|
|
}
|
|
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
|
|
pkt->pSrc = (u32)hw->src_phys;
|
|
pkt->pDst = 0;
|
|
pkt->size = hw->len;
|
|
pkt->pPayload = 0;
|
|
pkt->stat = 0;
|
|
|
|
/* set HASH_TERM bit on last buf if terminate was set */
|
|
if (terminate && (descno == (num_desc - 1))) {
|
|
pkt->pkt1 |= BM_DCP_PACKET1_HASH_TERM;
|
|
|
|
memset(input->digest, 0, sizeof(input->digest));
|
|
|
|
/* set payload ptr to the 1st buffer's digest */
|
|
pkt->pPayload = (u32)input->my_phys +
|
|
offsetof(
|
|
struct dcp_hash_coherent_block,
|
|
digest);
|
|
}
|
|
}
|
|
|
|
/* submit the work */
|
|
|
|
__raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(chan));
|
|
|
|
mb();
|
|
/* Load the 1st descriptor's physical address */
|
|
__raw_writel((u32)input->my_phys +
|
|
offsetof(struct dcp_hash_coherent_block,
|
|
pkt[0]), sdcp->dcp_regs_base + HW_DCP_CHnCMDPTR(chan));
|
|
|
|
/* XXX wake from interrupt instead of looping */
|
|
timeout = jiffies + msecs_to_jiffies(1000);
|
|
|
|
/* write num_desc into sema register */
|
|
__raw_writel(BF(num_desc, DCP_CHnSEMA_INCREMENT),
|
|
sdcp->dcp_regs_base + HW_DCP_CHnSEMA(chan));
|
|
|
|
while (time_before(jiffies, timeout) &&
|
|
((__raw_readl(sdcp->dcp_regs_base +
|
|
HW_DCP_CHnSEMA(chan)) >> 16) & 0xff) != 0) {
|
|
|
|
cpu_relax();
|
|
}
|
|
|
|
if (!time_before(jiffies, timeout)) {
|
|
dev_err(sdcp->dev,
|
|
"Timeout while waiting STAT 0x%08x\n",
|
|
__raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT));
|
|
}
|
|
|
|
stat = __raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(chan));
|
|
if ((stat & 0xff) != 0)
|
|
dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
|
|
__raw_readl(sdcp->dcp_regs_base +
|
|
HW_DCP_CHnSTAT(chan)) & 0xff);
|
|
|
|
/* unmap all src buffers */
|
|
for (descno = 0, hw = input; descno < num_desc; descno++) {
|
|
if (descno != 0)
|
|
hw = hw->next;
|
|
if (hw->src != sdcp->buf1 && hw->src != sdcp->buf2)
|
|
dma_unmap_single(sdcp->dev, hw->src_phys, hw->len,
|
|
DMA_TO_DEVICE);
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int dcp_sha_init(struct shash_desc *desc)
|
|
{
|
|
struct dcp *sdcp = global_sdcp;
|
|
struct dcp_hash_op *op = shash_desc_ctx(desc);
|
|
struct mutex *mutex = &sdcp->op_mutex[HASH_CHAN];
|
|
|
|
mutex_lock(mutex);
|
|
|
|
op->length = 0;
|
|
|
|
/* reset the lengths and the pointers of buffer descriptors */
|
|
sdcp->buf1_desc->len = 0;
|
|
sdcp->buf1_desc->src = sdcp->buf1;
|
|
sdcp->buf2_desc->len = 0;
|
|
sdcp->buf2_desc->src = sdcp->buf2;
|
|
op->head_desc = sdcp->buf1_desc;
|
|
op->tail_desc = sdcp->buf2_desc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dcp_sha_update(struct shash_desc *desc, const u8 *data,
|
|
unsigned int length)
|
|
{
|
|
struct dcp *sdcp = global_sdcp;
|
|
struct dcp_hash_op *op = shash_desc_ctx(desc);
|
|
struct dcp_hash_coherent_block *temp;
|
|
u32 rem_bytes, bytes_borrowed, hash_sel;
|
|
int ret = 0;
|
|
|
|
if (strcmp(desc->tfm->base.__crt_alg->cra_name, "sha1") == 0)
|
|
hash_sel = BV_DCP_PACKET2_HASH_SELECT__SHA1;
|
|
else
|
|
hash_sel = BV_DCP_PACKET2_HASH_SELECT__SHA256;
|
|
|
|
sdcp->user_buf_desc->src = (void *)data;
|
|
sdcp->user_buf_desc->len = length;
|
|
|
|
op->tail_desc->len = 0;
|
|
|
|
/* check if any pending data from previous updates */
|
|
if (op->head_desc->len) {
|
|
|
|
/* borrow from this buffer to make it 64 bytes */
|
|
bytes_borrowed = min(64 - op->head_desc->len,
|
|
sdcp->user_buf_desc->len);
|
|
|
|
/* copy n bytes to head */
|
|
memcpy(op->head_desc->src + op->head_desc->len,
|
|
sdcp->user_buf_desc->src, bytes_borrowed);
|
|
op->head_desc->len += bytes_borrowed;
|
|
|
|
/* update current buffer's src and len */
|
|
sdcp->user_buf_desc->src += bytes_borrowed;
|
|
sdcp->user_buf_desc->len -= bytes_borrowed;
|
|
}
|
|
|
|
/* Is current buffer unaligned to 64 byte length?
|
|
* Each buffer's length must be a multiple of 64 bytes for DCP
|
|
*/
|
|
rem_bytes = sdcp->user_buf_desc->len % 64;
|
|
|
|
/* if length is unaligned, copy remainder to tail */
|
|
if (rem_bytes) {
|
|
|
|
memcpy(op->tail_desc->src, (sdcp->user_buf_desc->src +
|
|
sdcp->user_buf_desc->len - rem_bytes),
|
|
rem_bytes);
|
|
|
|
/* update length of current buffer */
|
|
sdcp->user_buf_desc->len -= rem_bytes;
|
|
|
|
op->tail_desc->len = rem_bytes;
|
|
}
|
|
|
|
/* do not send to DCP if length is < 64 */
|
|
if ((op->head_desc->len + sdcp->user_buf_desc->len) >= 64) {
|
|
|
|
/* set hash alg to be used (SHA1 or SHA256) */
|
|
op->head_desc->hash_sel = hash_sel;
|
|
sdcp->user_buf_desc->hash_sel = hash_sel;
|
|
|
|
if (op->head_desc->len) {
|
|
op->head_desc->next = sdcp->user_buf_desc;
|
|
|
|
ret = dcp_perform_hash_op(op->head_desc,
|
|
sdcp->user_buf_desc->len ? 2 : 1,
|
|
op->length == 0, false);
|
|
} else {
|
|
ret = dcp_perform_hash_op(sdcp->user_buf_desc, 1,
|
|
op->length == 0, false);
|
|
}
|
|
|
|
op->length += op->head_desc->len + sdcp->user_buf_desc->len;
|
|
op->head_desc->len = 0;
|
|
}
|
|
|
|
/* if tail has bytes, make it the head for next time */
|
|
if (op->tail_desc->len) {
|
|
temp = op->head_desc;
|
|
op->head_desc = op->tail_desc;
|
|
op->tail_desc = temp;
|
|
}
|
|
|
|
/* hash_sel to be used by final function */
|
|
op->head_desc->hash_sel = hash_sel;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dcp_sha_final(struct shash_desc *desc, u8 *out)
|
|
{
|
|
struct dcp_hash_op *op = shash_desc_ctx(desc);
|
|
const uint8_t *digest;
|
|
struct dcp *sdcp = global_sdcp;
|
|
u32 i, digest_len;
|
|
struct mutex *mutex = &sdcp->op_mutex[HASH_CHAN];
|
|
int ret = 0;
|
|
|
|
/* Send the leftover bytes in head, which can be length 0,
|
|
* but DCP still produces hash result in payload ptr.
|
|
* Last data bytes need not be 64-byte multiple.
|
|
*/
|
|
ret = dcp_perform_hash_op(op->head_desc, 1, op->length == 0, true);
|
|
|
|
op->length += op->head_desc->len;
|
|
|
|
digest_len = (op->head_desc->hash_sel ==
|
|
BV_DCP_PACKET2_HASH_SELECT__SHA1) ? SHA1_DIGEST_SIZE :
|
|
SHA256_DIGEST_SIZE;
|
|
|
|
/* hardware reverses the digest (for some unexplicable reason) */
|
|
digest = op->head_desc->digest + digest_len;
|
|
for (i = 0; i < digest_len; i++)
|
|
*out++ = *--digest;
|
|
|
|
mutex_unlock(mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct shash_alg dcp_sha1_alg = {
|
|
.init = dcp_sha_init,
|
|
.update = dcp_sha_update,
|
|
.final = dcp_sha_final,
|
|
.descsize = sizeof(struct dcp_hash_op),
|
|
.digestsize = SHA1_DIGEST_SIZE,
|
|
.base = {
|
|
.cra_name = "sha1",
|
|
.cra_driver_name = "sha1-dcp",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = SHA1_BLOCK_SIZE,
|
|
.cra_ctxsize =
|
|
sizeof(struct dcp_hash_op),
|
|
.cra_module = THIS_MODULE,
|
|
}
|
|
};
|
|
|
|
static struct shash_alg dcp_sha256_alg = {
|
|
.init = dcp_sha_init,
|
|
.update = dcp_sha_update,
|
|
.final = dcp_sha_final,
|
|
.descsize = sizeof(struct dcp_hash_op),
|
|
.digestsize = SHA256_DIGEST_SIZE,
|
|
.base = {
|
|
.cra_name = "sha256",
|
|
.cra_driver_name = "sha256-dcp",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = SHA256_BLOCK_SIZE,
|
|
.cra_ctxsize =
|
|
sizeof(struct dcp_hash_op),
|
|
.cra_module = THIS_MODULE,
|
|
}
|
|
};
|
|
|
|
static irqreturn_t dcp_common_irq(int irq, void *context)
|
|
{
|
|
struct dcp *sdcp = context;
|
|
u32 msk;
|
|
|
|
/* check */
|
|
msk = __raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT) &
|
|
BF(0x0f, DCP_STAT_IRQ);
|
|
if (msk == 0)
|
|
return IRQ_NONE;
|
|
|
|
/* clear this channel */
|
|
__raw_writel(msk, sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
|
|
if (msk & BF(0x01, DCP_STAT_IRQ))
|
|
sdcp->wait[0]++;
|
|
if (msk & BF(0x02, DCP_STAT_IRQ))
|
|
sdcp->wait[1]++;
|
|
if (msk & BF(0x04, DCP_STAT_IRQ))
|
|
sdcp->wait[2]++;
|
|
if (msk & BF(0x08, DCP_STAT_IRQ))
|
|
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);
|
|
}
|
|
|
|
/* DCP bootstream verification interface: uses OTP key for crypto */
|
|
static int dcp_bootstream_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct dcp *sdcp = global_sdcp;
|
|
struct dcpboot_dma_area *da = sdcp->dcpboot_dma_area;
|
|
void __user *argp = (void __user *)arg;
|
|
int chan = ROM_DCP_CHAN;
|
|
unsigned long timeout;
|
|
struct mutex *mutex;
|
|
int retVal;
|
|
|
|
/* be paranoid */
|
|
if (sdcp == NULL)
|
|
return -EBADF;
|
|
|
|
if (cmd != DBS_ENC && cmd != DBS_DEC)
|
|
return -EINVAL;
|
|
|
|
/* copy to (aligned) block */
|
|
if (copy_from_user(da->block, argp, 16))
|
|
return -EFAULT;
|
|
|
|
mutex = &sdcp->op_mutex[chan];
|
|
mutex_lock(mutex);
|
|
|
|
__raw_writel(-1, sdcp->dcp_regs_base +
|
|
HW_DCP_CHnSTAT_CLR(ROM_DCP_CHAN));
|
|
__raw_writel(BF(ROM_DCP_CHAN_MASK, DCP_STAT_IRQ),
|
|
sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
|
|
|
|
da->hw_packet.pNext = 0;
|
|
da->hw_packet.pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
|
|
BM_DCP_PACKET1_ENABLE_CIPHER | BM_DCP_PACKET1_OTP_KEY |
|
|
BM_DCP_PACKET1_INTERRUPT |
|
|
(cmd == DBS_ENC ? BM_DCP_PACKET1_CIPHER_ENCRYPT : 0);
|
|
da->hw_packet.pkt2 = BF(0, DCP_PACKET2_CIPHER_CFG) |
|
|
BF(0, DCP_PACKET2_KEY_SELECT) |
|
|
BF(BV_DCP_PACKET2_CIPHER_MODE__ECB, DCP_PACKET2_CIPHER_MODE) |
|
|
BF(BV_DCP_PACKET2_CIPHER_SELECT__AES128, DCP_PACKET2_CIPHER_SELECT);
|
|
da->hw_packet.pSrc = sdcp->dcpboot_dma_area_phys +
|
|
offsetof(struct dcpboot_dma_area, block);
|
|
da->hw_packet.pDst = da->hw_packet.pSrc; /* in-place */
|
|
da->hw_packet.size = 16;
|
|
da->hw_packet.pPayload = 0;
|
|
da->hw_packet.stat = 0;
|
|
|
|
/* Load the work packet pointer and bump the channel semaphore */
|
|
__raw_writel(sdcp->dcpboot_dma_area_phys +
|
|
offsetof(struct dcpboot_dma_area, hw_packet),
|
|
sdcp->dcp_regs_base + HW_DCP_CHnCMDPTR(ROM_DCP_CHAN));
|
|
|
|
sdcp->wait[chan] = 0;
|
|
__raw_writel(BF(1, DCP_CHnSEMA_INCREMENT),
|
|
sdcp->dcp_regs_base + HW_DCP_CHnSEMA(ROM_DCP_CHAN));
|
|
|
|
timeout = jiffies + msecs_to_jiffies(100);
|
|
|
|
while (time_before(jiffies, timeout) && sdcp->wait[chan] == 0)
|
|
cpu_relax();
|
|
|
|
if (!time_before(jiffies, timeout)) {
|
|
dev_err(sdcp->dev,
|
|
"Timeout while waiting for operation to complete\n");
|
|
retVal = -ETIMEDOUT;
|
|
goto exit;
|
|
}
|
|
|
|
if ((__raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(ROM_DCP_CHAN))
|
|
& 0xff) != 0) {
|
|
dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
|
|
__raw_readl(sdcp->dcp_regs_base +
|
|
HW_DCP_CHnSTAT(ROM_DCP_CHAN)) & 0xff);
|
|
retVal = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
if (copy_to_user(argp, da->block, 16)) {
|
|
retVal = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
retVal = 0;
|
|
|
|
exit:
|
|
mutex_unlock(mutex);
|
|
return retVal;
|
|
}
|
|
|
|
static const struct file_operations dcp_bootstream_fops = {
|
|
.owner = THIS_MODULE,
|
|
.ioctl = dcp_bootstream_ioctl,
|
|
};
|
|
|
|
static struct miscdevice dcp_bootstream_misc = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "dcpboot",
|
|
.fops = &dcp_bootstream_fops,
|
|
};
|
|
|
|
static int dcp_probe(struct platform_device *pdev)
|
|
{
|
|
struct dcp *sdcp = NULL;
|
|
struct resource *r;
|
|
int i, ret;
|
|
dma_addr_t hw_phys;
|
|
|
|
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 < DCP_NUM_CHANNELS; i++) {
|
|
mutex_init(&sdcp->op_mutex[i]);
|
|
init_completion(&sdcp->op_wait[i]);
|
|
}
|
|
|
|
platform_set_drvdata(pdev, sdcp);
|
|
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!r) {
|
|
dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
|
|
ret = -ENXIO;
|
|
goto err_kfree;
|
|
}
|
|
sdcp->dcp_regs_base = (u32) IO_ADDRESS(r->start);
|
|
|
|
/* Soft reset and remove the clock gate */
|
|
__raw_writel(BM_DCP_CTRL_SFTRST, sdcp->dcp_regs_base + HW_DCP_CTRL_SET);
|
|
|
|
/* 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.
|
|
*/
|
|
__raw_readl(sdcp->dcp_regs_base + HW_DCP_CTRL);
|
|
__raw_readl(sdcp->dcp_regs_base + HW_DCP_CTRL);
|
|
|
|
__raw_writel(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE,
|
|
sdcp->dcp_regs_base + HW_DCP_CTRL_CLR);
|
|
|
|
/* Initialize control registers */
|
|
__raw_writel(DCP_CTRL_INIT, sdcp->dcp_regs_base + HW_DCP_CTRL);
|
|
__raw_writel(DCP_CHANNELCTRL_INIT, sdcp->dcp_regs_base +
|
|
HW_DCP_CHANNELCTRL);
|
|
|
|
/* 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.
|
|
*/
|
|
__raw_writel(0xFFFF0000, sdcp->dcp_regs_base + HW_DCP_CONTEXT);
|
|
|
|
for (i = 0; i < DCP_NUM_CHANNELS; i++)
|
|
__raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(i));
|
|
__raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
|
|
|
|
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_kfree;
|
|
}
|
|
sdcp->dcp_vmi_irq = r->start;
|
|
ret = request_irq(sdcp->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp",
|
|
sdcp);
|
|
if (ret != 0) {
|
|
dev_err(&pdev->dev, "can't request_irq (0)\n");
|
|
goto err_kfree;
|
|
}
|
|
|
|
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, "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;
|
|
}
|
|
|
|
/* Allocate the descriptor to be used for user buffer
|
|
* passed in by the "update" function from Crypto API
|
|
*/
|
|
sdcp->user_buf_desc = dma_alloc_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block), &hw_phys,
|
|
GFP_KERNEL);
|
|
if (sdcp->user_buf_desc == NULL) {
|
|
printk(KERN_ERR "Error allocating coherent block\n");
|
|
ret = -ENOMEM;
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
sdcp->user_buf_desc->my_phys = hw_phys;
|
|
|
|
/* Allocate 2 buffers (head & tail) & its descriptors to deal with
|
|
* buffer lengths that are not 64 byte aligned, except for the
|
|
* last one.
|
|
*/
|
|
sdcp->buf1 = dma_alloc_coherent(sdcp->dev,
|
|
64, &sdcp->buf1_phys, GFP_KERNEL);
|
|
if (sdcp->buf1 == NULL) {
|
|
printk(KERN_ERR "Error allocating coherent block\n");
|
|
ret = -ENOMEM;
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
sdcp->buf2 = dma_alloc_coherent(sdcp->dev,
|
|
64, &sdcp->buf2_phys, GFP_KERNEL);
|
|
if (sdcp->buf2 == NULL) {
|
|
printk(KERN_ERR "Error allocating coherent block\n");
|
|
ret = -ENOMEM;
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
sdcp->buf1_desc = dma_alloc_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block), &hw_phys,
|
|
GFP_KERNEL);
|
|
if (sdcp->buf1_desc == NULL) {
|
|
printk(KERN_ERR "Error allocating coherent block\n");
|
|
ret = -ENOMEM;
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
sdcp->buf1_desc->my_phys = hw_phys;
|
|
sdcp->buf1_desc->src = (void *)sdcp->buf1;
|
|
sdcp->buf1_desc->src_phys = sdcp->buf1_phys;
|
|
|
|
sdcp->buf2_desc = dma_alloc_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block), &hw_phys,
|
|
GFP_KERNEL);
|
|
if (sdcp->buf2_desc == NULL) {
|
|
printk(KERN_ERR "Error allocating coherent block\n");
|
|
ret = -ENOMEM;
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
sdcp->buf2_desc->my_phys = hw_phys;
|
|
sdcp->buf2_desc->src = (void *)sdcp->buf2;
|
|
sdcp->buf2_desc->src_phys = sdcp->buf2_phys;
|
|
|
|
|
|
ret = crypto_register_shash(&dcp_sha1_alg);
|
|
if (ret != 0) {
|
|
dev_err(&pdev->dev, "Failed to register sha1 hash\n");
|
|
goto err_unregister_aes_cbc;
|
|
}
|
|
|
|
if (__raw_readl(sdcp->dcp_regs_base + HW_DCP_CAPABILITY1) &
|
|
BF_DCP_CAPABILITY1_HASH_ALGORITHMS(
|
|
BV_DCP_CAPABILITY1_HASH_ALGORITHMS__SHA256)) {
|
|
|
|
ret = crypto_register_shash(&dcp_sha256_alg);
|
|
if (ret != 0) {
|
|
dev_err(&pdev->dev, "Failed to register sha256 hash\n");
|
|
goto err_unregister_sha1;
|
|
}
|
|
}
|
|
|
|
/* register dcpboot interface to allow apps (such as kobs-ng) to
|
|
* verify files (such as the bootstream) using the OTP key for crypto */
|
|
ret = misc_register(&dcp_bootstream_misc);
|
|
if (ret != 0) {
|
|
dev_err(&pdev->dev, "Unable to register misc device\n");
|
|
goto err_unregister_sha1;
|
|
}
|
|
|
|
sdcp->dcpboot_dma_area = dma_alloc_coherent(&pdev->dev,
|
|
sizeof(*sdcp->dcpboot_dma_area), &sdcp->dcpboot_dma_area_phys,
|
|
GFP_KERNEL);
|
|
if (sdcp->dcpboot_dma_area == NULL) {
|
|
dev_err(&pdev->dev,
|
|
"Unable to allocate DMAable memory \
|
|
for dcpboot interface\n");
|
|
goto err_dereg;
|
|
}
|
|
|
|
dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
|
|
return 0;
|
|
|
|
err_dereg:
|
|
misc_deregister(&dcp_bootstream_misc);
|
|
err_unregister_sha1:
|
|
crypto_unregister_shash(&dcp_sha1_alg);
|
|
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_free_irq0:
|
|
free_irq(sdcp->dcp_vmi_irq, sdcp);
|
|
err_kfree:
|
|
kfree(sdcp);
|
|
err:
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dcp_remove(struct platform_device *pdev)
|
|
{
|
|
struct 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);
|
|
|
|
/* if head and tail buffers were allocated, free them */
|
|
if (sdcp->buf1) {
|
|
dma_free_coherent(sdcp->dev, 64, sdcp->buf1, sdcp->buf1_phys);
|
|
dma_free_coherent(sdcp->dev, 64, sdcp->buf2, sdcp->buf2_phys);
|
|
|
|
dma_free_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block),
|
|
sdcp->buf1_desc, sdcp->buf1_desc->my_phys);
|
|
|
|
dma_free_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block),
|
|
sdcp->buf2_desc, sdcp->buf2_desc->my_phys);
|
|
|
|
dma_free_coherent(sdcp->dev,
|
|
sizeof(struct dcp_hash_coherent_block),
|
|
sdcp->user_buf_desc, sdcp->user_buf_desc->my_phys);
|
|
}
|
|
|
|
if (sdcp->dcpboot_dma_area) {
|
|
dma_free_coherent(&pdev->dev, sizeof(*sdcp->dcpboot_dma_area),
|
|
sdcp->dcpboot_dma_area, sdcp->dcpboot_dma_area_phys);
|
|
misc_deregister(&dcp_bootstream_misc);
|
|
}
|
|
|
|
|
|
crypto_unregister_shash(&dcp_sha1_alg);
|
|
|
|
if (__raw_readl(sdcp->dcp_regs_base + HW_DCP_CAPABILITY1) &
|
|
BF_DCP_CAPABILITY1_HASH_ALGORITHMS(
|
|
BV_DCP_CAPABILITY1_HASH_ALGORITHMS__SHA256))
|
|
crypto_unregister_shash(&dcp_sha256_alg);
|
|
|
|
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 dcp_suspend(struct platform_device *pdev,
|
|
pm_message_t state)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int dcp_resume(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
#else
|
|
#define dcp_suspend NULL
|
|
#define dcp_resume NULL
|
|
#endif
|
|
|
|
static struct platform_driver dcp_driver = {
|
|
.probe = dcp_probe,
|
|
.remove = dcp_remove,
|
|
.suspend = dcp_suspend,
|
|
.resume = dcp_resume,
|
|
.driver = {
|
|
.name = "dcp",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init
|
|
dcp_init(void)
|
|
{
|
|
return platform_driver_register(&dcp_driver);
|
|
}
|
|
|
|
static void __exit
|
|
dcp_exit(void)
|
|
{
|
|
platform_driver_unregister(&dcp_driver);
|
|
}
|
|
|
|
MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
|
|
MODULE_DESCRIPTION("DCP Crypto Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
module_init(dcp_init);
|
|
module_exit(dcp_exit);
|