468 lines
11 KiB
C
468 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2019 - 2021
|
|
*
|
|
* Richard van Schagen <vschagen@icloud.com>
|
|
*/
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#include "eip93-main.h"
|
|
#include "eip93-regs.h"
|
|
#include "eip93-common.h"
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_SKCIPHER)
|
|
#include "eip93-cipher.h"
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AES)
|
|
#include "eip93-aes.h"
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_DES)
|
|
#include "eip93-des.h"
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AEAD)
|
|
#include "eip93-aead.h"
|
|
#endif
|
|
|
|
static struct mtk_alg_template *mtk_algs[] = {
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_DES)
|
|
&mtk_alg_ecb_des,
|
|
&mtk_alg_cbc_des,
|
|
&mtk_alg_ecb_des3_ede,
|
|
&mtk_alg_cbc_des3_ede,
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AES)
|
|
&mtk_alg_ecb_aes,
|
|
&mtk_alg_cbc_aes,
|
|
&mtk_alg_ctr_aes,
|
|
&mtk_alg_rfc3686_aes,
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AEAD)
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_DES)
|
|
&mtk_alg_authenc_hmac_md5_cbc_des,
|
|
&mtk_alg_authenc_hmac_sha1_cbc_des,
|
|
&mtk_alg_authenc_hmac_sha224_cbc_des,
|
|
&mtk_alg_authenc_hmac_sha256_cbc_des,
|
|
&mtk_alg_authenc_hmac_md5_cbc_des3_ede,
|
|
&mtk_alg_authenc_hmac_sha1_cbc_des3_ede,
|
|
&mtk_alg_authenc_hmac_sha224_cbc_des3_ede,
|
|
&mtk_alg_authenc_hmac_sha256_cbc_des3_ede,
|
|
#endif
|
|
&mtk_alg_authenc_hmac_md5_cbc_aes,
|
|
&mtk_alg_authenc_hmac_sha1_cbc_aes,
|
|
&mtk_alg_authenc_hmac_sha224_cbc_aes,
|
|
&mtk_alg_authenc_hmac_sha256_cbc_aes,
|
|
&mtk_alg_authenc_hmac_md5_rfc3686_aes,
|
|
&mtk_alg_authenc_hmac_sha1_rfc3686_aes,
|
|
&mtk_alg_authenc_hmac_sha224_rfc3686_aes,
|
|
&mtk_alg_authenc_hmac_sha256_rfc3686_aes,
|
|
#endif
|
|
};
|
|
|
|
inline void mtk_irq_disable(struct mtk_device *mtk, u32 mask)
|
|
{
|
|
__raw_writel(mask, mtk->base + EIP93_REG_MASK_DISABLE);
|
|
}
|
|
|
|
inline void mtk_irq_enable(struct mtk_device *mtk, u32 mask)
|
|
{
|
|
__raw_writel(mask, mtk->base + EIP93_REG_MASK_ENABLE);
|
|
}
|
|
|
|
inline void mtk_irq_clear(struct mtk_device *mtk, u32 mask)
|
|
{
|
|
__raw_writel(mask, mtk->base + EIP93_REG_INT_CLR);
|
|
}
|
|
|
|
static void mtk_unregister_algs(unsigned int i)
|
|
{
|
|
unsigned int j;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
switch (mtk_algs[j]->type) {
|
|
case MTK_ALG_TYPE_SKCIPHER:
|
|
crypto_unregister_skcipher(&mtk_algs[j]->alg.skcipher);
|
|
break;
|
|
case MTK_ALG_TYPE_AEAD:
|
|
crypto_unregister_aead(&mtk_algs[j]->alg.aead);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mtk_register_algs(struct mtk_device *mtk)
|
|
{
|
|
unsigned int i;
|
|
int err = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mtk_algs); i++) {
|
|
mtk_algs[i]->mtk = mtk;
|
|
|
|
switch (mtk_algs[i]->type) {
|
|
case MTK_ALG_TYPE_SKCIPHER:
|
|
err = crypto_register_skcipher(&mtk_algs[i]->alg.skcipher);
|
|
break;
|
|
case MTK_ALG_TYPE_AEAD:
|
|
err = crypto_register_aead(&mtk_algs[i]->alg.aead);
|
|
break;
|
|
}
|
|
if (err)
|
|
goto fail;
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
mtk_unregister_algs(i);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void mtk_handle_result_descriptor(struct mtk_device *mtk)
|
|
{
|
|
struct crypto_async_request *async;
|
|
struct eip93_descriptor_s *rdesc;
|
|
bool last_entry;
|
|
u32 flags;
|
|
int handled, ready, err;
|
|
union peCrtlStat_w done1;
|
|
union peLength_w done2;
|
|
|
|
get_more:
|
|
handled = 0;
|
|
|
|
ready = readl(mtk->base + EIP93_REG_PE_RD_COUNT) & GENMASK(10, 0);
|
|
|
|
if (!ready) {
|
|
mtk_irq_clear(mtk, EIP93_INT_PE_RDRTHRESH_REQ);
|
|
mtk_irq_enable(mtk, EIP93_INT_PE_RDRTHRESH_REQ);
|
|
return;
|
|
}
|
|
|
|
last_entry = false;
|
|
|
|
while (ready) {
|
|
rdesc = mtk_get_descriptor(mtk);
|
|
if (IS_ERR(rdesc)) {
|
|
dev_err(mtk->dev, "Ndesc: %d nreq: %d\n",
|
|
handled, ready);
|
|
err = -EIO;
|
|
break;
|
|
}
|
|
/* make sure DMA is finished writing */
|
|
do {
|
|
done1.word = READ_ONCE(rdesc->peCrtlStat.word);
|
|
done2.word = READ_ONCE(rdesc->peLength.word);
|
|
} while ((!done1.bits.peReady) || (!done2.bits.peReady));
|
|
|
|
err = rdesc->peCrtlStat.bits.errStatus;
|
|
|
|
flags = rdesc->userId;
|
|
async = (struct crypto_async_request *)rdesc->arc4Addr;
|
|
|
|
writel(1, mtk->base + EIP93_REG_PE_RD_COUNT);
|
|
mtk_irq_clear(mtk, EIP93_INT_PE_RDRTHRESH_REQ);
|
|
|
|
handled++;
|
|
ready--;
|
|
|
|
if (flags & MTK_DESC_LAST) {
|
|
last_entry = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!last_entry)
|
|
goto get_more;
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_SKCIPHER)
|
|
if (flags & MTK_DESC_SKCIPHER)
|
|
mtk_skcipher_handle_result(async, err);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AEAD)
|
|
if (flags & MTK_DESC_AEAD)
|
|
mtk_aead_handle_result(async, err);
|
|
#endif
|
|
goto get_more;
|
|
}
|
|
|
|
static void mtk_done_task(unsigned long data)
|
|
{
|
|
struct mtk_device *mtk = (struct mtk_device *)data;
|
|
|
|
mtk_handle_result_descriptor(mtk);
|
|
}
|
|
|
|
static irqreturn_t mtk_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct mtk_device *mtk = (struct mtk_device *)dev_id;
|
|
u32 irq_status;
|
|
|
|
irq_status = readl(mtk->base + EIP93_REG_INT_MASK_STAT);
|
|
|
|
if (irq_status & EIP93_INT_PE_RDRTHRESH_REQ) {
|
|
mtk_irq_disable(mtk, EIP93_INT_PE_RDRTHRESH_REQ);
|
|
tasklet_schedule(&mtk->ring->done_task);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
mtk_irq_clear(mtk, irq_status);
|
|
if (irq_status)
|
|
mtk_irq_disable(mtk, irq_status);
|
|
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
static void mtk_initialize(struct mtk_device *mtk)
|
|
{
|
|
union peConfig_w peConfig;
|
|
union peEndianCfg_w peEndianCfg;
|
|
union peIntCfg_w peIntCfg;
|
|
union peClockCfg_w peClockCfg;
|
|
union peBufThresh_w peBufThresh;
|
|
union peRingThresh_w peRingThresh;
|
|
|
|
/* Reset Engine and setup Mode */
|
|
peConfig.word = 0;
|
|
peConfig.bits.resetPE = 1;
|
|
peConfig.bits.resetRing = 1;
|
|
peConfig.bits.peMode = 3;
|
|
peConfig.bits.enCDRupdate = 1;
|
|
|
|
writel(peConfig.word, mtk->base + EIP93_REG_PE_CONFIG);
|
|
|
|
udelay(10);
|
|
|
|
peConfig.bits.resetPE = 0;
|
|
peConfig.bits.resetRing = 0;
|
|
|
|
writel(peConfig.word, mtk->base + EIP93_REG_PE_CONFIG);
|
|
|
|
/* Initialize the BYTE_ORDER_CFG register */
|
|
peEndianCfg.word = 0;
|
|
writel(peEndianCfg.word, mtk->base + EIP93_REG_PE_ENDIAN_CONFIG);
|
|
|
|
/* Initialize the INT_CFG register */
|
|
peIntCfg.word = 0;
|
|
writel(peIntCfg.word, mtk->base + EIP93_REG_INT_CFG);
|
|
|
|
/* Config Clocks */
|
|
peClockCfg.word = 0;
|
|
peClockCfg.bits.enPEclk = 1;
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_DES)
|
|
peClockCfg.bits.enDESclk = 1;
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_AES)
|
|
peClockCfg.bits.enAESclk = 1;
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CRYPTO_DEV_EIP93_HMAC)
|
|
peClockCfg.bits.enHASHclk = 1;
|
|
#endif
|
|
writel(peClockCfg.word, mtk->base + EIP93_REG_PE_CLOCK_CTRL);
|
|
|
|
/* Config DMA thresholds */
|
|
peBufThresh.word = 0;
|
|
peBufThresh.bits.inputBuffer = 128;
|
|
peBufThresh.bits.outputBuffer = 128;
|
|
|
|
writel(peBufThresh.word, mtk->base + EIP93_REG_PE_BUF_THRESH);
|
|
|
|
/* Clear/ack all interrupts before disable all */
|
|
mtk_irq_clear(mtk, 0xFFFFFFFF);
|
|
mtk_irq_disable(mtk, 0xFFFFFFFF);
|
|
|
|
/* Config Ring Threshold */
|
|
peRingThresh.word = 0;
|
|
peRingThresh.bits.CDRThresh = MTK_RING_SIZE - MTK_RING_BUSY;
|
|
peRingThresh.bits.RDRThresh = 0;
|
|
peRingThresh.bits.RDTimeout = 5;
|
|
peRingThresh.bits.enTimeout = 1;
|
|
|
|
writel(peRingThresh.word, mtk->base + EIP93_REG_PE_RING_THRESH);
|
|
}
|
|
|
|
static void mtk_desc_free(struct mtk_device *mtk)
|
|
{
|
|
writel(0, mtk->base + EIP93_REG_PE_RING_CONFIG);
|
|
writel(0, mtk->base + EIP93_REG_PE_CDR_BASE);
|
|
writel(0, mtk->base + EIP93_REG_PE_RDR_BASE);
|
|
}
|
|
|
|
static int mtk_set_ring(struct mtk_device *mtk, struct mtk_desc_ring *ring,
|
|
int Offset)
|
|
{
|
|
ring->offset = Offset;
|
|
ring->base = dmam_alloc_coherent(mtk->dev, Offset * MTK_RING_SIZE,
|
|
&ring->base_dma, GFP_KERNEL);
|
|
if (!ring->base)
|
|
return -ENOMEM;
|
|
|
|
ring->write = ring->base;
|
|
ring->base_end = ring->base + Offset * (MTK_RING_SIZE - 1);
|
|
ring->read = ring->base;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_desc_init(struct mtk_device *mtk)
|
|
{
|
|
struct mtk_state_pool *saState_pool;
|
|
struct mtk_desc_ring *cdr = &mtk->ring->cdr;
|
|
struct mtk_desc_ring *rdr = &mtk->ring->rdr;
|
|
union peRingCfg_w peRingCfg;
|
|
int RingOffset, err, i;
|
|
|
|
RingOffset = sizeof(struct eip93_descriptor_s);
|
|
|
|
err = mtk_set_ring(mtk, cdr, RingOffset);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mtk_set_ring(mtk, rdr, RingOffset);
|
|
if (err)
|
|
return err;
|
|
|
|
writel((u32)cdr->base_dma, mtk->base + EIP93_REG_PE_CDR_BASE);
|
|
writel((u32)rdr->base_dma, mtk->base + EIP93_REG_PE_RDR_BASE);
|
|
|
|
peRingCfg.word = 0;
|
|
peRingCfg.bits.ringSize = MTK_RING_SIZE - 1;
|
|
peRingCfg.bits.ringOffset = RingOffset / 4;
|
|
|
|
writel(peRingCfg.word, mtk->base + EIP93_REG_PE_RING_CONFIG);
|
|
|
|
atomic_set(&mtk->ring->free, MTK_RING_SIZE - 1);
|
|
/* Create State record DMA pool */
|
|
RingOffset = sizeof(struct saState_s);
|
|
mtk->ring->saState = dmam_alloc_coherent(mtk->dev,
|
|
RingOffset * MTK_RING_SIZE,
|
|
&mtk->ring->saState_dma, GFP_KERNEL);
|
|
if (!mtk->ring->saState)
|
|
return -ENOMEM;
|
|
|
|
mtk->ring->saState_pool = devm_kcalloc(mtk->dev, 1,
|
|
sizeof(struct mtk_state_pool) * MTK_RING_SIZE,
|
|
GFP_KERNEL);
|
|
|
|
for (i = 0; i < MTK_RING_SIZE; i++) {
|
|
saState_pool = &mtk->ring->saState_pool[i];
|
|
saState_pool->base = mtk->ring->saState + (i * RingOffset);
|
|
saState_pool->base_dma = mtk->ring->saState_dma + (i * RingOffset);
|
|
saState_pool->in_use = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_cleanup(struct mtk_device *mtk)
|
|
{
|
|
tasklet_kill(&mtk->ring->done_task);
|
|
|
|
/* Clear/ack all interrupts before disable all */
|
|
mtk_irq_clear(mtk, 0xFFFFFFFF);
|
|
mtk_irq_disable(mtk, 0xFFFFFFFF);
|
|
|
|
writel(0, mtk->base + EIP93_REG_PE_CLOCK_CTRL);
|
|
|
|
mtk_desc_free(mtk);
|
|
}
|
|
|
|
static int mtk_crypto_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct mtk_device *mtk;
|
|
struct resource *res;
|
|
int err;
|
|
|
|
mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL);
|
|
if (!mtk)
|
|
return -ENOMEM;
|
|
|
|
mtk->dev = dev;
|
|
platform_set_drvdata(pdev, mtk);
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
mtk->base = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
if (IS_ERR(mtk->base))
|
|
return PTR_ERR(mtk->base);
|
|
|
|
mtk->irq = platform_get_irq(pdev, 0);
|
|
|
|
if (mtk->irq < 0)
|
|
return mtk->irq;
|
|
|
|
err = devm_request_threaded_irq(mtk->dev, mtk->irq, mtk_irq_handler,
|
|
NULL, IRQF_ONESHOT,
|
|
dev_name(mtk->dev), mtk);
|
|
|
|
mtk->ring = devm_kcalloc(mtk->dev, 1, sizeof(*mtk->ring), GFP_KERNEL);
|
|
|
|
if (!mtk->ring)
|
|
return -ENOMEM;
|
|
|
|
err = mtk_desc_init(mtk);
|
|
if (err)
|
|
return err;
|
|
|
|
tasklet_init(&mtk->ring->done_task, mtk_done_task, (unsigned long)mtk);
|
|
|
|
spin_lock_init(&mtk->ring->read_lock);
|
|
spin_lock_init(&mtk->ring->write_lock);
|
|
|
|
mtk_initialize(mtk);
|
|
|
|
/* Init. finished, enable RDR interupt */
|
|
mtk_irq_enable(mtk, EIP93_INT_PE_RDRTHRESH_REQ);
|
|
|
|
err = mtk_register_algs(mtk);
|
|
if (err) {
|
|
mtk_cleanup(mtk);
|
|
return err;
|
|
}
|
|
|
|
dev_info(mtk->dev, "EIP93 Crypto Engine Initialized.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_crypto_remove(struct platform_device *pdev)
|
|
{
|
|
struct mtk_device *mtk = platform_get_drvdata(pdev);
|
|
|
|
mtk_unregister_algs(ARRAY_SIZE(mtk_algs));
|
|
mtk_cleanup(mtk);
|
|
dev_info(mtk->dev, "EIP93 removed.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_OF)
|
|
static const struct of_device_id mtk_crypto_of_match[] = {
|
|
{ .compatible = "mediatek,mtk-eip93", },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, mtk_crypto_of_match);
|
|
#endif
|
|
|
|
static struct platform_driver mtk_crypto_driver = {
|
|
.probe = mtk_crypto_probe,
|
|
.remove = mtk_crypto_remove,
|
|
.driver = {
|
|
.name = "mtk-eip93",
|
|
.of_match_table = of_match_ptr(mtk_crypto_of_match),
|
|
},
|
|
};
|
|
module_platform_driver(mtk_crypto_driver);
|
|
|
|
MODULE_AUTHOR("Richard van Schagen <vschagen@cs.com>");
|
|
MODULE_ALIAS("platform:" KBUILD_MODNAME);
|
|
MODULE_DESCRIPTION("Mediatek EIP-93 crypto engine driver");
|
|
MODULE_LICENSE("GPL v2");
|