mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 12:55:04 +07:00
2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
449 lines
12 KiB
C
449 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* sun4i-ss-core.c - hardware cryptographic accelerator for Allwinner A20 SoC
|
|
*
|
|
* Copyright (C) 2013-2015 Corentin LABBE <clabbe.montjoie@gmail.com>
|
|
*
|
|
* Core file which registers crypto algorithms supported by the SS.
|
|
*
|
|
* You could find a link for the datasheet in Documentation/arm/sunxi/README
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <crypto/scatterwalk.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/reset.h>
|
|
|
|
#include "sun4i-ss.h"
|
|
|
|
static struct sun4i_ss_alg_template ss_algs[] = {
|
|
{ .type = CRYPTO_ALG_TYPE_AHASH,
|
|
.mode = SS_OP_MD5,
|
|
.alg.hash = {
|
|
.init = sun4i_hash_init,
|
|
.update = sun4i_hash_update,
|
|
.final = sun4i_hash_final,
|
|
.finup = sun4i_hash_finup,
|
|
.digest = sun4i_hash_digest,
|
|
.export = sun4i_hash_export_md5,
|
|
.import = sun4i_hash_import_md5,
|
|
.halg = {
|
|
.digestsize = MD5_DIGEST_SIZE,
|
|
.statesize = sizeof(struct md5_state),
|
|
.base = {
|
|
.cra_name = "md5",
|
|
.cra_driver_name = "md5-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_alignmask = 3,
|
|
.cra_blocksize = MD5_HMAC_BLOCK_SIZE,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_init = sun4i_hash_crainit
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_AHASH,
|
|
.mode = SS_OP_SHA1,
|
|
.alg.hash = {
|
|
.init = sun4i_hash_init,
|
|
.update = sun4i_hash_update,
|
|
.final = sun4i_hash_final,
|
|
.finup = sun4i_hash_finup,
|
|
.digest = sun4i_hash_digest,
|
|
.export = sun4i_hash_export_sha1,
|
|
.import = sun4i_hash_import_sha1,
|
|
.halg = {
|
|
.digestsize = SHA1_DIGEST_SIZE,
|
|
.statesize = sizeof(struct sha1_state),
|
|
.base = {
|
|
.cra_name = "sha1",
|
|
.cra_driver_name = "sha1-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_alignmask = 3,
|
|
.cra_blocksize = SHA1_BLOCK_SIZE,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_init = sun4i_hash_crainit
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_aes_setkey,
|
|
.encrypt = sun4i_ss_cbc_aes_encrypt,
|
|
.decrypt = sun4i_ss_cbc_aes_decrypt,
|
|
.min_keysize = AES_MIN_KEY_SIZE,
|
|
.max_keysize = AES_MAX_KEY_SIZE,
|
|
.ivsize = AES_BLOCK_SIZE,
|
|
.base = {
|
|
.cra_name = "cbc(aes)",
|
|
.cra_driver_name = "cbc-aes-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = AES_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_tfm_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_aes_setkey,
|
|
.encrypt = sun4i_ss_ecb_aes_encrypt,
|
|
.decrypt = sun4i_ss_ecb_aes_decrypt,
|
|
.min_keysize = AES_MIN_KEY_SIZE,
|
|
.max_keysize = AES_MAX_KEY_SIZE,
|
|
.base = {
|
|
.cra_name = "ecb(aes)",
|
|
.cra_driver_name = "ecb-aes-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = AES_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_tfm_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_des_setkey,
|
|
.encrypt = sun4i_ss_cbc_des_encrypt,
|
|
.decrypt = sun4i_ss_cbc_des_decrypt,
|
|
.min_keysize = DES_KEY_SIZE,
|
|
.max_keysize = DES_KEY_SIZE,
|
|
.ivsize = DES_BLOCK_SIZE,
|
|
.base = {
|
|
.cra_name = "cbc(des)",
|
|
.cra_driver_name = "cbc-des-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = DES_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_des_setkey,
|
|
.encrypt = sun4i_ss_ecb_des_encrypt,
|
|
.decrypt = sun4i_ss_ecb_des_decrypt,
|
|
.min_keysize = DES_KEY_SIZE,
|
|
.max_keysize = DES_KEY_SIZE,
|
|
.base = {
|
|
.cra_name = "ecb(des)",
|
|
.cra_driver_name = "ecb-des-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = DES_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_des3_setkey,
|
|
.encrypt = sun4i_ss_cbc_des3_encrypt,
|
|
.decrypt = sun4i_ss_cbc_des3_decrypt,
|
|
.min_keysize = DES3_EDE_KEY_SIZE,
|
|
.max_keysize = DES3_EDE_KEY_SIZE,
|
|
.ivsize = DES3_EDE_BLOCK_SIZE,
|
|
.base = {
|
|
.cra_name = "cbc(des3_ede)",
|
|
.cra_driver_name = "cbc-des3-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
{ .type = CRYPTO_ALG_TYPE_SKCIPHER,
|
|
.alg.crypto = {
|
|
.setkey = sun4i_ss_des3_setkey,
|
|
.encrypt = sun4i_ss_ecb_des3_encrypt,
|
|
.decrypt = sun4i_ss_ecb_des3_decrypt,
|
|
.min_keysize = DES3_EDE_KEY_SIZE,
|
|
.max_keysize = DES3_EDE_KEY_SIZE,
|
|
.base = {
|
|
.cra_name = "ecb(des3_ede)",
|
|
.cra_driver_name = "ecb-des3-sun4i-ss",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
|
|
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_NEED_FALLBACK,
|
|
.cra_ctxsize = sizeof(struct sun4i_req_ctx),
|
|
.cra_module = THIS_MODULE,
|
|
.cra_alignmask = 3,
|
|
.cra_init = sun4i_ss_cipher_init,
|
|
.cra_exit = sun4i_ss_cipher_exit,
|
|
}
|
|
}
|
|
},
|
|
#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG
|
|
{
|
|
.type = CRYPTO_ALG_TYPE_RNG,
|
|
.alg.rng = {
|
|
.base = {
|
|
.cra_name = "stdrng",
|
|
.cra_driver_name = "sun4i_ss_rng",
|
|
.cra_priority = 300,
|
|
.cra_ctxsize = 0,
|
|
.cra_module = THIS_MODULE,
|
|
},
|
|
.generate = sun4i_ss_prng_generate,
|
|
.seed = sun4i_ss_prng_seed,
|
|
.seedsize = SS_SEED_LEN / BITS_PER_BYTE,
|
|
}
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static int sun4i_ss_probe(struct platform_device *pdev)
|
|
{
|
|
struct resource *res;
|
|
u32 v;
|
|
int err, i;
|
|
unsigned long cr;
|
|
const unsigned long cr_ahb = 24 * 1000 * 1000;
|
|
const unsigned long cr_mod = 150 * 1000 * 1000;
|
|
struct sun4i_ss_ctx *ss;
|
|
|
|
if (!pdev->dev.of_node)
|
|
return -ENODEV;
|
|
|
|
ss = devm_kzalloc(&pdev->dev, sizeof(*ss), GFP_KERNEL);
|
|
if (!ss)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
ss->base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(ss->base)) {
|
|
dev_err(&pdev->dev, "Cannot request MMIO\n");
|
|
return PTR_ERR(ss->base);
|
|
}
|
|
|
|
ss->ssclk = devm_clk_get(&pdev->dev, "mod");
|
|
if (IS_ERR(ss->ssclk)) {
|
|
err = PTR_ERR(ss->ssclk);
|
|
dev_err(&pdev->dev, "Cannot get SS clock err=%d\n", err);
|
|
return err;
|
|
}
|
|
dev_dbg(&pdev->dev, "clock ss acquired\n");
|
|
|
|
ss->busclk = devm_clk_get(&pdev->dev, "ahb");
|
|
if (IS_ERR(ss->busclk)) {
|
|
err = PTR_ERR(ss->busclk);
|
|
dev_err(&pdev->dev, "Cannot get AHB SS clock err=%d\n", err);
|
|
return err;
|
|
}
|
|
dev_dbg(&pdev->dev, "clock ahb_ss acquired\n");
|
|
|
|
ss->reset = devm_reset_control_get_optional(&pdev->dev, "ahb");
|
|
if (IS_ERR(ss->reset)) {
|
|
if (PTR_ERR(ss->reset) == -EPROBE_DEFER)
|
|
return PTR_ERR(ss->reset);
|
|
dev_info(&pdev->dev, "no reset control found\n");
|
|
ss->reset = NULL;
|
|
}
|
|
|
|
/* Enable both clocks */
|
|
err = clk_prepare_enable(ss->busclk);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Cannot prepare_enable busclk\n");
|
|
return err;
|
|
}
|
|
err = clk_prepare_enable(ss->ssclk);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Cannot prepare_enable ssclk\n");
|
|
goto error_ssclk;
|
|
}
|
|
|
|
/*
|
|
* Check that clock have the correct rates given in the datasheet
|
|
* Try to set the clock to the maximum allowed
|
|
*/
|
|
err = clk_set_rate(ss->ssclk, cr_mod);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Cannot set clock rate to ssclk\n");
|
|
goto error_clk;
|
|
}
|
|
|
|
/* Deassert reset if we have a reset control */
|
|
if (ss->reset) {
|
|
err = reset_control_deassert(ss->reset);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Cannot deassert reset control\n");
|
|
goto error_clk;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The only impact on clocks below requirement are bad performance,
|
|
* so do not print "errors"
|
|
* warn on Overclocked clocks
|
|
*/
|
|
cr = clk_get_rate(ss->busclk);
|
|
if (cr >= cr_ahb)
|
|
dev_dbg(&pdev->dev, "Clock bus %lu (%lu MHz) (must be >= %lu)\n",
|
|
cr, cr / 1000000, cr_ahb);
|
|
else
|
|
dev_warn(&pdev->dev, "Clock bus %lu (%lu MHz) (must be >= %lu)\n",
|
|
cr, cr / 1000000, cr_ahb);
|
|
|
|
cr = clk_get_rate(ss->ssclk);
|
|
if (cr <= cr_mod)
|
|
if (cr < cr_mod)
|
|
dev_warn(&pdev->dev, "Clock ss %lu (%lu MHz) (must be <= %lu)\n",
|
|
cr, cr / 1000000, cr_mod);
|
|
else
|
|
dev_dbg(&pdev->dev, "Clock ss %lu (%lu MHz) (must be <= %lu)\n",
|
|
cr, cr / 1000000, cr_mod);
|
|
else
|
|
dev_warn(&pdev->dev, "Clock ss is at %lu (%lu MHz) (must be <= %lu)\n",
|
|
cr, cr / 1000000, cr_mod);
|
|
|
|
/*
|
|
* Datasheet named it "Die Bonding ID"
|
|
* I expect to be a sort of Security System Revision number.
|
|
* Since the A80 seems to have an other version of SS
|
|
* this info could be useful
|
|
*/
|
|
writel(SS_ENABLED, ss->base + SS_CTL);
|
|
v = readl(ss->base + SS_CTL);
|
|
v >>= 16;
|
|
v &= 0x07;
|
|
dev_info(&pdev->dev, "Die ID %d\n", v);
|
|
writel(0, ss->base + SS_CTL);
|
|
|
|
ss->dev = &pdev->dev;
|
|
|
|
spin_lock_init(&ss->slock);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ss_algs); i++) {
|
|
ss_algs[i].ss = ss;
|
|
switch (ss_algs[i].type) {
|
|
case CRYPTO_ALG_TYPE_SKCIPHER:
|
|
err = crypto_register_skcipher(&ss_algs[i].alg.crypto);
|
|
if (err) {
|
|
dev_err(ss->dev, "Fail to register %s\n",
|
|
ss_algs[i].alg.crypto.base.cra_name);
|
|
goto error_alg;
|
|
}
|
|
break;
|
|
case CRYPTO_ALG_TYPE_AHASH:
|
|
err = crypto_register_ahash(&ss_algs[i].alg.hash);
|
|
if (err) {
|
|
dev_err(ss->dev, "Fail to register %s\n",
|
|
ss_algs[i].alg.hash.halg.base.cra_name);
|
|
goto error_alg;
|
|
}
|
|
break;
|
|
case CRYPTO_ALG_TYPE_RNG:
|
|
err = crypto_register_rng(&ss_algs[i].alg.rng);
|
|
if (err) {
|
|
dev_err(ss->dev, "Fail to register %s\n",
|
|
ss_algs[i].alg.rng.base.cra_name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
platform_set_drvdata(pdev, ss);
|
|
return 0;
|
|
error_alg:
|
|
i--;
|
|
for (; i >= 0; i--) {
|
|
switch (ss_algs[i].type) {
|
|
case CRYPTO_ALG_TYPE_SKCIPHER:
|
|
crypto_unregister_skcipher(&ss_algs[i].alg.crypto);
|
|
break;
|
|
case CRYPTO_ALG_TYPE_AHASH:
|
|
crypto_unregister_ahash(&ss_algs[i].alg.hash);
|
|
break;
|
|
case CRYPTO_ALG_TYPE_RNG:
|
|
crypto_unregister_rng(&ss_algs[i].alg.rng);
|
|
break;
|
|
}
|
|
}
|
|
if (ss->reset)
|
|
reset_control_assert(ss->reset);
|
|
error_clk:
|
|
clk_disable_unprepare(ss->ssclk);
|
|
error_ssclk:
|
|
clk_disable_unprepare(ss->busclk);
|
|
return err;
|
|
}
|
|
|
|
static int sun4i_ss_remove(struct platform_device *pdev)
|
|
{
|
|
int i;
|
|
struct sun4i_ss_ctx *ss = platform_get_drvdata(pdev);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ss_algs); i++) {
|
|
switch (ss_algs[i].type) {
|
|
case CRYPTO_ALG_TYPE_SKCIPHER:
|
|
crypto_unregister_skcipher(&ss_algs[i].alg.crypto);
|
|
break;
|
|
case CRYPTO_ALG_TYPE_AHASH:
|
|
crypto_unregister_ahash(&ss_algs[i].alg.hash);
|
|
break;
|
|
case CRYPTO_ALG_TYPE_RNG:
|
|
crypto_unregister_rng(&ss_algs[i].alg.rng);
|
|
break;
|
|
}
|
|
}
|
|
|
|
writel(0, ss->base + SS_CTL);
|
|
if (ss->reset)
|
|
reset_control_assert(ss->reset);
|
|
clk_disable_unprepare(ss->busclk);
|
|
clk_disable_unprepare(ss->ssclk);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id a20ss_crypto_of_match_table[] = {
|
|
{ .compatible = "allwinner,sun4i-a10-crypto" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, a20ss_crypto_of_match_table);
|
|
|
|
static struct platform_driver sun4i_ss_driver = {
|
|
.probe = sun4i_ss_probe,
|
|
.remove = sun4i_ss_remove,
|
|
.driver = {
|
|
.name = "sun4i-ss",
|
|
.of_match_table = a20ss_crypto_of_match_table,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(sun4i_ss_driver);
|
|
|
|
MODULE_ALIAS("platform:sun4i-ss");
|
|
MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
|