mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
7.6 KiB
317 lines
7.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* amlgoic-core.c - hardware cryptographic offloader for Amlogic GXL SoC |
|
* |
|
* Copyright (C) 2018-2019 Corentin Labbe <[email protected]> |
|
* |
|
* Core file which registers crypto algorithms supported by the hardware. |
|
*/ |
|
#include <linux/clk.h> |
|
#include <linux/crypto.h> |
|
#include <linux/io.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/irq.h> |
|
#include <linux/module.h> |
|
#include <linux/of.h> |
|
#include <linux/of_device.h> |
|
#include <linux/platform_device.h> |
|
#include <crypto/internal/skcipher.h> |
|
#include <linux/dma-mapping.h> |
|
|
|
#include "amlogic-gxl.h" |
|
|
|
static irqreturn_t meson_irq_handler(int irq, void *data) |
|
{ |
|
struct meson_dev *mc = (struct meson_dev *)data; |
|
int flow; |
|
u32 p; |
|
|
|
for (flow = 0; flow < MAXFLOW; flow++) { |
|
if (mc->irqs[flow] == irq) { |
|
p = readl(mc->base + ((0x04 + flow) << 2)); |
|
if (p) { |
|
writel_relaxed(0xF, mc->base + ((0x4 + flow) << 2)); |
|
mc->chanlist[flow].status = 1; |
|
complete(&mc->chanlist[flow].complete); |
|
return IRQ_HANDLED; |
|
} |
|
dev_err(mc->dev, "%s %d Got irq for flow %d but ctrl is empty\n", __func__, irq, flow); |
|
} |
|
} |
|
|
|
dev_err(mc->dev, "%s %d from unknown irq\n", __func__, irq); |
|
return IRQ_HANDLED; |
|
} |
|
|
|
static struct meson_alg_template mc_algs[] = { |
|
{ |
|
.type = CRYPTO_ALG_TYPE_SKCIPHER, |
|
.blockmode = MESON_OPMODE_CBC, |
|
.alg.skcipher = { |
|
.base = { |
|
.cra_name = "cbc(aes)", |
|
.cra_driver_name = "cbc-aes-gxl", |
|
.cra_priority = 400, |
|
.cra_blocksize = AES_BLOCK_SIZE, |
|
.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | |
|
CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | |
|
CRYPTO_ALG_NEED_FALLBACK, |
|
.cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), |
|
.cra_module = THIS_MODULE, |
|
.cra_alignmask = 0xf, |
|
.cra_init = meson_cipher_init, |
|
.cra_exit = meson_cipher_exit, |
|
}, |
|
.min_keysize = AES_MIN_KEY_SIZE, |
|
.max_keysize = AES_MAX_KEY_SIZE, |
|
.ivsize = AES_BLOCK_SIZE, |
|
.setkey = meson_aes_setkey, |
|
.encrypt = meson_skencrypt, |
|
.decrypt = meson_skdecrypt, |
|
} |
|
}, |
|
{ |
|
.type = CRYPTO_ALG_TYPE_SKCIPHER, |
|
.blockmode = MESON_OPMODE_ECB, |
|
.alg.skcipher = { |
|
.base = { |
|
.cra_name = "ecb(aes)", |
|
.cra_driver_name = "ecb-aes-gxl", |
|
.cra_priority = 400, |
|
.cra_blocksize = AES_BLOCK_SIZE, |
|
.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | |
|
CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | |
|
CRYPTO_ALG_NEED_FALLBACK, |
|
.cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), |
|
.cra_module = THIS_MODULE, |
|
.cra_alignmask = 0xf, |
|
.cra_init = meson_cipher_init, |
|
.cra_exit = meson_cipher_exit, |
|
}, |
|
.min_keysize = AES_MIN_KEY_SIZE, |
|
.max_keysize = AES_MAX_KEY_SIZE, |
|
.setkey = meson_aes_setkey, |
|
.encrypt = meson_skencrypt, |
|
.decrypt = meson_skdecrypt, |
|
} |
|
}, |
|
}; |
|
|
|
#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG |
|
static int meson_debugfs_show(struct seq_file *seq, void *v) |
|
{ |
|
struct meson_dev *mc = seq->private; |
|
int i; |
|
|
|
for (i = 0; i < MAXFLOW; i++) |
|
seq_printf(seq, "Channel %d: nreq %lu\n", i, mc->chanlist[i].stat_req); |
|
|
|
for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { |
|
switch (mc_algs[i].type) { |
|
case CRYPTO_ALG_TYPE_SKCIPHER: |
|
seq_printf(seq, "%s %s %lu %lu\n", |
|
mc_algs[i].alg.skcipher.base.cra_driver_name, |
|
mc_algs[i].alg.skcipher.base.cra_name, |
|
mc_algs[i].stat_req, mc_algs[i].stat_fb); |
|
break; |
|
} |
|
} |
|
return 0; |
|
} |
|
DEFINE_SHOW_ATTRIBUTE(meson_debugfs); |
|
#endif |
|
|
|
static void meson_free_chanlist(struct meson_dev *mc, int i) |
|
{ |
|
while (i >= 0) { |
|
crypto_engine_exit(mc->chanlist[i].engine); |
|
if (mc->chanlist[i].tl) |
|
dma_free_coherent(mc->dev, sizeof(struct meson_desc) * MAXDESC, |
|
mc->chanlist[i].tl, |
|
mc->chanlist[i].t_phy); |
|
i--; |
|
} |
|
} |
|
|
|
/* |
|
* Allocate the channel list structure |
|
*/ |
|
static int meson_allocate_chanlist(struct meson_dev *mc) |
|
{ |
|
int i, err; |
|
|
|
mc->chanlist = devm_kcalloc(mc->dev, MAXFLOW, |
|
sizeof(struct meson_flow), GFP_KERNEL); |
|
if (!mc->chanlist) |
|
return -ENOMEM; |
|
|
|
for (i = 0; i < MAXFLOW; i++) { |
|
init_completion(&mc->chanlist[i].complete); |
|
|
|
mc->chanlist[i].engine = crypto_engine_alloc_init(mc->dev, true); |
|
if (!mc->chanlist[i].engine) { |
|
dev_err(mc->dev, "Cannot allocate engine\n"); |
|
i--; |
|
err = -ENOMEM; |
|
goto error_engine; |
|
} |
|
err = crypto_engine_start(mc->chanlist[i].engine); |
|
if (err) { |
|
dev_err(mc->dev, "Cannot start engine\n"); |
|
goto error_engine; |
|
} |
|
mc->chanlist[i].tl = dma_alloc_coherent(mc->dev, |
|
sizeof(struct meson_desc) * MAXDESC, |
|
&mc->chanlist[i].t_phy, |
|
GFP_KERNEL); |
|
if (!mc->chanlist[i].tl) { |
|
err = -ENOMEM; |
|
goto error_engine; |
|
} |
|
} |
|
return 0; |
|
error_engine: |
|
meson_free_chanlist(mc, i); |
|
return err; |
|
} |
|
|
|
static int meson_register_algs(struct meson_dev *mc) |
|
{ |
|
int err, i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { |
|
mc_algs[i].mc = mc; |
|
switch (mc_algs[i].type) { |
|
case CRYPTO_ALG_TYPE_SKCIPHER: |
|
err = crypto_register_skcipher(&mc_algs[i].alg.skcipher); |
|
if (err) { |
|
dev_err(mc->dev, "Fail to register %s\n", |
|
mc_algs[i].alg.skcipher.base.cra_name); |
|
mc_algs[i].mc = NULL; |
|
return err; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void meson_unregister_algs(struct meson_dev *mc) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { |
|
if (!mc_algs[i].mc) |
|
continue; |
|
switch (mc_algs[i].type) { |
|
case CRYPTO_ALG_TYPE_SKCIPHER: |
|
crypto_unregister_skcipher(&mc_algs[i].alg.skcipher); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
static int meson_crypto_probe(struct platform_device *pdev) |
|
{ |
|
struct meson_dev *mc; |
|
int err, i; |
|
|
|
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); |
|
if (!mc) |
|
return -ENOMEM; |
|
|
|
mc->dev = &pdev->dev; |
|
platform_set_drvdata(pdev, mc); |
|
|
|
mc->base = devm_platform_ioremap_resource(pdev, 0); |
|
if (IS_ERR(mc->base)) { |
|
err = PTR_ERR(mc->base); |
|
dev_err(&pdev->dev, "Cannot request MMIO err=%d\n", err); |
|
return err; |
|
} |
|
mc->busclk = devm_clk_get(&pdev->dev, "blkmv"); |
|
if (IS_ERR(mc->busclk)) { |
|
err = PTR_ERR(mc->busclk); |
|
dev_err(&pdev->dev, "Cannot get core clock err=%d\n", err); |
|
return err; |
|
} |
|
|
|
mc->irqs = devm_kcalloc(mc->dev, MAXFLOW, sizeof(int), GFP_KERNEL); |
|
for (i = 0; i < MAXFLOW; i++) { |
|
mc->irqs[i] = platform_get_irq(pdev, i); |
|
if (mc->irqs[i] < 0) |
|
return mc->irqs[i]; |
|
|
|
err = devm_request_irq(&pdev->dev, mc->irqs[i], meson_irq_handler, 0, |
|
"gxl-crypto", mc); |
|
if (err < 0) { |
|
dev_err(mc->dev, "Cannot request IRQ for flow %d\n", i); |
|
return err; |
|
} |
|
} |
|
|
|
err = clk_prepare_enable(mc->busclk); |
|
if (err != 0) { |
|
dev_err(&pdev->dev, "Cannot prepare_enable busclk\n"); |
|
return err; |
|
} |
|
|
|
err = meson_allocate_chanlist(mc); |
|
if (err) |
|
goto error_flow; |
|
|
|
err = meson_register_algs(mc); |
|
if (err) |
|
goto error_alg; |
|
|
|
#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG |
|
mc->dbgfs_dir = debugfs_create_dir("gxl-crypto", NULL); |
|
debugfs_create_file("stats", 0444, mc->dbgfs_dir, mc, &meson_debugfs_fops); |
|
#endif |
|
|
|
return 0; |
|
error_alg: |
|
meson_unregister_algs(mc); |
|
error_flow: |
|
meson_free_chanlist(mc, MAXFLOW - 1); |
|
clk_disable_unprepare(mc->busclk); |
|
return err; |
|
} |
|
|
|
static int meson_crypto_remove(struct platform_device *pdev) |
|
{ |
|
struct meson_dev *mc = platform_get_drvdata(pdev); |
|
|
|
#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG |
|
debugfs_remove_recursive(mc->dbgfs_dir); |
|
#endif |
|
|
|
meson_unregister_algs(mc); |
|
|
|
meson_free_chanlist(mc, MAXFLOW - 1); |
|
|
|
clk_disable_unprepare(mc->busclk); |
|
return 0; |
|
} |
|
|
|
static const struct of_device_id meson_crypto_of_match_table[] = { |
|
{ .compatible = "amlogic,gxl-crypto", }, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(of, meson_crypto_of_match_table); |
|
|
|
static struct platform_driver meson_crypto_driver = { |
|
.probe = meson_crypto_probe, |
|
.remove = meson_crypto_remove, |
|
.driver = { |
|
.name = "gxl-crypto", |
|
.of_match_table = meson_crypto_of_match_table, |
|
}, |
|
}; |
|
|
|
module_platform_driver(meson_crypto_driver); |
|
|
|
MODULE_DESCRIPTION("Amlogic GXL cryptographic offloader"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_AUTHOR("Corentin Labbe <[email protected]>");
|
|
|