1302 lines
32 KiB
C
1302 lines
32 KiB
C
|
/*
|
||
|
* The driver of SUNXI SecuritySystem controller.
|
||
|
*
|
||
|
* Copyright (C) 2013 Allwinner.
|
||
|
*
|
||
|
* Mintow <duanmintao@allwinnertech.com>
|
||
|
*
|
||
|
* This file is licensed under the terms of the GNU General Public
|
||
|
* License version 2. This program is licensed "as is" without any
|
||
|
* warranty of any kind, whether express or implied.
|
||
|
*/
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/clk/sunxi.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <crypto/hash.h>
|
||
|
#include <crypto/md5.h>
|
||
|
#include <crypto/des.h>
|
||
|
#include <crypto/internal/hash.h>
|
||
|
#include <crypto/internal/rng.h>
|
||
|
|
||
|
#include <linux/dmaengine.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/dma/sunxi-dma.h>
|
||
|
|
||
|
#include "sunxi_ss.h"
|
||
|
#include "sunxi_ss_proc.h"
|
||
|
#include "sunxi_ss_reg.h"
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/of_address.h>
|
||
|
|
||
|
static const struct of_device_id sunxi_ss_of_match[] = {
|
||
|
{.compatible = "allwinner,sunxi-ce",},
|
||
|
{},
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, sunxi_ss_of_match);
|
||
|
#endif
|
||
|
|
||
|
sunxi_ss_t *ss_dev;
|
||
|
|
||
|
static DEFINE_MUTEX(ss_lock);
|
||
|
|
||
|
void ss_dev_lock(void)
|
||
|
{
|
||
|
mutex_lock(&ss_lock);
|
||
|
}
|
||
|
|
||
|
void ss_dev_unlock(void)
|
||
|
{
|
||
|
mutex_unlock(&ss_lock);
|
||
|
}
|
||
|
|
||
|
void __iomem *ss_membase(void)
|
||
|
{
|
||
|
return ss_dev->base_addr;
|
||
|
}
|
||
|
|
||
|
void ss_reset(void)
|
||
|
{
|
||
|
SS_ENTER();
|
||
|
sunxi_periph_reset_assert(ss_dev->mclk);
|
||
|
sunxi_periph_reset_deassert(ss_dev->mclk);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_RSA_CLK_ENABLE
|
||
|
void ss_clk_set(u32 rate)
|
||
|
{
|
||
|
#ifdef CONFIG_EVB_PLATFORM
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = clk_get_rate(ss_dev->mclk);
|
||
|
if (ret == rate)
|
||
|
return;
|
||
|
|
||
|
SS_DBG("Change the SS clk to %d MHz.\n", rate/1000000);
|
||
|
ret = clk_set_rate(ss_dev->mclk, rate);
|
||
|
if (ret != 0)
|
||
|
SS_ERR("clk_set_rate(%d) failed! return %d\n", rate, ret);
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int ss_aes_key_is_weak(const u8 *key, unsigned int keylen)
|
||
|
{
|
||
|
s32 i;
|
||
|
u8 tmp = key[0];
|
||
|
|
||
|
for (i = 0; i < keylen; i++)
|
||
|
if (tmp != key[i])
|
||
|
return 0;
|
||
|
|
||
|
SS_ERR("The key is weak!\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
|
||
|
unsigned int keylen)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
ss_aes_ctx_t *ctx = crypto_ablkcipher_ctx(tfm);
|
||
|
|
||
|
SS_DBG("keylen = %d\n", keylen);
|
||
|
if (ctx->comm.flags & SS_FLAG_NEW_KEY) {
|
||
|
SS_ERR("The key has already update.\n");
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
ret = ss_aes_key_valid(tfm, keylen);
|
||
|
if (ret != 0)
|
||
|
return ret;
|
||
|
|
||
|
if (ss_aes_key_is_weak(key, keylen)) {
|
||
|
crypto_ablkcipher_tfm(tfm)->crt_flags
|
||
|
|= CRYPTO_TFM_REQ_WEAK_KEY;
|
||
|
/* testmgr.c need this, but we don't want to support it. */
|
||
|
/* return -EINVAL; */
|
||
|
}
|
||
|
|
||
|
ctx->key_size = keylen;
|
||
|
memcpy(ctx->key, key, keylen);
|
||
|
if (keylen < AES_KEYSIZE_256)
|
||
|
memset(&ctx->key[keylen], 0, AES_KEYSIZE_256 - keylen);
|
||
|
|
||
|
ctx->comm.flags |= SS_FLAG_NEW_KEY;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int ss_aes_ecb_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_ecb_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cbc_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cbc_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_CTR_MODE_ENABLE
|
||
|
static int ss_aes_ctr_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CTR);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_ctr_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CTR);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_CTS_MODE_ENABLE
|
||
|
static int ss_aes_cts_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CTS);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cts_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CTS);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_XTS_MODE_ENABLE
|
||
|
static int ss_aes_xts_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_XTS);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_xts_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_XTS);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_OFB_MODE_ENABLE
|
||
|
static int ss_aes_ofb_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_OFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_ofb_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_OFB);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_CFB_MODE_ENABLE
|
||
|
static int ss_aes_cfb1_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 1;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb1_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 1;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb8_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 8;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb8_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 8;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb64_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 64;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb64_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 64;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb128_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 128;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
|
||
|
static int ss_aes_cfb128_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
req_ctx->bitwidth = 128;
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int ss_des_ecb_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_DES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_des_ecb_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_DES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_des_cbc_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_DES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
static int ss_des_cbc_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_DES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
static int ss_des3_ecb_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_3DES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_des3_ecb_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_3DES, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
|
||
|
static int ss_des3_cbc_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_3DES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
static int ss_des3_cbc_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_DECRYPT, SS_METHOD_3DES, SS_AES_MODE_CBC);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_RSA_ENABLE
|
||
|
static int ss_rsa_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_ENCRYPT,
|
||
|
SS_METHOD_RSA, CE_RSA_OP_M_EXP);
|
||
|
}
|
||
|
|
||
|
static int ss_rsa_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_DECRYPT,
|
||
|
SS_METHOD_RSA, CE_RSA_OP_M_EXP);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_DH_ENABLE
|
||
|
static int ss_dh_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_ENCRYPT,
|
||
|
SS_METHOD_DH, CE_RSA_OP_M_EXP);
|
||
|
}
|
||
|
|
||
|
static int ss_dh_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_DECRYPT,
|
||
|
SS_METHOD_DH, CE_RSA_OP_M_EXP);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_ECC_ENABLE
|
||
|
static int ss_ecdh_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_ECC,
|
||
|
CE_ECC_OP_POINT_MUL);
|
||
|
}
|
||
|
|
||
|
static int ss_ecdh_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_ECC,
|
||
|
CE_ECC_OP_POINT_MUL);
|
||
|
}
|
||
|
|
||
|
static int ss_ecc_sign_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_ECC, CE_ECC_OP_SIGN);
|
||
|
}
|
||
|
|
||
|
static int ss_ecc_sign_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_ECC, CE_ECC_OP_SIGN);
|
||
|
}
|
||
|
|
||
|
static int ss_ecc_verify_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_RSA,
|
||
|
CE_RSA_OP_M_MUL);
|
||
|
}
|
||
|
|
||
|
static int ss_ecc_verify_decrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_RSA,
|
||
|
CE_RSA_OP_M_MUL);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_HMAC_SHA1_ENABLE
|
||
|
static int ss_hmac_sha1_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_HMAC_SHA1, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_HMAC_SHA256_ENABLE
|
||
|
static int ss_hmac_sha256_encrypt(struct ablkcipher_request *req)
|
||
|
{
|
||
|
return ss_aes_crypt(req,
|
||
|
SS_DIR_ENCRYPT, SS_METHOD_HMAC_SHA256, SS_AES_MODE_ECB);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int ss_rng_reset(struct crypto_rng *tfm, const u8 *seed, u32 slen)
|
||
|
{
|
||
|
int len = slen > SS_PRNG_SEED_LEN ? SS_PRNG_SEED_LEN : slen;
|
||
|
ss_aes_ctx_t *ctx = crypto_rng_ctx(tfm);
|
||
|
|
||
|
SS_DBG("Seed len: %d/%d, flags = %#x\n", len, slen, ctx->comm.flags);
|
||
|
ctx->key_size = len;
|
||
|
memset(ctx->key, 0, SS_PRNG_SEED_LEN);
|
||
|
memcpy(ctx->key, seed, len);
|
||
|
ctx->comm.flags |= SS_FLAG_NEW_KEY;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int ss_flow_request(ss_comm_ctx_t *comm)
|
||
|
{
|
||
|
int i;
|
||
|
unsigned long flags = 0;
|
||
|
|
||
|
spin_lock_irqsave(&ss_dev->lock, flags);
|
||
|
for (i = 0; i < SS_FLOW_NUM; i++) {
|
||
|
if (ss_dev->flows[i].available == SS_FLOW_AVAILABLE) {
|
||
|
comm->flow = i;
|
||
|
ss_dev->flows[i].available = SS_FLOW_UNAVAILABLE;
|
||
|
SS_DBG("The flow %d is available.\n", i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
spin_unlock_irqrestore(&ss_dev->lock, flags);
|
||
|
|
||
|
if (i == SS_FLOW_NUM) {
|
||
|
SS_ERR("Failed to get an available flow.\n");
|
||
|
i = -1;
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
void ss_flow_release(ss_comm_ctx_t *comm)
|
||
|
{
|
||
|
unsigned long flags = 0;
|
||
|
|
||
|
spin_lock_irqsave(&ss_dev->lock, flags);
|
||
|
ss_dev->flows[comm->flow].available = SS_FLOW_AVAILABLE;
|
||
|
spin_unlock_irqrestore(&ss_dev->lock, flags);
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_cra_init(struct crypto_tfm *tfm)
|
||
|
{
|
||
|
if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
tfm->crt_ablkcipher.reqsize = sizeof(ss_aes_req_ctx_t);
|
||
|
SS_DBG("reqsize = %d\n", tfm->crt_u.ablkcipher.reqsize);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_cra_rng_init(struct crypto_tfm *tfm)
|
||
|
{
|
||
|
if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_cra_hash_init(struct crypto_tfm *tfm)
|
||
|
{
|
||
|
if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
|
||
|
sizeof(ss_aes_req_ctx_t));
|
||
|
|
||
|
SS_DBG("reqsize = %zu\n", sizeof(ss_aes_req_ctx_t));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void sunxi_ss_cra_exit(struct crypto_tfm *tfm)
|
||
|
{
|
||
|
SS_ENTER();
|
||
|
ss_flow_release(crypto_tfm_ctx(tfm));
|
||
|
/* sun8iw6 and sun9iw1 need reset SS controller after each operation. */
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
ss_reset();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int ss_hash_init(struct ahash_request *req, int type, int size, char *iv)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ahash_request_ctx(req);
|
||
|
ss_hash_ctx_t *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
||
|
|
||
|
SS_DBG("Method: %d\n", type);
|
||
|
|
||
|
memset(req_ctx, 0, sizeof(ss_aes_req_ctx_t));
|
||
|
req_ctx->type = type;
|
||
|
|
||
|
ctx->md_size = size;
|
||
|
memcpy(ctx->md, iv, size);
|
||
|
|
||
|
ctx->cnt = 0;
|
||
|
memset(ctx->pad, 0, SS_HASH_PAD_SIZE);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int ss_md5_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[MD5_DIGEST_SIZE/4] = {SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3};
|
||
|
|
||
|
return ss_hash_init(req, SS_METHOD_MD5, MD5_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
|
||
|
static int ss_sha1_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[SHA1_DIGEST_SIZE/4] = {
|
||
|
SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4};
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_PRE_ENABLE
|
||
|
#ifdef SS_SHA_NO_SWAP_IV4
|
||
|
ss_hash_swap((char *)iv, SHA1_DIGEST_SIZE - 4);
|
||
|
#else
|
||
|
ss_hash_swap((char *)iv, SHA1_DIGEST_SIZE);
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
return ss_hash_init(req, SS_METHOD_SHA1, SHA1_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_SHA224_ENABLE
|
||
|
static int ss_sha224_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[SHA256_DIGEST_SIZE/4] = {
|
||
|
SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3,
|
||
|
SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7};
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_PRE_ENABLE
|
||
|
ss_hash_swap((char *)iv, SHA256_DIGEST_SIZE);
|
||
|
#endif
|
||
|
|
||
|
return ss_hash_init(req,
|
||
|
SS_METHOD_SHA224, SHA256_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_SHA256_ENABLE
|
||
|
static int ss_sha256_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[SHA256_DIGEST_SIZE/4] = {
|
||
|
SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3,
|
||
|
SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7};
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_PRE_ENABLE
|
||
|
ss_hash_swap((char *)iv, SHA256_DIGEST_SIZE);
|
||
|
#endif
|
||
|
|
||
|
return ss_hash_init(req,
|
||
|
SS_METHOD_SHA256, SHA256_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#define GET_U64_HIGH(data64) (int)(data64 >> 32)
|
||
|
#define GET_U64_LOW(data64) (int)(data64 & 0xFFFFFFFF)
|
||
|
|
||
|
#ifdef SS_SHA384_ENABLE
|
||
|
static int ss_sha384_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[SHA512_DIGEST_SIZE/4] = {
|
||
|
GET_U64_HIGH(SHA384_H0), GET_U64_LOW(SHA384_H0),
|
||
|
GET_U64_HIGH(SHA384_H1), GET_U64_LOW(SHA384_H1),
|
||
|
GET_U64_HIGH(SHA384_H2), GET_U64_LOW(SHA384_H2),
|
||
|
GET_U64_HIGH(SHA384_H3), GET_U64_LOW(SHA384_H3),
|
||
|
GET_U64_HIGH(SHA384_H4), GET_U64_LOW(SHA384_H4),
|
||
|
GET_U64_HIGH(SHA384_H5), GET_U64_LOW(SHA384_H5),
|
||
|
GET_U64_HIGH(SHA384_H6), GET_U64_LOW(SHA384_H6),
|
||
|
GET_U64_HIGH(SHA384_H7), GET_U64_LOW(SHA384_H7)};
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_PRE_ENABLE
|
||
|
ss_hash_swap((char *)iv, SHA512_DIGEST_SIZE);
|
||
|
#endif
|
||
|
|
||
|
return ss_hash_init(req,
|
||
|
SS_METHOD_SHA384, SHA512_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_SHA512_ENABLE
|
||
|
static int ss_sha512_init(struct ahash_request *req)
|
||
|
{
|
||
|
int iv[SHA512_DIGEST_SIZE/4] = {
|
||
|
GET_U64_HIGH(SHA512_H0), GET_U64_LOW(SHA512_H0),
|
||
|
GET_U64_HIGH(SHA512_H1), GET_U64_LOW(SHA512_H1),
|
||
|
GET_U64_HIGH(SHA512_H2), GET_U64_LOW(SHA512_H2),
|
||
|
GET_U64_HIGH(SHA512_H3), GET_U64_LOW(SHA512_H3),
|
||
|
GET_U64_HIGH(SHA512_H4), GET_U64_LOW(SHA512_H4),
|
||
|
GET_U64_HIGH(SHA512_H5), GET_U64_LOW(SHA512_H5),
|
||
|
GET_U64_HIGH(SHA512_H6), GET_U64_LOW(SHA512_H6),
|
||
|
GET_U64_HIGH(SHA512_H7), GET_U64_LOW(SHA512_H7)};
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_PRE_ENABLE
|
||
|
ss_hash_swap((char *)iv, SHA512_DIGEST_SIZE);
|
||
|
#endif
|
||
|
|
||
|
return ss_hash_init(req,
|
||
|
SS_METHOD_SHA512, SHA512_DIGEST_SIZE, (char *)iv);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#define DES_MIN_KEY_SIZE DES_KEY_SIZE
|
||
|
#define DES_MAX_KEY_SIZE DES_KEY_SIZE
|
||
|
#define DES3_MIN_KEY_SIZE DES3_EDE_KEY_SIZE
|
||
|
#define DES3_MAX_KEY_SIZE DES3_EDE_KEY_SIZE
|
||
|
|
||
|
#define DECLARE_SS_AES_ALG(utype, ltype, lmode, block_size, iv_size) \
|
||
|
{ \
|
||
|
.cra_name = #lmode"("#ltype")", \
|
||
|
.cra_driver_name = "ss-"#lmode"-"#ltype, \
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \
|
||
|
.cra_type = &crypto_ablkcipher_type, \
|
||
|
.cra_blocksize = block_size, \
|
||
|
.cra_alignmask = 3, \
|
||
|
.cra_u.ablkcipher = { \
|
||
|
.setkey = ss_aes_setkey, \
|
||
|
.encrypt = ss_##ltype##_##lmode##_encrypt, \
|
||
|
.decrypt = ss_##ltype##_##lmode##_decrypt, \
|
||
|
.min_keysize = utype##_MIN_KEY_SIZE, \
|
||
|
.max_keysize = utype##_MAX_KEY_SIZE, \
|
||
|
.ivsize = iv_size, \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
#ifdef SS_XTS_MODE_ENABLE
|
||
|
#define DECLARE_SS_AES_XTS_ALG(utype, ltype, lmode, block_size, iv_size) \
|
||
|
{ \
|
||
|
.cra_name = #lmode"("#ltype")", \
|
||
|
.cra_driver_name = "ss-"#lmode"-"#ltype, \
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \
|
||
|
.cra_type = &crypto_ablkcipher_type, \
|
||
|
.cra_blocksize = block_size, \
|
||
|
.cra_alignmask = 3, \
|
||
|
.cra_u.ablkcipher = { \
|
||
|
.setkey = ss_aes_setkey, \
|
||
|
.encrypt = ss_##ltype##_##lmode##_encrypt, \
|
||
|
.decrypt = ss_##ltype##_##lmode##_decrypt, \
|
||
|
.min_keysize = utype##_MAX_KEY_SIZE, \
|
||
|
.max_keysize = utype##_MAX_KEY_SIZE * 2, \
|
||
|
.ivsize = iv_size, \
|
||
|
} \
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#define DECLARE_SS_ASYM_ALG(type, bitwidth, key_size, iv_size) \
|
||
|
{ \
|
||
|
.cra_name = #type"("#bitwidth")", \
|
||
|
.cra_driver_name = "ss-"#type"-"#bitwidth, \
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \
|
||
|
.cra_type = &crypto_ablkcipher_type, \
|
||
|
.cra_blocksize = key_size%AES_BLOCK_SIZE == 0 ? AES_BLOCK_SIZE : 4, \
|
||
|
.cra_alignmask = key_size%AES_BLOCK_SIZE == 0 ? 31 : 3, \
|
||
|
.cra_u.ablkcipher = { \
|
||
|
.setkey = ss_aes_setkey, \
|
||
|
.encrypt = ss_##type##_encrypt, \
|
||
|
.decrypt = ss_##type##_decrypt, \
|
||
|
.min_keysize = key_size, \
|
||
|
.max_keysize = key_size, \
|
||
|
.ivsize = iv_size, \
|
||
|
}, \
|
||
|
}
|
||
|
#ifndef SS_SUPPORT_CE_V3_2
|
||
|
#define DECLARE_SS_RSA_ALG(type, bitwidth) \
|
||
|
DECLARE_SS_ASYM_ALG(type, bitwidth, (bitwidth/8), (bitwidth/8))
|
||
|
#else
|
||
|
#define DECLARE_SS_RSA_ALG(type, bitwidth) \
|
||
|
DECLARE_SS_ASYM_ALG(type, bitwidth, (bitwidth/8), 0)
|
||
|
#endif
|
||
|
#define DECLARE_SS_DH_ALG(type, bitwidth) DECLARE_SS_RSA_ALG(type, bitwidth)
|
||
|
|
||
|
|
||
|
static struct crypto_alg sunxi_ss_algs[] = {
|
||
|
DECLARE_SS_AES_ALG(AES, aes, ecb, AES_BLOCK_SIZE, 0),
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cbc, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
#ifdef SS_CTR_MODE_ENABLE
|
||
|
DECLARE_SS_AES_ALG(AES, aes, ctr, 1, AES_MIN_KEY_SIZE),
|
||
|
#endif
|
||
|
#ifdef SS_CTS_MODE_ENABLE
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cts, 1, AES_MIN_KEY_SIZE),
|
||
|
#endif
|
||
|
#ifdef SS_XTS_MODE_ENABLE
|
||
|
DECLARE_SS_AES_XTS_ALG(AES, aes, xts, 1, AES_MIN_KEY_SIZE),
|
||
|
#endif
|
||
|
#ifdef SS_OFB_MODE_ENABLE
|
||
|
DECLARE_SS_AES_ALG(AES, aes, ofb, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
#endif
|
||
|
#ifdef SS_CFB_MODE_ENABLE
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cfb1, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cfb8, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cfb64, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
DECLARE_SS_AES_ALG(AES, aes, cfb128, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE),
|
||
|
#endif
|
||
|
DECLARE_SS_AES_ALG(DES, des, ecb, DES_BLOCK_SIZE, 0),
|
||
|
DECLARE_SS_AES_ALG(DES, des, cbc, DES_BLOCK_SIZE, DES_KEY_SIZE),
|
||
|
DECLARE_SS_AES_ALG(DES3, des3, ecb, DES3_EDE_BLOCK_SIZE, 0),
|
||
|
DECLARE_SS_AES_ALG(DES3, des3, cbc, DES3_EDE_BLOCK_SIZE, DES_KEY_SIZE),
|
||
|
#ifdef SS_RSA512_ENABLE
|
||
|
DECLARE_SS_RSA_ALG(rsa, 512),
|
||
|
#endif
|
||
|
#ifdef SS_RSA1024_ENABLE
|
||
|
DECLARE_SS_RSA_ALG(rsa, 1024),
|
||
|
#endif
|
||
|
#ifdef SS_RSA2048_ENABLE
|
||
|
DECLARE_SS_RSA_ALG(rsa, 2048),
|
||
|
#endif
|
||
|
#ifdef SS_RSA3072_ENABLE
|
||
|
DECLARE_SS_RSA_ALG(rsa, 3072),
|
||
|
#endif
|
||
|
#ifdef SS_RSA4096_ENABLE
|
||
|
DECLARE_SS_RSA_ALG(rsa, 4096),
|
||
|
#endif
|
||
|
#ifdef SS_DH512_ENABLE
|
||
|
DECLARE_SS_DH_ALG(dh, 512),
|
||
|
#endif
|
||
|
#ifdef SS_DH1024_ENABLE
|
||
|
DECLARE_SS_DH_ALG(dh, 1024),
|
||
|
#endif
|
||
|
#ifdef SS_DH2048_ENABLE
|
||
|
DECLARE_SS_DH_ALG(dh, 2048),
|
||
|
#endif
|
||
|
#ifdef SS_DH3072_ENABLE
|
||
|
DECLARE_SS_DH_ALG(dh, 3072),
|
||
|
#endif
|
||
|
#ifdef SS_DH4096_ENABLE
|
||
|
DECLARE_SS_DH_ALG(dh, 4096),
|
||
|
#endif
|
||
|
#ifdef SS_ECC_ENABLE
|
||
|
#ifndef SS_SUPPORT_CE_V3_2
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 160, 160/8, 160/8),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 224, 224/8, 224/8),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 256, 256/8, 256/8),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 521, ((521+31)/32)*4, ((521+31)/32)*4),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 160, 160/8, (160/8)*2),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 224, 224/8, (224/8)*2),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 256, 256/8, (256/8)*2),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 521, ((521+31)/32)*4, ((521+31)/32)*4*2),
|
||
|
#else
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 160, 160/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 224, 224/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 256, 256/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecdh, 521, ((521+31)/32)*4, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 160, 160/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 224, 224/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 256, 256/8, 0),
|
||
|
DECLARE_SS_ASYM_ALG(ecc_sign, 521, ((521+31)/32)*4, 0),
|
||
|
#endif
|
||
|
DECLARE_SS_RSA_ALG(ecc_verify, 512),
|
||
|
DECLARE_SS_RSA_ALG(ecc_verify, 1024),
|
||
|
#endif
|
||
|
|
||
|
#ifdef SS_HMAC_SHA1_ENABLE
|
||
|
{
|
||
|
.cra_name = "hmac-sha1",
|
||
|
.cra_driver_name = "ss-hmac-sha1",
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
|
||
|
.cra_type = &crypto_ablkcipher_type,
|
||
|
.cra_blocksize = 4,
|
||
|
.cra_alignmask = 3,
|
||
|
.cra_u.ablkcipher = {
|
||
|
.setkey = ss_aes_setkey,
|
||
|
.encrypt = ss_hmac_sha1_encrypt,
|
||
|
.decrypt = NULL,
|
||
|
.min_keysize = SHA1_BLOCK_SIZE,
|
||
|
.max_keysize = SHA1_BLOCK_SIZE,
|
||
|
.ivsize = 0,
|
||
|
}
|
||
|
},
|
||
|
#endif
|
||
|
#ifdef SS_HMAC_SHA256_ENABLE
|
||
|
{
|
||
|
.cra_name = "hmac-sha256",
|
||
|
.cra_driver_name = "ss-hmac-sha256",
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
|
||
|
.cra_type = &crypto_ablkcipher_type,
|
||
|
.cra_blocksize = 4,
|
||
|
.cra_alignmask = 3,
|
||
|
.cra_u.ablkcipher = {
|
||
|
.setkey = ss_aes_setkey,
|
||
|
.encrypt = ss_hmac_sha256_encrypt,
|
||
|
.decrypt = NULL,
|
||
|
.min_keysize = SHA256_BLOCK_SIZE,
|
||
|
.max_keysize = SHA256_BLOCK_SIZE,
|
||
|
.ivsize = 0,
|
||
|
}
|
||
|
},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
#define DECLARE_SS_RNG_ALG(ltype) \
|
||
|
{ \
|
||
|
.generate = ss_##ltype##_get_random, \
|
||
|
.seed = ss_rng_reset, \
|
||
|
.seedsize = SS_SEED_SIZE, \
|
||
|
.base = { \
|
||
|
.cra_name = #ltype, \
|
||
|
.cra_driver_name = "ss-"#ltype, \
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_RNG, \
|
||
|
.cra_priority = SS_ALG_PRIORITY, \
|
||
|
.cra_ctxsize = sizeof(ss_aes_ctx_t), \
|
||
|
.cra_module = THIS_MODULE, \
|
||
|
.cra_init = sunxi_ss_cra_rng_init, \
|
||
|
.cra_exit = sunxi_ss_cra_exit, \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
static struct rng_alg sunxi_ss_algs_rng[] = {
|
||
|
#ifdef SS_TRNG_ENABLE
|
||
|
DECLARE_SS_RNG_ALG(trng),
|
||
|
#endif
|
||
|
DECLARE_SS_RNG_ALG(prng),
|
||
|
};
|
||
|
|
||
|
#define MD5_BLOCK_SIZE MD5_HMAC_BLOCK_SIZE
|
||
|
#define sha224_state sha256_state
|
||
|
#define sha384_state sha512_state
|
||
|
#define DECLARE_SS_AHASH_ALG(ltype, utype) \
|
||
|
{ \
|
||
|
.init = ss_##ltype##_init, \
|
||
|
.update = ss_hash_update, \
|
||
|
.final = ss_hash_final, \
|
||
|
.finup = ss_hash_finup, \
|
||
|
.digest = ss_hash_digest, \
|
||
|
.halg = { \
|
||
|
.digestsize = utype##_DIGEST_SIZE, \
|
||
|
.statesize = sizeof(struct ltype##_state), \
|
||
|
.base = { \
|
||
|
.cra_name = #ltype, \
|
||
|
.cra_driver_name = "ss-"#ltype, \
|
||
|
.cra_priority = SS_ALG_PRIORITY, \
|
||
|
.cra_flags = CRYPTO_ALG_TYPE_AHASH|CRYPTO_ALG_ASYNC, \
|
||
|
.cra_blocksize = utype##_BLOCK_SIZE, \
|
||
|
.cra_ctxsize = sizeof(ss_hash_ctx_t), \
|
||
|
.cra_alignmask = 3, \
|
||
|
.cra_module = THIS_MODULE, \
|
||
|
.cra_init = sunxi_ss_cra_hash_init, \
|
||
|
.cra_exit = sunxi_ss_cra_exit, \
|
||
|
} \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
static struct ahash_alg sunxi_ss_algs_hash[] = {
|
||
|
DECLARE_SS_AHASH_ALG(md5, MD5),
|
||
|
DECLARE_SS_AHASH_ALG(sha1, SHA1),
|
||
|
#ifdef SS_SHA224_ENABLE
|
||
|
DECLARE_SS_AHASH_ALG(sha224, SHA224),
|
||
|
#endif
|
||
|
#ifdef SS_SHA256_ENABLE
|
||
|
DECLARE_SS_AHASH_ALG(sha256, SHA256),
|
||
|
#endif
|
||
|
#ifdef SS_SHA384_ENABLE
|
||
|
DECLARE_SS_AHASH_ALG(sha384, SHA384),
|
||
|
#endif
|
||
|
#ifdef SS_SHA512_ENABLE
|
||
|
DECLARE_SS_AHASH_ALG(sha512, SHA512),
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
/* Requeset the resource: IRQ, mem */
|
||
|
static int sunxi_ss_res_request(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct device_node *pnode = pdev->dev.of_node;
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < SS_FLOW_NUM; i++) {
|
||
|
sss->flows[i].buf_src = kmalloc(SS_DMA_BUF_SIZE, GFP_KERNEL);
|
||
|
if (sss->flows[i].buf_src == NULL) {
|
||
|
SS_ERR("Can not allocate DMA source buffer\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
sss->flows[i].buf_src_dma = virt_to_phys(sss->flows[i].buf_src);
|
||
|
|
||
|
sss->flows[i].buf_dst = kmalloc(SS_DMA_BUF_SIZE, GFP_KERNEL);
|
||
|
if (sss->flows[i].buf_dst == NULL) {
|
||
|
SS_ERR("Can not allocate DMA source buffer\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
sss->flows[i].buf_dst_dma = virt_to_phys(sss->flows[i].buf_dst);
|
||
|
init_completion(&sss->flows[i].done);
|
||
|
}
|
||
|
#endif
|
||
|
sss->irq = irq_of_parse_and_map(pnode, SS_RES_INDEX);
|
||
|
if (sss->irq == 0) {
|
||
|
SS_ERR("Failed to get the SS IRQ.\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = request_irq(sss->irq,
|
||
|
sunxi_ss_irq_handler, 0, sss->dev_name, sss);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("Cannot request IRQ\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
sss->base_addr = of_iomap(pnode, SS_RES_INDEX);
|
||
|
if (sss->base_addr == NULL) {
|
||
|
SS_ERR("Unable to remap IO\n");
|
||
|
return -ENXIO;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Release the resource: IRQ, mem */
|
||
|
static int sunxi_ss_res_release(sunxi_ss_t *sss)
|
||
|
{
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
int i;
|
||
|
#endif
|
||
|
|
||
|
iounmap(sss->base_addr);
|
||
|
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
for (i = 0; i < SS_FLOW_NUM; i++) {
|
||
|
kfree(sss->flows[i].buf_src);
|
||
|
kfree(sss->flows[i].buf_dst);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
free_irq(sss->irq, sss);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int sunxi_ss_hw_init(sunxi_ss_t *sss)
|
||
|
{
|
||
|
#ifdef CONFIG_EVB_PLATFORM
|
||
|
int ret = 0;
|
||
|
#endif
|
||
|
struct clk *pclk = NULL;
|
||
|
struct device_node *pnode = sss->pdev->dev.of_node;
|
||
|
|
||
|
pclk = of_clk_get(pnode, 1);
|
||
|
if (IS_ERR_OR_NULL(pclk)) {
|
||
|
SS_ERR("Unable to get pll clock, return %x\n", PTR_RET(pclk));
|
||
|
return PTR_RET(pclk);
|
||
|
}
|
||
|
|
||
|
sss->mclk = of_clk_get(pnode, 0);
|
||
|
if (IS_ERR_OR_NULL(sss->mclk)) {
|
||
|
SS_ERR("Fail to get module clk, ret %x\n", PTR_RET(sss->mclk));
|
||
|
return PTR_RET(sss->mclk);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_RSA_CLK_ENABLE
|
||
|
if (of_property_read_u32_array(pnode, "clock-frequency",
|
||
|
&sss->gen_clkrate, 2)) {
|
||
|
#else
|
||
|
if (of_property_read_u32(pnode, "clock-frequency", &sss->gen_clkrate)) {
|
||
|
#endif
|
||
|
SS_ERR("Unable to get clock-frequency.\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
SS_DBG("The clk freq: %d, %d\n", sss->gen_clkrate, sss->rsa_clkrate);
|
||
|
|
||
|
#ifdef CONFIG_EVB_PLATFORM
|
||
|
ret = clk_set_parent(sss->mclk, pclk);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("clk_set_parent() failed! return %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = clk_set_rate(sss->mclk, sss->gen_clkrate);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("Set rate(%d) failed! ret %d\n", sss->gen_clkrate, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
SS_DBG("SS mclk %luMHz, pclk %luMHz\n", clk_get_rate(sss->mclk)/1000000,
|
||
|
clk_get_rate(pclk)/1000000);
|
||
|
|
||
|
if (clk_prepare_enable(sss->mclk)) {
|
||
|
SS_ERR("Couldn't enable module clock\n");
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
clk_put(pclk);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_hw_exit(sunxi_ss_t *sss)
|
||
|
{
|
||
|
clk_disable_unprepare(sss->mclk);
|
||
|
clk_put(sss->mclk);
|
||
|
sss->mclk = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_alg_register(void)
|
||
|
{
|
||
|
int i;
|
||
|
int ret = 0;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs); i++) {
|
||
|
INIT_LIST_HEAD(&sunxi_ss_algs[i].cra_list);
|
||
|
SS_DBG("Add %s...\n", sunxi_ss_algs[i].cra_name);
|
||
|
sunxi_ss_algs[i].cra_priority = SS_ALG_PRIORITY;
|
||
|
sunxi_ss_algs[i].cra_ctxsize = sizeof(ss_aes_ctx_t);
|
||
|
sunxi_ss_algs[i].cra_module = THIS_MODULE;
|
||
|
sunxi_ss_algs[i].cra_exit = sunxi_ss_cra_exit;
|
||
|
sunxi_ss_algs[i].cra_init = sunxi_ss_cra_init;
|
||
|
|
||
|
ret = crypto_register_alg(&sunxi_ss_algs[i]);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("crypto_register_alg(%s) failed! return %d\n",
|
||
|
sunxi_ss_algs[i].cra_name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_rng); i++) {
|
||
|
SS_DBG("Add %s...\n", sunxi_ss_algs_rng[i].base.cra_name);
|
||
|
ret = crypto_register_rng(&sunxi_ss_algs_rng[i]);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("crypto_register_rng(%s) failed! return %d\n",
|
||
|
sunxi_ss_algs_rng[i].base.cra_name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_hash); i++) {
|
||
|
SS_DBG("Add %s...\n", sunxi_ss_algs_hash[i].halg.base.cra_name);
|
||
|
ret = crypto_register_ahash(&sunxi_ss_algs_hash[i]);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("crypto_register_ahash(%s) failed! return %d\n",
|
||
|
sunxi_ss_algs_hash[i].halg.base.cra_name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void sunxi_ss_alg_unregister(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs); i++)
|
||
|
crypto_unregister_alg(&sunxi_ss_algs[i]);
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_rng); i++)
|
||
|
crypto_unregister_rng(&sunxi_ss_algs_rng[i]);
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_hash); i++)
|
||
|
crypto_unregister_ahash(&sunxi_ss_algs_hash[i]);
|
||
|
}
|
||
|
|
||
|
static ssize_t sunxi_ss_info_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct platform_device *pdev =
|
||
|
container_of(dev, struct platform_device, dev);
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"pdev->id = %d\n"
|
||
|
"pdev->name = %s\n"
|
||
|
"pdev->num_resources = %u\n"
|
||
|
"pdev->resource.irq = %d\n"
|
||
|
"SS module clk rate = %ld Mhz\n"
|
||
|
"IO membase = 0x%p\n",
|
||
|
pdev->id, pdev->name, pdev->num_resources,
|
||
|
sss->irq,
|
||
|
(clk_get_rate(sss->mclk)/1000000), sss->base_addr);
|
||
|
}
|
||
|
static struct device_attribute sunxi_ss_info_attr =
|
||
|
__ATTR(info, S_IRUGO, sunxi_ss_info_show, NULL);
|
||
|
|
||
|
static ssize_t sunxi_ss_status_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int i;
|
||
|
struct platform_device *pdev =
|
||
|
container_of(dev, struct platform_device, dev);
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
static char const *avail[] = {"Available", "Unavailable"};
|
||
|
|
||
|
if (sss == NULL)
|
||
|
return snprintf(buf, PAGE_SIZE, "%s\n", "sunxi_ss is NULL!");
|
||
|
|
||
|
buf[0] = 0;
|
||
|
for (i = 0; i < SS_FLOW_NUM; i++) {
|
||
|
snprintf(buf+strlen(buf), PAGE_SIZE-strlen(buf),
|
||
|
"The flow %d state: %s\n"
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
" Src: 0x%p / 0x%08x\n"
|
||
|
" Dst: 0x%p / 0x%08x\n"
|
||
|
#endif
|
||
|
, i, avail[sss->flows[i].available]
|
||
|
#ifdef SS_IDMA_ENABLE
|
||
|
, sss->flows[i].buf_src, sss->flows[i].buf_src_dma
|
||
|
, sss->flows[i].buf_dst, sss->flows[i].buf_dst_dma
|
||
|
#endif
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return strlen(buf)
|
||
|
+ ss_reg_print(buf + strlen(buf), PAGE_SIZE - strlen(buf));
|
||
|
}
|
||
|
|
||
|
static struct device_attribute sunxi_ss_status_attr =
|
||
|
__ATTR(status, S_IRUGO, sunxi_ss_status_show, NULL);
|
||
|
|
||
|
static void sunxi_ss_sysfs_create(struct platform_device *_pdev)
|
||
|
{
|
||
|
device_create_file(&_pdev->dev, &sunxi_ss_info_attr);
|
||
|
device_create_file(&_pdev->dev, &sunxi_ss_status_attr);
|
||
|
}
|
||
|
|
||
|
static void sunxi_ss_sysfs_remove(struct platform_device *_pdev)
|
||
|
{
|
||
|
device_remove_file(&_pdev->dev, &sunxi_ss_info_attr);
|
||
|
device_remove_file(&_pdev->dev, &sunxi_ss_status_attr);
|
||
|
}
|
||
|
|
||
|
static u64 sunxi_ss_dma_mask = DMA_BIT_MASK(32);
|
||
|
|
||
|
static int sunxi_ss_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
sunxi_ss_t *sss = NULL;
|
||
|
|
||
|
sss = devm_kzalloc(&pdev->dev, sizeof(sunxi_ss_t), GFP_KERNEL);
|
||
|
if (sss == NULL) {
|
||
|
SS_ERR("Unable to allocate sunxi_ss_t\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
pdev->dev.dma_mask = &sunxi_ss_dma_mask;
|
||
|
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||
|
#endif
|
||
|
|
||
|
snprintf(sss->dev_name, sizeof(sss->dev_name), SUNXI_SS_DEV_NAME);
|
||
|
platform_set_drvdata(pdev, sss);
|
||
|
|
||
|
ret = sunxi_ss_res_request(pdev);
|
||
|
if (ret != 0)
|
||
|
goto err0;
|
||
|
|
||
|
sss->pdev = pdev;
|
||
|
|
||
|
ret = sunxi_ss_hw_init(sss);
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("SS hw init failed!\n");
|
||
|
goto err1;
|
||
|
}
|
||
|
|
||
|
ss_dev = sss;
|
||
|
ret = sunxi_ss_alg_register();
|
||
|
if (ret != 0) {
|
||
|
SS_ERR("sunxi_ss_alg_register() failed! return %d\n", ret);
|
||
|
goto err2;
|
||
|
}
|
||
|
|
||
|
sunxi_ss_sysfs_create(pdev);
|
||
|
|
||
|
SS_DBG("SS is inited, base 0x%p, irq %d!\n", sss->base_addr, sss->irq);
|
||
|
return 0;
|
||
|
|
||
|
err2:
|
||
|
sunxi_ss_hw_exit(sss);
|
||
|
err1:
|
||
|
sunxi_ss_res_release(sss);
|
||
|
err0:
|
||
|
platform_set_drvdata(pdev, NULL);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
|
||
|
ss_wait_idle();
|
||
|
sunxi_ss_sysfs_remove(pdev);
|
||
|
|
||
|
sunxi_ss_alg_unregister();
|
||
|
sunxi_ss_hw_exit(sss);
|
||
|
sunxi_ss_res_release(sss);
|
||
|
|
||
|
platform_set_drvdata(pdev, NULL);
|
||
|
ss_dev = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_PM
|
||
|
static int sunxi_ss_suspend(struct device *dev)
|
||
|
{
|
||
|
#ifdef CONFIG_EVB_PLATFORM
|
||
|
struct platform_device *pdev = to_platform_device(dev);
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
unsigned long flags = 0;
|
||
|
|
||
|
SS_ENTER();
|
||
|
|
||
|
/* Wait for the completion of SS operation. */
|
||
|
ss_dev_lock();
|
||
|
|
||
|
spin_lock_irqsave(&ss_dev->lock, flags);
|
||
|
sss->suspend = 1;
|
||
|
spin_unlock_irqrestore(&sss->lock, flags);
|
||
|
|
||
|
sunxi_ss_hw_exit(sss);
|
||
|
ss_dev_unlock();
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_ss_resume(struct device *dev)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
#ifdef CONFIG_EVB_PLATFORM
|
||
|
struct platform_device *pdev = to_platform_device(dev);
|
||
|
sunxi_ss_t *sss = platform_get_drvdata(pdev);
|
||
|
unsigned long flags = 0;
|
||
|
|
||
|
SS_ENTER();
|
||
|
ret = sunxi_ss_hw_init(sss);
|
||
|
spin_lock_irqsave(&ss_dev->lock, flags);
|
||
|
sss->suspend = 0;
|
||
|
spin_unlock_irqrestore(&sss->lock, flags);
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct dev_pm_ops sunxi_ss_dev_pm_ops = {
|
||
|
.suspend = sunxi_ss_suspend,
|
||
|
.resume = sunxi_ss_resume,
|
||
|
};
|
||
|
|
||
|
#define SUNXI_SS_DEV_PM_OPS (&sunxi_ss_dev_pm_ops)
|
||
|
#else
|
||
|
#define SUNXI_SS_DEV_PM_OPS NULL
|
||
|
#endif /* CONFIG_PM */
|
||
|
|
||
|
static struct platform_driver sunxi_ss_driver = {
|
||
|
.probe = sunxi_ss_probe,
|
||
|
.remove = sunxi_ss_remove,
|
||
|
.driver = {
|
||
|
.name = SUNXI_SS_DEV_NAME,
|
||
|
.owner = THIS_MODULE,
|
||
|
.pm = SUNXI_SS_DEV_PM_OPS,
|
||
|
.of_match_table = sunxi_ss_of_match,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int __init sunxi_ss_init(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = platform_driver_register(&sunxi_ss_driver);
|
||
|
if (ret < 0) {
|
||
|
SS_ERR("platform_driver_register() failed, return %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void __exit sunxi_ss_exit(void)
|
||
|
{
|
||
|
platform_driver_unregister(&sunxi_ss_driver);
|
||
|
}
|
||
|
|
||
|
module_init(sunxi_ss_init);
|
||
|
module_exit(sunxi_ss_exit);
|
||
|
|
||
|
MODULE_AUTHOR("mintow");
|
||
|
MODULE_DESCRIPTION("SUNXI SS Controller Driver");
|
||
|
MODULE_ALIAS("platform:"SUNXI_SS_DEV_NAME);
|
||
|
MODULE_LICENSE("GPL");
|