Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rk_crypto.o
rk_crypto-objs := rk3288_crypto.o \
rk3288_crypto_skcipher.o \
rk3288_crypto_ahash.o
@@ -0,0 +1,448 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Crypto acceleration support for Rockchip RK3288
*
* Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Zain Wang <zain.wang@rock-chips.com>
*
* Some ideas are from marvell-cesa.c and s5p-sss.c driver.
*/
#include "rk3288_crypto.h"
#include <crypto/engine.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
static struct rockchip_ip rocklist = {
.dev_list = LIST_HEAD_INIT(rocklist.dev_list),
.lock = __SPIN_LOCK_UNLOCKED(rocklist.lock),
};
struct rk_crypto_info *get_rk_crypto(void)
{
struct rk_crypto_info *first;
spin_lock(&rocklist.lock);
first = list_first_entry_or_null(&rocklist.dev_list,
struct rk_crypto_info, list);
list_rotate_left(&rocklist.dev_list);
spin_unlock(&rocklist.lock);
return first;
}
static const struct rk_variant rk3288_variant = {
.num_clks = 4,
.rkclks = {
{ "sclk", 150000000},
}
};
static const struct rk_variant rk3328_variant = {
.num_clks = 3,
};
static const struct rk_variant rk3399_variant = {
.num_clks = 3,
};
static int rk_crypto_get_clks(struct rk_crypto_info *dev)
{
int i, j, err;
unsigned long cr;
dev->num_clks = devm_clk_bulk_get_all(dev->dev, &dev->clks);
if (dev->num_clks < dev->variant->num_clks) {
dev_err(dev->dev, "Missing clocks, got %d instead of %d\n",
dev->num_clks, dev->variant->num_clks);
return -EINVAL;
}
for (i = 0; i < dev->num_clks; i++) {
cr = clk_get_rate(dev->clks[i].clk);
for (j = 0; j < ARRAY_SIZE(dev->variant->rkclks); j++) {
if (dev->variant->rkclks[j].max == 0)
continue;
if (strcmp(dev->variant->rkclks[j].name, dev->clks[i].id))
continue;
if (cr > dev->variant->rkclks[j].max) {
err = clk_set_rate(dev->clks[i].clk,
dev->variant->rkclks[j].max);
if (err)
dev_err(dev->dev, "Fail downclocking %s from %lu to %lu\n",
dev->variant->rkclks[j].name, cr,
dev->variant->rkclks[j].max);
else
dev_info(dev->dev, "Downclocking %s from %lu to %lu\n",
dev->variant->rkclks[j].name, cr,
dev->variant->rkclks[j].max);
}
}
}
return 0;
}
static int rk_crypto_enable_clk(struct rk_crypto_info *dev)
{
int err;
err = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
if (err)
dev_err(dev->dev, "Could not enable clock clks\n");
return err;
}
static void rk_crypto_disable_clk(struct rk_crypto_info *dev)
{
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
}
/*
* Power management strategy: The device is suspended until a request
* is handled. For avoiding suspend/resume yoyo, the autosuspend is set to 2s.
*/
static int rk_crypto_pm_suspend(struct device *dev)
{
struct rk_crypto_info *rkdev = dev_get_drvdata(dev);
rk_crypto_disable_clk(rkdev);
reset_control_assert(rkdev->rst);
return 0;
}
static int rk_crypto_pm_resume(struct device *dev)
{
struct rk_crypto_info *rkdev = dev_get_drvdata(dev);
int ret;
ret = rk_crypto_enable_clk(rkdev);
if (ret)
return ret;
reset_control_deassert(rkdev->rst);
return 0;
}
static const struct dev_pm_ops rk_crypto_pm_ops = {
SET_RUNTIME_PM_OPS(rk_crypto_pm_suspend, rk_crypto_pm_resume, NULL)
};
static int rk_crypto_pm_init(struct rk_crypto_info *rkdev)
{
int err;
pm_runtime_use_autosuspend(rkdev->dev);
pm_runtime_set_autosuspend_delay(rkdev->dev, 2000);
err = pm_runtime_set_suspended(rkdev->dev);
if (err)
return err;
pm_runtime_enable(rkdev->dev);
return err;
}
static void rk_crypto_pm_exit(struct rk_crypto_info *rkdev)
{
pm_runtime_disable(rkdev->dev);
}
static irqreturn_t rk_crypto_irq_handle(int irq, void *dev_id)
{
struct rk_crypto_info *dev = platform_get_drvdata(dev_id);
u32 interrupt_status;
interrupt_status = CRYPTO_READ(dev, RK_CRYPTO_INTSTS);
CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, interrupt_status);
dev->status = 1;
if (interrupt_status & 0x0a) {
dev_warn(dev->dev, "DMA Error\n");
dev->status = 0;
}
complete(&dev->complete);
return IRQ_HANDLED;
}
static struct rk_crypto_tmp *rk_cipher_algs[] = {
&rk_ecb_aes_alg,
&rk_cbc_aes_alg,
&rk_ecb_des_alg,
&rk_cbc_des_alg,
&rk_ecb_des3_ede_alg,
&rk_cbc_des3_ede_alg,
&rk_ahash_sha1,
&rk_ahash_sha256,
&rk_ahash_md5,
};
static int rk_crypto_debugfs_show(struct seq_file *seq, void *v)
{
struct rk_crypto_info *dd;
unsigned int i;
spin_lock(&rocklist.lock);
list_for_each_entry(dd, &rocklist.dev_list, list) {
seq_printf(seq, "%s %s requests: %lu\n",
dev_driver_string(dd->dev), dev_name(dd->dev),
dd->nreq);
}
spin_unlock(&rocklist.lock);
for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
if (!rk_cipher_algs[i]->dev)
continue;
switch (rk_cipher_algs[i]->type) {
case CRYPTO_ALG_TYPE_SKCIPHER:
seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
rk_cipher_algs[i]->alg.skcipher.base.base.cra_driver_name,
rk_cipher_algs[i]->alg.skcipher.base.base.cra_name,
rk_cipher_algs[i]->stat_req, rk_cipher_algs[i]->stat_fb);
seq_printf(seq, "\tfallback due to length: %lu\n",
rk_cipher_algs[i]->stat_fb_len);
seq_printf(seq, "\tfallback due to alignment: %lu\n",
rk_cipher_algs[i]->stat_fb_align);
seq_printf(seq, "\tfallback due to SGs: %lu\n",
rk_cipher_algs[i]->stat_fb_sgdiff);
break;
case CRYPTO_ALG_TYPE_AHASH:
seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
rk_cipher_algs[i]->alg.hash.base.halg.base.cra_driver_name,
rk_cipher_algs[i]->alg.hash.base.halg.base.cra_name,
rk_cipher_algs[i]->stat_req, rk_cipher_algs[i]->stat_fb);
break;
}
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(rk_crypto_debugfs);
static void register_debugfs(struct rk_crypto_info *crypto_info)
{
struct dentry *dbgfs_dir __maybe_unused;
struct dentry *dbgfs_stats __maybe_unused;
/* Ignore error of debugfs */
dbgfs_dir = debugfs_create_dir("rk3288_crypto", NULL);
dbgfs_stats = debugfs_create_file("stats", 0444, dbgfs_dir, &rocklist,
&rk_crypto_debugfs_fops);
#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP_DEBUG
rocklist.dbgfs_dir = dbgfs_dir;
rocklist.dbgfs_stats = dbgfs_stats;
#endif
}
static int rk_crypto_register(struct rk_crypto_info *crypto_info)
{
unsigned int i, k;
int err = 0;
for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
rk_cipher_algs[i]->dev = crypto_info;
switch (rk_cipher_algs[i]->type) {
case CRYPTO_ALG_TYPE_SKCIPHER:
dev_info(crypto_info->dev, "Register %s as %s\n",
rk_cipher_algs[i]->alg.skcipher.base.base.cra_name,
rk_cipher_algs[i]->alg.skcipher.base.base.cra_driver_name);
err = crypto_engine_register_skcipher(&rk_cipher_algs[i]->alg.skcipher);
break;
case CRYPTO_ALG_TYPE_AHASH:
dev_info(crypto_info->dev, "Register %s as %s\n",
rk_cipher_algs[i]->alg.hash.base.halg.base.cra_name,
rk_cipher_algs[i]->alg.hash.base.halg.base.cra_driver_name);
err = crypto_engine_register_ahash(&rk_cipher_algs[i]->alg.hash);
break;
default:
dev_err(crypto_info->dev, "unknown algorithm\n");
}
if (err)
goto err_cipher_algs;
}
return 0;
err_cipher_algs:
for (k = 0; k < i; k++) {
if (rk_cipher_algs[i]->type == CRYPTO_ALG_TYPE_SKCIPHER)
crypto_engine_unregister_skcipher(&rk_cipher_algs[k]->alg.skcipher);
else
crypto_engine_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
}
return err;
}
static void rk_crypto_unregister(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
if (rk_cipher_algs[i]->type == CRYPTO_ALG_TYPE_SKCIPHER)
crypto_engine_unregister_skcipher(&rk_cipher_algs[i]->alg.skcipher);
else
crypto_engine_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
}
}
static const struct of_device_id crypto_of_id_table[] = {
{ .compatible = "rockchip,rk3288-crypto",
.data = &rk3288_variant,
},
{ .compatible = "rockchip,rk3328-crypto",
.data = &rk3328_variant,
},
{ .compatible = "rockchip,rk3399-crypto",
.data = &rk3399_variant,
},
{}
};
MODULE_DEVICE_TABLE(of, crypto_of_id_table);
static int rk_crypto_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rk_crypto_info *crypto_info, *first;
int err = 0;
crypto_info = devm_kzalloc(&pdev->dev,
sizeof(*crypto_info), GFP_KERNEL);
if (!crypto_info) {
err = -ENOMEM;
goto err_crypto;
}
crypto_info->dev = &pdev->dev;
platform_set_drvdata(pdev, crypto_info);
crypto_info->variant = of_device_get_match_data(&pdev->dev);
if (!crypto_info->variant) {
dev_err(&pdev->dev, "Missing variant\n");
return -EINVAL;
}
crypto_info->rst = devm_reset_control_array_get_exclusive(dev);
if (IS_ERR(crypto_info->rst)) {
err = PTR_ERR(crypto_info->rst);
goto err_crypto;
}
reset_control_assert(crypto_info->rst);
usleep_range(10, 20);
reset_control_deassert(crypto_info->rst);
crypto_info->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(crypto_info->reg)) {
err = PTR_ERR(crypto_info->reg);
goto err_crypto;
}
err = rk_crypto_get_clks(crypto_info);
if (err)
goto err_crypto;
crypto_info->irq = platform_get_irq(pdev, 0);
if (crypto_info->irq < 0) {
err = crypto_info->irq;
goto err_crypto;
}
err = devm_request_irq(&pdev->dev, crypto_info->irq,
rk_crypto_irq_handle, IRQF_SHARED,
"rk-crypto", pdev);
if (err) {
dev_err(&pdev->dev, "irq request failed.\n");
goto err_crypto;
}
crypto_info->engine = crypto_engine_alloc_init(&pdev->dev, true);
if (!crypto_info->engine) {
err = -ENOMEM;
goto err_crypto;
}
crypto_engine_start(crypto_info->engine);
init_completion(&crypto_info->complete);
err = rk_crypto_pm_init(crypto_info);
if (err)
goto err_pm;
spin_lock(&rocklist.lock);
first = list_first_entry_or_null(&rocklist.dev_list,
struct rk_crypto_info, list);
list_add_tail(&crypto_info->list, &rocklist.dev_list);
spin_unlock(&rocklist.lock);
if (!first) {
err = rk_crypto_register(crypto_info);
if (err) {
dev_err(dev, "Fail to register crypto algorithms");
goto err_register_alg;
}
register_debugfs(crypto_info);
}
return 0;
err_register_alg:
rk_crypto_pm_exit(crypto_info);
err_pm:
crypto_engine_exit(crypto_info->engine);
err_crypto:
dev_err(dev, "Crypto Accelerator not successfully registered\n");
return err;
}
static void rk_crypto_remove(struct platform_device *pdev)
{
struct rk_crypto_info *crypto_tmp = platform_get_drvdata(pdev);
struct rk_crypto_info *first;
spin_lock_bh(&rocklist.lock);
list_del(&crypto_tmp->list);
first = list_first_entry_or_null(&rocklist.dev_list,
struct rk_crypto_info, list);
spin_unlock_bh(&rocklist.lock);
if (!first) {
#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP_DEBUG
debugfs_remove_recursive(rocklist.dbgfs_dir);
#endif
rk_crypto_unregister();
}
rk_crypto_pm_exit(crypto_tmp);
crypto_engine_exit(crypto_tmp->engine);
}
static struct platform_driver crypto_driver = {
.probe = rk_crypto_probe,
.remove = rk_crypto_remove,
.driver = {
.name = "rk3288-crypto",
.pm = &rk_crypto_pm_ops,
.of_match_table = crypto_of_id_table,
},
};
module_platform_driver(crypto_driver);
MODULE_AUTHOR("Zain Wang <zain.wang@rock-chips.com>");
MODULE_DESCRIPTION("Support for Rockchip's cryptographic engine");
MODULE_LICENSE("GPL");
@@ -0,0 +1,285 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __RK3288_CRYPTO_H__
#define __RK3288_CRYPTO_H__
#include <crypto/aes.h>
#include <crypto/engine.h>
#include <crypto/internal/des.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <crypto/md5.h>
#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#define _SBF(v, f) ((v) << (f))
/* Crypto control registers*/
#define RK_CRYPTO_INTSTS 0x0000
#define RK_CRYPTO_PKA_DONE_INT BIT(5)
#define RK_CRYPTO_HASH_DONE_INT BIT(4)
#define RK_CRYPTO_HRDMA_ERR_INT BIT(3)
#define RK_CRYPTO_HRDMA_DONE_INT BIT(2)
#define RK_CRYPTO_BCDMA_ERR_INT BIT(1)
#define RK_CRYPTO_BCDMA_DONE_INT BIT(0)
#define RK_CRYPTO_INTENA 0x0004
#define RK_CRYPTO_PKA_DONE_ENA BIT(5)
#define RK_CRYPTO_HASH_DONE_ENA BIT(4)
#define RK_CRYPTO_HRDMA_ERR_ENA BIT(3)
#define RK_CRYPTO_HRDMA_DONE_ENA BIT(2)
#define RK_CRYPTO_BCDMA_ERR_ENA BIT(1)
#define RK_CRYPTO_BCDMA_DONE_ENA BIT(0)
#define RK_CRYPTO_CTRL 0x0008
#define RK_CRYPTO_WRITE_MASK _SBF(0xFFFF, 16)
#define RK_CRYPTO_TRNG_FLUSH BIT(9)
#define RK_CRYPTO_TRNG_START BIT(8)
#define RK_CRYPTO_PKA_FLUSH BIT(7)
#define RK_CRYPTO_HASH_FLUSH BIT(6)
#define RK_CRYPTO_BLOCK_FLUSH BIT(5)
#define RK_CRYPTO_PKA_START BIT(4)
#define RK_CRYPTO_HASH_START BIT(3)
#define RK_CRYPTO_BLOCK_START BIT(2)
#define RK_CRYPTO_TDES_START BIT(1)
#define RK_CRYPTO_AES_START BIT(0)
#define RK_CRYPTO_CONF 0x000c
/* HASH Receive DMA Address Mode: fix | increment */
#define RK_CRYPTO_HR_ADDR_MODE BIT(8)
/* Block Transmit DMA Address Mode: fix | increment */
#define RK_CRYPTO_BT_ADDR_MODE BIT(7)
/* Block Receive DMA Address Mode: fix | increment */
#define RK_CRYPTO_BR_ADDR_MODE BIT(6)
#define RK_CRYPTO_BYTESWAP_HRFIFO BIT(5)
#define RK_CRYPTO_BYTESWAP_BTFIFO BIT(4)
#define RK_CRYPTO_BYTESWAP_BRFIFO BIT(3)
/* AES = 0 OR DES = 1 */
#define RK_CRYPTO_DESSEL BIT(2)
#define RK_CYYPTO_HASHINSEL_INDEPENDENT_SOURCE _SBF(0x00, 0)
#define RK_CYYPTO_HASHINSEL_BLOCK_CIPHER_INPUT _SBF(0x01, 0)
#define RK_CYYPTO_HASHINSEL_BLOCK_CIPHER_OUTPUT _SBF(0x02, 0)
/* Block Receiving DMA Start Address Register */
#define RK_CRYPTO_BRDMAS 0x0010
/* Block Transmitting DMA Start Address Register */
#define RK_CRYPTO_BTDMAS 0x0014
/* Block Receiving DMA Length Register */
#define RK_CRYPTO_BRDMAL 0x0018
/* Hash Receiving DMA Start Address Register */
#define RK_CRYPTO_HRDMAS 0x001c
/* Hash Receiving DMA Length Register */
#define RK_CRYPTO_HRDMAL 0x0020
/* AES registers */
#define RK_CRYPTO_AES_CTRL 0x0080
#define RK_CRYPTO_AES_BYTESWAP_CNT BIT(11)
#define RK_CRYPTO_AES_BYTESWAP_KEY BIT(10)
#define RK_CRYPTO_AES_BYTESWAP_IV BIT(9)
#define RK_CRYPTO_AES_BYTESWAP_DO BIT(8)
#define RK_CRYPTO_AES_BYTESWAP_DI BIT(7)
#define RK_CRYPTO_AES_KEY_CHANGE BIT(6)
#define RK_CRYPTO_AES_ECB_MODE _SBF(0x00, 4)
#define RK_CRYPTO_AES_CBC_MODE _SBF(0x01, 4)
#define RK_CRYPTO_AES_CTR_MODE _SBF(0x02, 4)
#define RK_CRYPTO_AES_128BIT_key _SBF(0x00, 2)
#define RK_CRYPTO_AES_192BIT_key _SBF(0x01, 2)
#define RK_CRYPTO_AES_256BIT_key _SBF(0x02, 2)
/* Slave = 0 / fifo = 1 */
#define RK_CRYPTO_AES_FIFO_MODE BIT(1)
/* Encryption = 0 , Decryption = 1 */
#define RK_CRYPTO_AES_DEC BIT(0)
#define RK_CRYPTO_AES_STS 0x0084
#define RK_CRYPTO_AES_DONE BIT(0)
/* AES Input Data 0-3 Register */
#define RK_CRYPTO_AES_DIN_0 0x0088
#define RK_CRYPTO_AES_DIN_1 0x008c
#define RK_CRYPTO_AES_DIN_2 0x0090
#define RK_CRYPTO_AES_DIN_3 0x0094
/* AES output Data 0-3 Register */
#define RK_CRYPTO_AES_DOUT_0 0x0098
#define RK_CRYPTO_AES_DOUT_1 0x009c
#define RK_CRYPTO_AES_DOUT_2 0x00a0
#define RK_CRYPTO_AES_DOUT_3 0x00a4
/* AES IV Data 0-3 Register */
#define RK_CRYPTO_AES_IV_0 0x00a8
#define RK_CRYPTO_AES_IV_1 0x00ac
#define RK_CRYPTO_AES_IV_2 0x00b0
#define RK_CRYPTO_AES_IV_3 0x00b4
/* AES Key Data 0-3 Register */
#define RK_CRYPTO_AES_KEY_0 0x00b8
#define RK_CRYPTO_AES_KEY_1 0x00bc
#define RK_CRYPTO_AES_KEY_2 0x00c0
#define RK_CRYPTO_AES_KEY_3 0x00c4
#define RK_CRYPTO_AES_KEY_4 0x00c8
#define RK_CRYPTO_AES_KEY_5 0x00cc
#define RK_CRYPTO_AES_KEY_6 0x00d0
#define RK_CRYPTO_AES_KEY_7 0x00d4
/* des/tdes */
#define RK_CRYPTO_TDES_CTRL 0x0100
#define RK_CRYPTO_TDES_BYTESWAP_KEY BIT(8)
#define RK_CRYPTO_TDES_BYTESWAP_IV BIT(7)
#define RK_CRYPTO_TDES_BYTESWAP_DO BIT(6)
#define RK_CRYPTO_TDES_BYTESWAP_DI BIT(5)
/* 0: ECB, 1: CBC */
#define RK_CRYPTO_TDES_CHAINMODE_CBC BIT(4)
/* TDES Key Mode, 0 : EDE, 1 : EEE */
#define RK_CRYPTO_TDES_EEE BIT(3)
/* 0: DES, 1:TDES */
#define RK_CRYPTO_TDES_SELECT BIT(2)
/* 0: Slave, 1:Fifo */
#define RK_CRYPTO_TDES_FIFO_MODE BIT(1)
/* Encryption = 0 , Decryption = 1 */
#define RK_CRYPTO_TDES_DEC BIT(0)
#define RK_CRYPTO_TDES_STS 0x0104
#define RK_CRYPTO_TDES_DONE BIT(0)
#define RK_CRYPTO_TDES_DIN_0 0x0108
#define RK_CRYPTO_TDES_DIN_1 0x010c
#define RK_CRYPTO_TDES_DOUT_0 0x0110
#define RK_CRYPTO_TDES_DOUT_1 0x0114
#define RK_CRYPTO_TDES_IV_0 0x0118
#define RK_CRYPTO_TDES_IV_1 0x011c
#define RK_CRYPTO_TDES_KEY1_0 0x0120
#define RK_CRYPTO_TDES_KEY1_1 0x0124
#define RK_CRYPTO_TDES_KEY2_0 0x0128
#define RK_CRYPTO_TDES_KEY2_1 0x012c
#define RK_CRYPTO_TDES_KEY3_0 0x0130
#define RK_CRYPTO_TDES_KEY3_1 0x0134
/* HASH */
#define RK_CRYPTO_HASH_CTRL 0x0180
#define RK_CRYPTO_HASH_SWAP_DO BIT(3)
#define RK_CRYPTO_HASH_SWAP_DI BIT(2)
#define RK_CRYPTO_HASH_SHA1 _SBF(0x00, 0)
#define RK_CRYPTO_HASH_MD5 _SBF(0x01, 0)
#define RK_CRYPTO_HASH_SHA256 _SBF(0x02, 0)
#define RK_CRYPTO_HASH_PRNG _SBF(0x03, 0)
#define RK_CRYPTO_HASH_STS 0x0184
#define RK_CRYPTO_HASH_DONE BIT(0)
#define RK_CRYPTO_HASH_MSG_LEN 0x0188
#define RK_CRYPTO_HASH_DOUT_0 0x018c
#define RK_CRYPTO_HASH_DOUT_1 0x0190
#define RK_CRYPTO_HASH_DOUT_2 0x0194
#define RK_CRYPTO_HASH_DOUT_3 0x0198
#define RK_CRYPTO_HASH_DOUT_4 0x019c
#define RK_CRYPTO_HASH_DOUT_5 0x01a0
#define RK_CRYPTO_HASH_DOUT_6 0x01a4
#define RK_CRYPTO_HASH_DOUT_7 0x01a8
#define CRYPTO_READ(dev, offset) \
readl_relaxed(((dev)->reg + (offset)))
#define CRYPTO_WRITE(dev, offset, val) \
writel_relaxed((val), ((dev)->reg + (offset)))
#define RK_MAX_CLKS 4
/*
* struct rockchip_ip - struct for managing a list of RK crypto instance
* @dev_list: Used for doing a list of rk_crypto_info
* @lock: Control access to dev_list
* @dbgfs_dir: Debugfs dentry for statistic directory
* @dbgfs_stats: Debugfs dentry for statistic counters
*/
struct rockchip_ip {
struct list_head dev_list;
spinlock_t lock; /* Control access to dev_list */
struct dentry *dbgfs_dir;
struct dentry *dbgfs_stats;
};
struct rk_clks {
const char *name;
unsigned long max;
};
struct rk_variant {
int num_clks;
struct rk_clks rkclks[RK_MAX_CLKS];
};
struct rk_crypto_info {
struct list_head list;
struct device *dev;
struct clk_bulk_data *clks;
int num_clks;
struct reset_control *rst;
void __iomem *reg;
int irq;
const struct rk_variant *variant;
unsigned long nreq;
struct crypto_engine *engine;
struct completion complete;
int status;
};
/* the private variable of hash */
struct rk_ahash_ctx {
/* for fallback */
struct crypto_ahash *fallback_tfm;
};
/* the private variable of hash for fallback */
struct rk_ahash_rctx {
struct rk_crypto_info *dev;
struct ahash_request fallback_req;
u32 mode;
int nrsg;
};
/* the private variable of cipher */
struct rk_cipher_ctx {
unsigned int keylen;
u8 key[AES_MAX_KEY_SIZE];
u8 iv[AES_BLOCK_SIZE];
struct crypto_skcipher *fallback_tfm;
};
struct rk_cipher_rctx {
struct rk_crypto_info *dev;
u8 backup_iv[AES_BLOCK_SIZE];
u32 mode;
struct skcipher_request fallback_req; // keep at the end
};
struct rk_crypto_tmp {
u32 type;
struct rk_crypto_info *dev;
union {
struct skcipher_engine_alg skcipher;
struct ahash_engine_alg hash;
} alg;
unsigned long stat_req;
unsigned long stat_fb;
unsigned long stat_fb_len;
unsigned long stat_fb_sglen;
unsigned long stat_fb_align;
unsigned long stat_fb_sgdiff;
};
extern struct rk_crypto_tmp rk_ecb_aes_alg;
extern struct rk_crypto_tmp rk_cbc_aes_alg;
extern struct rk_crypto_tmp rk_ecb_des_alg;
extern struct rk_crypto_tmp rk_cbc_des_alg;
extern struct rk_crypto_tmp rk_ecb_des3_ede_alg;
extern struct rk_crypto_tmp rk_cbc_des3_ede_alg;
extern struct rk_crypto_tmp rk_ahash_sha1;
extern struct rk_crypto_tmp rk_ahash_sha256;
extern struct rk_crypto_tmp rk_ahash_md5;
struct rk_crypto_info *get_rk_crypto(void);
#endif
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Crypto acceleration support for Rockchip RK3288
*
* Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Zain Wang <zain.wang@rock-chips.com>
*
* Some ideas are from marvell/cesa.c and s5p-sss.c driver.
*/
#include <linux/unaligned.h>
#include <crypto/internal/hash.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include "rk3288_crypto.h"
/*
* IC can not process zero message hash,
* so we put the fixed hash out when met zero message.
*/
static bool rk_ahash_need_fallback(struct ahash_request *req)
{
struct scatterlist *sg;
sg = req->src;
while (sg) {
if (!IS_ALIGNED(sg->offset, sizeof(u32))) {
return true;
}
if (sg->length % 4) {
return true;
}
sg = sg_next(sg);
}
return false;
}
static int rk_ahash_digest_fb(struct ahash_request *areq)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(areq);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
struct rk_ahash_ctx *tfmctx = crypto_ahash_ctx(tfm);
struct ahash_alg *alg = crypto_ahash_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.hash.base);
algt->stat_fb++;
ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
areq->base.complete, areq->base.data);
ahash_request_set_crypt(&rctx->fallback_req, areq->src, areq->result,
areq->nbytes);
return crypto_ahash_digest(&rctx->fallback_req);
}
static int zero_message_process(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
int rk_digest_size = crypto_ahash_digestsize(tfm);
switch (rk_digest_size) {
case SHA1_DIGEST_SIZE:
memcpy(req->result, sha1_zero_message_hash, rk_digest_size);
break;
case SHA256_DIGEST_SIZE:
memcpy(req->result, sha256_zero_message_hash, rk_digest_size);
break;
case MD5_DIGEST_SIZE:
memcpy(req->result, md5_zero_message_hash, rk_digest_size);
break;
default:
return -EINVAL;
}
return 0;
}
static void rk_ahash_reg_init(struct ahash_request *req,
struct rk_crypto_info *dev)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
int reg_status;
reg_status = CRYPTO_READ(dev, RK_CRYPTO_CTRL) |
RK_CRYPTO_HASH_FLUSH | _SBF(0xffff, 16);
CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, reg_status);
reg_status = CRYPTO_READ(dev, RK_CRYPTO_CTRL);
reg_status &= (~RK_CRYPTO_HASH_FLUSH);
reg_status |= _SBF(0xffff, 16);
CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, reg_status);
memset_io(dev->reg + RK_CRYPTO_HASH_DOUT_0, 0, 32);
CRYPTO_WRITE(dev, RK_CRYPTO_INTENA, RK_CRYPTO_HRDMA_ERR_ENA |
RK_CRYPTO_HRDMA_DONE_ENA);
CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, RK_CRYPTO_HRDMA_ERR_INT |
RK_CRYPTO_HRDMA_DONE_INT);
CRYPTO_WRITE(dev, RK_CRYPTO_HASH_CTRL, rctx->mode |
RK_CRYPTO_HASH_SWAP_DO);
CRYPTO_WRITE(dev, RK_CRYPTO_CONF, RK_CRYPTO_BYTESWAP_HRFIFO |
RK_CRYPTO_BYTESWAP_BRFIFO |
RK_CRYPTO_BYTESWAP_BTFIFO);
CRYPTO_WRITE(dev, RK_CRYPTO_HASH_MSG_LEN, req->nbytes);
}
static int rk_ahash_init(struct ahash_request *req)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
return crypto_ahash_init(&rctx->fallback_req);
}
static int rk_ahash_update(struct ahash_request *req)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
ahash_request_set_crypt(&rctx->fallback_req, req->src, NULL, req->nbytes);
return crypto_ahash_update(&rctx->fallback_req);
}
static int rk_ahash_final(struct ahash_request *req)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
ahash_request_set_crypt(&rctx->fallback_req, NULL, req->result, 0);
return crypto_ahash_final(&rctx->fallback_req);
}
static int rk_ahash_finup(struct ahash_request *req)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
ahash_request_set_crypt(&rctx->fallback_req, req->src, req->result,
req->nbytes);
return crypto_ahash_finup(&rctx->fallback_req);
}
static int rk_ahash_import(struct ahash_request *req, const void *in)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
return crypto_ahash_import(&rctx->fallback_req, in);
}
static int rk_ahash_export(struct ahash_request *req, void *out)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm);
ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
ahash_request_set_callback(&rctx->fallback_req,
req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,
req->base.complete, req->base.data);
return crypto_ahash_export(&rctx->fallback_req, out);
}
static int rk_ahash_digest(struct ahash_request *req)
{
struct rk_ahash_rctx *rctx = ahash_request_ctx(req);
struct rk_crypto_info *dev;
struct crypto_engine *engine;
if (rk_ahash_need_fallback(req))
return rk_ahash_digest_fb(req);
if (!req->nbytes)
return zero_message_process(req);
dev = get_rk_crypto();
rctx->dev = dev;
engine = dev->engine;
return crypto_transfer_hash_request_to_engine(engine, req);
}
static void crypto_ahash_dma_start(struct rk_crypto_info *dev, struct scatterlist *sg)
{
CRYPTO_WRITE(dev, RK_CRYPTO_HRDMAS, sg_dma_address(sg));
CRYPTO_WRITE(dev, RK_CRYPTO_HRDMAL, sg_dma_len(sg) / 4);
CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, RK_CRYPTO_HASH_START |
(RK_CRYPTO_HASH_START << 16));
}
static int rk_hash_prepare(struct crypto_engine *engine, void *breq)
{
struct ahash_request *areq = container_of(breq, struct ahash_request, base);
struct rk_ahash_rctx *rctx = ahash_request_ctx(areq);
struct rk_crypto_info *rkc = rctx->dev;
int ret;
ret = dma_map_sg(rkc->dev, areq->src, sg_nents(areq->src), DMA_TO_DEVICE);
if (ret <= 0)
return -EINVAL;
rctx->nrsg = ret;
return 0;
}
static void rk_hash_unprepare(struct crypto_engine *engine, void *breq)
{
struct ahash_request *areq = container_of(breq, struct ahash_request, base);
struct rk_ahash_rctx *rctx = ahash_request_ctx(areq);
struct rk_crypto_info *rkc = rctx->dev;
dma_unmap_sg(rkc->dev, areq->src, sg_nents(areq->src), DMA_TO_DEVICE);
}
static int rk_hash_run(struct crypto_engine *engine, void *breq)
{
struct ahash_request *areq = container_of(breq, struct ahash_request, base);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
struct rk_ahash_rctx *rctx = ahash_request_ctx(areq);
struct ahash_alg *alg = crypto_ahash_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.hash.base);
struct scatterlist *sg = areq->src;
struct rk_crypto_info *rkc = rctx->dev;
int err;
int i;
u32 v;
err = pm_runtime_resume_and_get(rkc->dev);
if (err)
return err;
err = rk_hash_prepare(engine, breq);
if (err)
goto theend;
rctx->mode = 0;
algt->stat_req++;
rkc->nreq++;
switch (crypto_ahash_digestsize(tfm)) {
case SHA1_DIGEST_SIZE:
rctx->mode = RK_CRYPTO_HASH_SHA1;
break;
case SHA256_DIGEST_SIZE:
rctx->mode = RK_CRYPTO_HASH_SHA256;
break;
case MD5_DIGEST_SIZE:
rctx->mode = RK_CRYPTO_HASH_MD5;
break;
default:
err = -EINVAL;
goto theend;
}
rk_ahash_reg_init(areq, rkc);
while (sg) {
reinit_completion(&rkc->complete);
rkc->status = 0;
crypto_ahash_dma_start(rkc, sg);
wait_for_completion_interruptible_timeout(&rkc->complete,
msecs_to_jiffies(2000));
if (!rkc->status) {
dev_err(rkc->dev, "DMA timeout\n");
err = -EFAULT;
goto theend;
}
sg = sg_next(sg);
}
/*
* it will take some time to process date after last dma
* transmission.
*
* waiting time is relative with the last date len,
* so cannot set a fixed time here.
* 10us makes system not call here frequently wasting
* efficiency, and make it response quickly when dma
* complete.
*/
readl_poll_timeout(rkc->reg + RK_CRYPTO_HASH_STS, v, v == 0, 10, 1000);
for (i = 0; i < crypto_ahash_digestsize(tfm) / 4; i++) {
v = readl(rkc->reg + RK_CRYPTO_HASH_DOUT_0 + i * 4);
put_unaligned_le32(v, areq->result + i * 4);
}
theend:
pm_runtime_put_autosuspend(rkc->dev);
rk_hash_unprepare(engine, breq);
local_bh_disable();
crypto_finalize_hash_request(engine, breq, err);
local_bh_enable();
return 0;
}
static int rk_hash_init_tfm(struct crypto_ahash *tfm)
{
struct rk_ahash_ctx *tctx = crypto_ahash_ctx(tfm);
const char *alg_name = crypto_ahash_alg_name(tfm);
struct ahash_alg *alg = crypto_ahash_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.hash.base);
/* for fallback */
tctx->fallback_tfm = crypto_alloc_ahash(alg_name, 0,
CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(tctx->fallback_tfm)) {
dev_err(algt->dev->dev, "Could not load fallback driver.\n");
return PTR_ERR(tctx->fallback_tfm);
}
crypto_ahash_set_reqsize(tfm,
sizeof(struct rk_ahash_rctx) +
crypto_ahash_reqsize(tctx->fallback_tfm));
return 0;
}
static void rk_hash_exit_tfm(struct crypto_ahash *tfm)
{
struct rk_ahash_ctx *tctx = crypto_ahash_ctx(tfm);
crypto_free_ahash(tctx->fallback_tfm);
}
struct rk_crypto_tmp rk_ahash_sha1 = {
.type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash.base = {
.init = rk_ahash_init,
.update = rk_ahash_update,
.final = rk_ahash_final,
.finup = rk_ahash_finup,
.export = rk_ahash_export,
.import = rk_ahash_import,
.digest = rk_ahash_digest,
.init_tfm = rk_hash_init_tfm,
.exit_tfm = rk_hash_exit_tfm,
.halg = {
.digestsize = SHA1_DIGEST_SIZE,
.statesize = sizeof(struct sha1_state),
.base = {
.cra_name = "sha1",
.cra_driver_name = "rk-sha1",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct rk_ahash_ctx),
.cra_module = THIS_MODULE,
}
}
},
.alg.hash.op = {
.do_one_request = rk_hash_run,
},
};
struct rk_crypto_tmp rk_ahash_sha256 = {
.type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash.base = {
.init = rk_ahash_init,
.update = rk_ahash_update,
.final = rk_ahash_final,
.finup = rk_ahash_finup,
.export = rk_ahash_export,
.import = rk_ahash_import,
.digest = rk_ahash_digest,
.init_tfm = rk_hash_init_tfm,
.exit_tfm = rk_hash_exit_tfm,
.halg = {
.digestsize = SHA256_DIGEST_SIZE,
.statesize = sizeof(struct sha256_state),
.base = {
.cra_name = "sha256",
.cra_driver_name = "rk-sha256",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct rk_ahash_ctx),
.cra_module = THIS_MODULE,
}
}
},
.alg.hash.op = {
.do_one_request = rk_hash_run,
},
};
struct rk_crypto_tmp rk_ahash_md5 = {
.type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash.base = {
.init = rk_ahash_init,
.update = rk_ahash_update,
.final = rk_ahash_final,
.finup = rk_ahash_finup,
.export = rk_ahash_export,
.import = rk_ahash_import,
.digest = rk_ahash_digest,
.init_tfm = rk_hash_init_tfm,
.exit_tfm = rk_hash_exit_tfm,
.halg = {
.digestsize = MD5_DIGEST_SIZE,
.statesize = sizeof(struct md5_state),
.base = {
.cra_name = "md5",
.cra_driver_name = "rk-md5",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct rk_ahash_ctx),
.cra_module = THIS_MODULE,
}
}
},
.alg.hash.op = {
.do_one_request = rk_hash_run,
},
};
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Crypto acceleration support for Rockchip RK3288
*
* Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
*
* Author: Zain Wang <zain.wang@rock-chips.com>
*
* Some ideas are from marvell-cesa.c and s5p-sss.c driver.
*/
#include <crypto/engine.h>
#include <crypto/internal/skcipher.h>
#include <crypto/scatterwalk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include "rk3288_crypto.h"
#define RK_CRYPTO_DEC BIT(0)
static int rk_cipher_need_fallback(struct skcipher_request *req)
{
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.skcipher.base);
struct scatterlist *sgs, *sgd;
unsigned int stodo, dtodo, len;
unsigned int bs = crypto_skcipher_blocksize(tfm);
if (!req->cryptlen)
return true;
len = req->cryptlen;
sgs = req->src;
sgd = req->dst;
while (sgs && sgd) {
if (!IS_ALIGNED(sgs->offset, sizeof(u32))) {
algt->stat_fb_align++;
return true;
}
if (!IS_ALIGNED(sgd->offset, sizeof(u32))) {
algt->stat_fb_align++;
return true;
}
stodo = min(len, sgs->length);
if (stodo % bs) {
algt->stat_fb_len++;
return true;
}
dtodo = min(len, sgd->length);
if (dtodo % bs) {
algt->stat_fb_len++;
return true;
}
if (stodo != dtodo) {
algt->stat_fb_sgdiff++;
return true;
}
len -= stodo;
sgs = sg_next(sgs);
sgd = sg_next(sgd);
}
return false;
}
static int rk_cipher_fallback(struct skcipher_request *areq)
{
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
struct rk_cipher_ctx *op = crypto_skcipher_ctx(tfm);
struct rk_cipher_rctx *rctx = skcipher_request_ctx(areq);
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.skcipher.base);
int err;
algt->stat_fb++;
skcipher_request_set_tfm(&rctx->fallback_req, op->fallback_tfm);
skcipher_request_set_callback(&rctx->fallback_req, areq->base.flags,
areq->base.complete, areq->base.data);
skcipher_request_set_crypt(&rctx->fallback_req, areq->src, areq->dst,
areq->cryptlen, areq->iv);
if (rctx->mode & RK_CRYPTO_DEC)
err = crypto_skcipher_decrypt(&rctx->fallback_req);
else
err = crypto_skcipher_encrypt(&rctx->fallback_req);
return err;
}
static int rk_cipher_handle_req(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
struct rk_crypto_info *rkc;
struct crypto_engine *engine;
if (rk_cipher_need_fallback(req))
return rk_cipher_fallback(req);
rkc = get_rk_crypto();
engine = rkc->engine;
rctx->dev = rkc;
return crypto_transfer_skcipher_request_to_engine(engine, req);
}
static int rk_aes_setkey(struct crypto_skcipher *cipher,
const u8 *key, unsigned int keylen)
{
struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher);
struct rk_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 &&
keylen != AES_KEYSIZE_256)
return -EINVAL;
ctx->keylen = keylen;
memcpy(ctx->key, key, keylen);
return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen);
}
static int rk_des_setkey(struct crypto_skcipher *cipher,
const u8 *key, unsigned int keylen)
{
struct rk_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
int err;
err = verify_skcipher_des_key(cipher, key);
if (err)
return err;
ctx->keylen = keylen;
memcpy(ctx->key, key, keylen);
return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen);
}
static int rk_tdes_setkey(struct crypto_skcipher *cipher,
const u8 *key, unsigned int keylen)
{
struct rk_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
int err;
err = verify_skcipher_des3_key(cipher, key);
if (err)
return err;
ctx->keylen = keylen;
memcpy(ctx->key, key, keylen);
return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen);
}
static int rk_aes_ecb_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_AES_ECB_MODE;
return rk_cipher_handle_req(req);
}
static int rk_aes_ecb_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_AES_ECB_MODE | RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static int rk_aes_cbc_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_AES_CBC_MODE;
return rk_cipher_handle_req(req);
}
static int rk_aes_cbc_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_AES_CBC_MODE | RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static int rk_des_ecb_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = 0;
return rk_cipher_handle_req(req);
}
static int rk_des_ecb_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static int rk_des_cbc_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_CHAINMODE_CBC;
return rk_cipher_handle_req(req);
}
static int rk_des_cbc_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_CHAINMODE_CBC | RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static int rk_des3_ede_ecb_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_SELECT;
return rk_cipher_handle_req(req);
}
static int rk_des3_ede_ecb_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static int rk_des3_ede_cbc_encrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_TDES_CHAINMODE_CBC;
return rk_cipher_handle_req(req);
}
static int rk_des3_ede_cbc_decrypt(struct skcipher_request *req)
{
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
rctx->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_TDES_CHAINMODE_CBC |
RK_CRYPTO_DEC;
return rk_cipher_handle_req(req);
}
static void rk_cipher_hw_init(struct rk_crypto_info *dev, struct skcipher_request *req)
{
struct crypto_skcipher *cipher = crypto_skcipher_reqtfm(req);
struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher);
struct rk_cipher_rctx *rctx = skcipher_request_ctx(req);
struct rk_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
u32 block, conf_reg = 0;
block = crypto_tfm_alg_blocksize(tfm);
if (block == DES_BLOCK_SIZE) {
rctx->mode |= RK_CRYPTO_TDES_FIFO_MODE |
RK_CRYPTO_TDES_BYTESWAP_KEY |
RK_CRYPTO_TDES_BYTESWAP_IV;
CRYPTO_WRITE(dev, RK_CRYPTO_TDES_CTRL, rctx->mode);
memcpy_toio(dev->reg + RK_CRYPTO_TDES_KEY1_0, ctx->key, ctx->keylen);
conf_reg = RK_CRYPTO_DESSEL;
} else {
rctx->mode |= RK_CRYPTO_AES_FIFO_MODE |
RK_CRYPTO_AES_KEY_CHANGE |
RK_CRYPTO_AES_BYTESWAP_KEY |
RK_CRYPTO_AES_BYTESWAP_IV;
if (ctx->keylen == AES_KEYSIZE_192)
rctx->mode |= RK_CRYPTO_AES_192BIT_key;
else if (ctx->keylen == AES_KEYSIZE_256)
rctx->mode |= RK_CRYPTO_AES_256BIT_key;
CRYPTO_WRITE(dev, RK_CRYPTO_AES_CTRL, rctx->mode);
memcpy_toio(dev->reg + RK_CRYPTO_AES_KEY_0, ctx->key, ctx->keylen);
}
conf_reg |= RK_CRYPTO_BYTESWAP_BTFIFO |
RK_CRYPTO_BYTESWAP_BRFIFO;
CRYPTO_WRITE(dev, RK_CRYPTO_CONF, conf_reg);
CRYPTO_WRITE(dev, RK_CRYPTO_INTENA,
RK_CRYPTO_BCDMA_ERR_ENA | RK_CRYPTO_BCDMA_DONE_ENA);
}
static void crypto_dma_start(struct rk_crypto_info *dev,
struct scatterlist *sgs,
struct scatterlist *sgd, unsigned int todo)
{
CRYPTO_WRITE(dev, RK_CRYPTO_BRDMAS, sg_dma_address(sgs));
CRYPTO_WRITE(dev, RK_CRYPTO_BRDMAL, todo);
CRYPTO_WRITE(dev, RK_CRYPTO_BTDMAS, sg_dma_address(sgd));
CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, RK_CRYPTO_BLOCK_START |
_SBF(RK_CRYPTO_BLOCK_START, 16));
}
static int rk_cipher_run(struct crypto_engine *engine, void *async_req)
{
struct skcipher_request *areq = container_of(async_req, struct skcipher_request, base);
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
struct rk_cipher_rctx *rctx = skcipher_request_ctx(areq);
struct scatterlist *sgs, *sgd;
int err = 0;
int ivsize = crypto_skcipher_ivsize(tfm);
int offset;
u8 iv[AES_BLOCK_SIZE];
u8 biv[AES_BLOCK_SIZE];
u8 *ivtouse = areq->iv;
unsigned int len = areq->cryptlen;
unsigned int todo;
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.skcipher.base);
struct rk_crypto_info *rkc = rctx->dev;
err = pm_runtime_resume_and_get(rkc->dev);
if (err)
return err;
algt->stat_req++;
rkc->nreq++;
if (areq->iv && ivsize > 0) {
if (rctx->mode & RK_CRYPTO_DEC) {
offset = areq->cryptlen - ivsize;
scatterwalk_map_and_copy(rctx->backup_iv, areq->src,
offset, ivsize, 0);
}
}
sgs = areq->src;
sgd = areq->dst;
while (sgs && sgd && len) {
if (!sgs->length) {
sgs = sg_next(sgs);
sgd = sg_next(sgd);
continue;
}
if (rctx->mode & RK_CRYPTO_DEC) {
/* we backup last block of source to be used as IV at next step */
offset = sgs->length - ivsize;
scatterwalk_map_and_copy(biv, sgs, offset, ivsize, 0);
}
if (sgs == sgd) {
err = dma_map_sg(rkc->dev, sgs, 1, DMA_BIDIRECTIONAL);
if (err <= 0) {
err = -EINVAL;
goto theend_iv;
}
} else {
err = dma_map_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE);
if (err <= 0) {
err = -EINVAL;
goto theend_iv;
}
err = dma_map_sg(rkc->dev, sgd, 1, DMA_FROM_DEVICE);
if (err <= 0) {
err = -EINVAL;
goto theend_sgs;
}
}
err = 0;
rk_cipher_hw_init(rkc, areq);
if (ivsize) {
if (ivsize == DES_BLOCK_SIZE)
memcpy_toio(rkc->reg + RK_CRYPTO_TDES_IV_0, ivtouse, ivsize);
else
memcpy_toio(rkc->reg + RK_CRYPTO_AES_IV_0, ivtouse, ivsize);
}
reinit_completion(&rkc->complete);
rkc->status = 0;
todo = min(sg_dma_len(sgs), len);
len -= todo;
crypto_dma_start(rkc, sgs, sgd, todo / 4);
wait_for_completion_interruptible_timeout(&rkc->complete,
msecs_to_jiffies(2000));
if (!rkc->status) {
dev_err(rkc->dev, "DMA timeout\n");
err = -EFAULT;
goto theend;
}
if (sgs == sgd) {
dma_unmap_sg(rkc->dev, sgs, 1, DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE);
dma_unmap_sg(rkc->dev, sgd, 1, DMA_FROM_DEVICE);
}
if (rctx->mode & RK_CRYPTO_DEC) {
memcpy(iv, biv, ivsize);
ivtouse = iv;
} else {
offset = sgd->length - ivsize;
scatterwalk_map_and_copy(iv, sgd, offset, ivsize, 0);
ivtouse = iv;
}
sgs = sg_next(sgs);
sgd = sg_next(sgd);
}
if (areq->iv && ivsize > 0) {
offset = areq->cryptlen - ivsize;
if (rctx->mode & RK_CRYPTO_DEC) {
memcpy(areq->iv, rctx->backup_iv, ivsize);
memzero_explicit(rctx->backup_iv, ivsize);
} else {
scatterwalk_map_and_copy(areq->iv, areq->dst, offset,
ivsize, 0);
}
}
theend:
pm_runtime_put_autosuspend(rkc->dev);
local_bh_disable();
crypto_finalize_skcipher_request(engine, areq, err);
local_bh_enable();
return 0;
theend_sgs:
if (sgs == sgd) {
dma_unmap_sg(rkc->dev, sgs, 1, DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE);
dma_unmap_sg(rkc->dev, sgd, 1, DMA_FROM_DEVICE);
}
theend_iv:
return err;
}
static int rk_cipher_tfm_init(struct crypto_skcipher *tfm)
{
struct rk_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
const char *name = crypto_tfm_alg_name(&tfm->base);
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
struct rk_crypto_tmp *algt = container_of(alg, struct rk_crypto_tmp, alg.skcipher.base);
ctx->fallback_tfm = crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(ctx->fallback_tfm)) {
dev_err(algt->dev->dev, "ERROR: Cannot allocate fallback for %s %ld\n",
name, PTR_ERR(ctx->fallback_tfm));
return PTR_ERR(ctx->fallback_tfm);
}
crypto_skcipher_set_reqsize(tfm, sizeof(struct rk_cipher_rctx) +
crypto_skcipher_reqsize(ctx->fallback_tfm));
return 0;
}
static void rk_cipher_tfm_exit(struct crypto_skcipher *tfm)
{
struct rk_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
memzero_explicit(ctx->key, ctx->keylen);
crypto_free_skcipher(ctx->fallback_tfm);
}
struct rk_crypto_tmp rk_ecb_aes_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "ecb(aes)",
.base.cra_driver_name = "ecb-aes-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = AES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x0f,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = rk_aes_setkey,
.encrypt = rk_aes_ecb_encrypt,
.decrypt = rk_aes_ecb_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};
struct rk_crypto_tmp rk_cbc_aes_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "cbc(aes)",
.base.cra_driver_name = "cbc-aes-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = AES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x0f,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
.setkey = rk_aes_setkey,
.encrypt = rk_aes_cbc_encrypt,
.decrypt = rk_aes_cbc_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};
struct rk_crypto_tmp rk_ecb_des_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "ecb(des)",
.base.cra_driver_name = "ecb-des-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = DES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x07,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.setkey = rk_des_setkey,
.encrypt = rk_des_ecb_encrypt,
.decrypt = rk_des_ecb_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};
struct rk_crypto_tmp rk_cbc_des_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "cbc(des)",
.base.cra_driver_name = "cbc-des-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = DES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x07,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.ivsize = DES_BLOCK_SIZE,
.setkey = rk_des_setkey,
.encrypt = rk_des_cbc_encrypt,
.decrypt = rk_des_cbc_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};
struct rk_crypto_tmp rk_ecb_des3_ede_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "ecb(des3_ede)",
.base.cra_driver_name = "ecb-des3-ede-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = DES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x07,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.setkey = rk_tdes_setkey,
.encrypt = rk_des3_ede_ecb_encrypt,
.decrypt = rk_des3_ede_ecb_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};
struct rk_crypto_tmp rk_cbc_des3_ede_alg = {
.type = CRYPTO_ALG_TYPE_SKCIPHER,
.alg.skcipher.base = {
.base.cra_name = "cbc(des3_ede)",
.base.cra_driver_name = "cbc-des3-ede-rk",
.base.cra_priority = 300,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
.base.cra_blocksize = DES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct rk_cipher_ctx),
.base.cra_alignmask = 0x07,
.base.cra_module = THIS_MODULE,
.init = rk_cipher_tfm_init,
.exit = rk_cipher_tfm_exit,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.ivsize = DES_BLOCK_SIZE,
.setkey = rk_tdes_setkey,
.encrypt = rk_des3_ede_cbc_encrypt,
.decrypt = rk_des3_ede_cbc_decrypt,
},
.alg.skcipher.op = {
.do_one_request = rk_cipher_run,
},
};