linuxOS_D21X/source/linux-5.10/drivers/crypto/artinchip/ce/crypto.c
2024-11-29 16:23:11 +08:00

486 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021, Artinchip Technology Co., Ltd
*
* Wu Dehuang <dehuang.wu@artinchip.com>
*/
#include <linux/clk.h>
#include <linux/crypto.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <crypto/aes.h>
#include <crypto/internal/des.h>
#include <crypto/engine.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/skcipher.h>
#include <crypto/internal/rng.h>
#include "crypto.h"
#define DRIVER_NAME "aic-crypto"
static irqreturn_t aic_crypto_irq_thread(int irq, void *arg)
{
struct aic_crypto_dev *ce_dev = arg;
u32 mask;
mask = BIT(SK_ALG_ACCELERATOR) | BIT(AK_ALG_ACCELERATOR) |
BIT(HASH_ALG_ACCELERATOR);
loop:
if (ce_dev->irq_status & BIT(SK_ALG_ACCELERATOR)) {
aic_skcipher_handle_irq(ce_dev);
ce_dev->irq_status &= ~BIT(SK_ALG_ACCELERATOR);
ce_dev->err_status &= (0xff << (8 * SK_ALG_ACCELERATOR));
}
if (ce_dev->irq_status & BIT(AK_ALG_ACCELERATOR)) {
aic_akcipher_handle_irq(ce_dev);
ce_dev->irq_status &= ~BIT(AK_ALG_ACCELERATOR);
ce_dev->err_status &= (0xff << (8 * AK_ALG_ACCELERATOR));
}
if (ce_dev->irq_status & BIT(HASH_ALG_ACCELERATOR)) {
aic_hash_handle_irq(ce_dev);
ce_dev->irq_status &= ~BIT(HASH_ALG_ACCELERATOR);
ce_dev->err_status &= (0xff << (8 * HASH_ALG_ACCELERATOR));
}
if (ce_dev->irq_status & mask)
goto loop;
return IRQ_HANDLED;
}
static irqreturn_t aic_crypto_irq_handler(int irq, void *arg)
{
struct aic_crypto_dev *ce_dev = arg;
u32 ints, errs;
ints = readl(ce_dev->base + CE_REG_ISR);
errs = readl(ce_dev->base + CE_REG_TER);
ce_dev->irq_status |= ints;
ce_dev->err_status |= errs;
writel(ints, ce_dev->base + CE_REG_ISR);
return IRQ_WAKE_THREAD;
}
bool aic_crypto_is_ce_avail(struct aic_crypto_dev *dev)
{
u32 val;
val = readl(dev->base + CE_REG_TCR);
return ((val & (0x1UL << TASK_LOAD_BIT_OFFSET)) == 0);
}
bool aic_crypto_is_accel_avail(struct aic_crypto_dev *dev, int accel)
{
return true;
}
bool aic_crypto_check_task_done(struct aic_crypto_dev *dev, int accel)
{
u32 val;
val = readl(dev->base + CE_REG_ISR);
if (val & BIT(accel))
return true;
return false;
}
void aic_crypto_pending_clear(struct aic_crypto_dev *dev, int accel)
{
writel(BIT(accel), dev->base + CE_REG_ISR);
}
void aic_crypto_irq_enable(struct aic_crypto_dev *ce_dev, int accel)
{
u32 rval;
rval = readl(ce_dev->base + CE_REG_ICR);
rval |= BIT(accel);
writel(rval, ce_dev->base + CE_REG_ICR);
}
void aic_crypto_irq_disable(struct aic_crypto_dev *ce_dev, int accel)
{
u32 rval;
rval = readl(ce_dev->base + CE_REG_ICR);
rval &= ~(BIT(accel));
writel(rval, ce_dev->base + CE_REG_ICR);
}
void aic_crypto_hardware_reset(struct aic_crypto_dev *dev)
{
if (!IS_ERR(dev->reset)) {
reset_control_assert(dev->reset);
udelay(2);
reset_control_deassert(dev->reset);
udelay(2);
}
}
/*
* Swap byte order in-place
*/
int aic_crypto_bignum_byteswap(u8 *bn, u32 len)
{
u32 i, j;
u8 val;
if (len == 0)
return -1;
i = 0;
j = len - 1;
while (i < j) {
val = bn[i];
bn[i] = bn[j];
bn[j] = val;
i++;
j--;
}
return 0;
}
int aic_crypto_bignum_le2be(u8 *src, u32 slen, u8 *dst, u32 dlen)
{
int i;
memset(dst, 0, dlen);
for (i = 0; i < slen && i < dlen; i++)
dst[dlen - 1 - i] = src[i];
return 0;
}
int aic_crypto_bignum_be2le(u8 *src, u32 slen, u8 *dst, u32 dlen)
{
int i;
memset(dst, 0, dlen);
for (i = 0; i < slen && i < dlen; i++)
dst[i] = src[slen - 1 - i];
return 0;
}
int aic_crypto_enqueue_task(struct aic_crypto_dev *dev, u32 algo,
dma_addr_t phy_task)
{
u32 val;
writel(cpu_to_le32(phy_task), dev->base + CE_REG_TAR);
val = (algo << TASK_ALG_BIT_OFFSET) | (0x1UL << TASK_LOAD_BIT_OFFSET);
writel(val, dev->base + CE_REG_TCR);
return 0;
}
void aic_crypto_sg_copy(void *buf, struct scatterlist *sg, size_t len, int out)
{
struct scatter_walk walk;
if (!len)
return;
scatterwalk_start(&walk, sg);
scatterwalk_advance(&walk, 0);
scatterwalk_copychunks(buf, &walk, len, out);
scatterwalk_done(&walk, out, 0);
}
void aic_crypto_dump_reg(struct aic_crypto_dev *dev)
{
pr_err("0x08 ICR 0x%08x\n", readl(dev->base + CE_REG_ICR));
pr_err("0x0c ISR 0x%08x\n", readl(dev->base + CE_REG_ISR));
pr_err("0x00 TAR 0x%08x\n", readl(dev->base + CE_REG_TAR));
pr_err("0x10 TCR 0x%08x\n", readl(dev->base + CE_REG_TCR));
pr_err("0x14 TSR 0x%08x\n", readl(dev->base + CE_REG_TSR));
pr_err("0x18 TER 0x%08x\n", readl(dev->base + CE_REG_TER));
}
#define is_aes(alg) (((alg) & 0xF0) == 0)
#define is_des(alg) ((((alg) & 0xF0) == 0x10) || (((alg) & 0xF0) == 0x20))
#define is_hash(alg) (((alg) & 0xF0) == 0x40)
void aic_crypto_dump_task(struct task_desc *task, int len)
{
u32 i, count;
count = len / sizeof(struct task_desc);
for (i = 0; i < count; i++) {
pr_err("task: 0x%08lx\n", (unsigned long)task);
if (is_aes(task->alg.alg_tag)) {
pr_err(" alg.alg_tag: %08x\n",
task->alg.aes_ecb.alg_tag);
pr_err(" alg.direction: %u\n",
task->alg.aes_ecb.direction);
pr_err(" alg.key_siz: %u\n",
task->alg.aes_ecb.key_siz);
pr_err(" alg.key_src: %u\n",
task->alg.aes_ecb.key_src);
pr_err(" alg.key_addr: %08x\n",
task->alg.aes_ecb.key_addr);
if (task->alg.alg_tag == ALG_TAG_AES_CBC)
pr_err(" alg.iv_addr: %08x\n",
task->alg.aes_cbc.iv_addr);
if (task->alg.alg_tag == ALG_TAG_AES_CTR) {
pr_err(" alg.ctr_in: %08x\n",
task->alg.aes_ctr.ctr_in_addr);
pr_err(" alg.ctr_out: %08x\n",
task->alg.aes_ctr.ctr_out_addr);
}
if (task->alg.alg_tag == ALG_TAG_AES_CTS)
pr_err(" alg.iv_addr: %08x\n",
task->alg.aes_cts.iv_addr);
if (task->alg.alg_tag == ALG_TAG_AES_XTS)
pr_err(" alg.tweak_addr: %08x\n",
task->alg.aes_xts.tweak_addr);
pr_err(" data.in_addr %08x\n",
task->data.in_addr);
pr_err(" data.in_len %u\n",
task->data.in_len);
pr_err(" data.out_addr %08x\n",
task->data.out_addr);
pr_err(" data.out_len %u\n",
task->data.out_len);
}
if (is_des(task->alg.alg_tag)) {
pr_err(" alg.alg_tag: %08x\n",
task->alg.des_ecb.alg_tag);
pr_err(" alg.direction: %u\n",
task->alg.des_ecb.direction);
pr_err(" alg.key_siz: %u\n",
task->alg.des_ecb.key_siz);
pr_err(" alg.key_src: %u\n",
task->alg.des_ecb.key_src);
pr_err(" alg.key_addr: %08x\n",
task->alg.des_ecb.key_addr);
if ((task->alg.alg_tag == ALG_TAG_DES_CBC) ||
(task->alg.alg_tag == ALG_TAG_TDES_CBC))
pr_err(" alg.iv_addr: %08x\n",
task->alg.des_cbc.iv_addr);
pr_err(" data.in_addr %08x\n",
task->data.in_addr);
pr_err(" data.in_len %u\n",
task->data.in_len);
pr_err(" data.out_addr %08x\n",
task->data.out_addr);
pr_err(" data.out_len %u\n",
task->data.out_len);
}
if (task->alg.alg_tag == ALG_TAG_RSA) {
pr_err(" alg.alg_tag: %08x\n",
task->alg.rsa.alg_tag);
pr_err(" alg.op_siz: %u\n",
task->alg.rsa.op_siz);
pr_err(" alg.m_addr: %08x\n",
task->alg.rsa.m_addr);
pr_err(" alg.d_e_addr: %08x\n",
task->alg.rsa.d_e_addr);
pr_err(" data.in_addr %08x\n",
task->data.in_addr);
pr_err(" data.in_len %u\n",
task->data.in_len);
pr_err(" data.out_addr %08x\n",
task->data.out_addr);
pr_err(" data.out_len %u\n",
task->data.out_len);
}
if (is_hash(task->alg.alg_tag)) {
pr_err(" alg.alg_tag: %08x\n",
task->alg.hmac.alg_tag);
pr_err(" alg.iv_mode: %u\n",
task->alg.hmac.iv_mode);
pr_err(" alg.iv_addr: %08x\n",
task->alg.hmac.iv_addr);
pr_err(" alg.key_addr: %08x\n",
task->alg.hmac.hmac_key_addr);
pr_err(" data.first %u\n",
task->data.first_flag);
pr_err(" data.last %u\n",
task->data.last_flag);
pr_err(" data.total_byte %u\n",
task->data.total_bytelen);
pr_err(" data.in_addr %08x\n",
task->data.in_addr);
pr_err(" data.in_len %u\n",
task->data.in_len);
pr_err(" data.out_addr %08x\n",
task->data.out_addr);
pr_err(" data.out_len %u\n",
task->data.out_len);
}
pr_err(" next: %08x\n\n", task->next);
task++;
}
}
static int aic_crypto_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aic_crypto_dev *ce_dev;
int irq, ret;
ce_dev = devm_kzalloc(dev, sizeof(*ce_dev), GFP_KERNEL);
if (!ce_dev)
return -ENOMEM;
ce_dev->dev = dev;
ce_dev->task_count = 0;
ce_dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ce_dev->base))
return PTR_ERR(ce_dev->base);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(dev, irq, aic_crypto_irq_handler,
aic_crypto_irq_thread, IRQF_ONESHOT,
dev_name(dev), ce_dev);
if (ret) {
dev_err(dev, "Request IRQ failed.\n");
return ret;
}
ce_dev->clk = devm_clk_get(dev, NULL);
if (IS_ERR(ce_dev->clk)) {
dev_err(dev, "Get clock failed.\n");
return PTR_ERR(ce_dev->clk);
}
ret = of_property_read_u32(dev->of_node, "clock-rate", &ce_dev->clk_rate);
if (ret) {
dev_err(dev, "Can't parse clock-rate\n");
return ret;
}
clk_set_rate(ce_dev->clk, ce_dev->clk_rate);
ret = clk_prepare_enable(ce_dev->clk);
if (ret) {
dev_err(ce_dev->dev, "Failed to enable clock\n");
return ret;
}
ce_dev->reset = devm_reset_control_get(dev, NULL);
if (!IS_ERR(ce_dev->reset)) {
reset_control_assert(ce_dev->reset);
udelay(2);
reset_control_deassert(ce_dev->reset);
}
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
platform_set_drvdata(pdev, ce_dev);
mutex_init(&ce_dev->ssram_lock);
ret = aic_crypto_skcipher_accelerator_init(ce_dev);
if (ret) {
dev_err(ce_dev->dev, "Failed to init skcipher accelerator\n");
return ret;
}
ret = aic_crypto_akcipher_accelerator_init(ce_dev);
if (ret) {
dev_err(ce_dev->dev, "Failed to init akcipher accelerator\n");
return ret;
}
ret = aic_crypto_hash_accelerator_init(ce_dev);
if (ret) {
dev_err(ce_dev->dev, "Failed to init hash accelerator\n");
return ret;
}
return ret;
}
static int aic_crypto_remove(struct platform_device *pdev)
{
struct aic_crypto_dev *ce_dev = platform_get_drvdata(pdev);
if (!ce_dev)
return -ENODEV;
aic_crypto_skcipher_accelerator_exit(ce_dev);
aic_crypto_akcipher_accelerator_exit(ce_dev);
aic_crypto_skcipher_accelerator_exit(ce_dev);
clk_disable_unprepare(ce_dev->clk);
return 0;
}
static const struct of_device_id aic_dt_ids[] = {
{ .compatible = "artinchip,aic-crypto-v1.0" },
{},
};
#ifdef CONFIG_PM
static int aic_crypto_runtime_suspend(struct device *dev)
{
struct aic_crypto_dev *ce_dev = dev_get_drvdata(dev);
clk_disable_unprepare(ce_dev->clk);
return 0;
}
static int aic_crypto_runtime_resume(struct device *dev)
{
struct aic_crypto_dev *ce_dev = dev_get_drvdata(dev);
clk_prepare_enable(ce_dev->clk);
return 0;
}
static const struct dev_pm_ops aic_crypto_pm_ops = {
SET_RUNTIME_PM_OPS(
aic_crypto_runtime_suspend,
aic_crypto_runtime_resume,
NULL) };
#define AIC_CRYPTO_DEV_PM_OPS (&aic_crypto_pm_ops)
#else
#define AIC_CRYPTO_DEV_PM_OPS NULL
#endif
MODULE_DEVICE_TABLE(of, aic_dt_ids);
static struct platform_driver aic_crypto_driver = {
.probe = aic_crypto_probe,
.remove = aic_crypto_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = aic_dt_ids,
.pm = AIC_CRYPTO_DEV_PM_OPS,
},
};
module_platform_driver(aic_crypto_driver);
MODULE_AUTHOR("Wu Dehuang <dehuang.wu@artinchip.com>");
MODULE_DESCRIPTION("Artinchip crypto engine driver");
MODULE_LICENSE("GPL");