162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * amlgoic-core.c - hardware cryptographic offloader for Amlogic GXL SoC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018-2019 Corentin Labbe <clabbe@baylibre.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Core file which registers crypto algorithms supported by the hardware. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <crypto/engine.h> 1162306a36Sopenharmony_ci#include <crypto/internal/skcipher.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "amlogic-gxl.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic irqreturn_t meson_irq_handler(int irq, void *data) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct meson_dev *mc = (struct meson_dev *)data; 2862306a36Sopenharmony_ci int flow; 2962306a36Sopenharmony_ci u32 p; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci for (flow = 0; flow < MAXFLOW; flow++) { 3262306a36Sopenharmony_ci if (mc->irqs[flow] == irq) { 3362306a36Sopenharmony_ci p = readl(mc->base + ((0x04 + flow) << 2)); 3462306a36Sopenharmony_ci if (p) { 3562306a36Sopenharmony_ci writel_relaxed(0xF, mc->base + ((0x4 + flow) << 2)); 3662306a36Sopenharmony_ci mc->chanlist[flow].status = 1; 3762306a36Sopenharmony_ci complete(&mc->chanlist[flow].complete); 3862306a36Sopenharmony_ci return IRQ_HANDLED; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci dev_err(mc->dev, "%s %d Got irq for flow %d but ctrl is empty\n", __func__, irq, flow); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci dev_err(mc->dev, "%s %d from unknown irq\n", __func__, irq); 4562306a36Sopenharmony_ci return IRQ_HANDLED; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct meson_alg_template mc_algs[] = { 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci .type = CRYPTO_ALG_TYPE_SKCIPHER, 5162306a36Sopenharmony_ci .blockmode = MESON_OPMODE_CBC, 5262306a36Sopenharmony_ci .alg.skcipher.base = { 5362306a36Sopenharmony_ci .base = { 5462306a36Sopenharmony_ci .cra_name = "cbc(aes)", 5562306a36Sopenharmony_ci .cra_driver_name = "cbc-aes-gxl", 5662306a36Sopenharmony_ci .cra_priority = 400, 5762306a36Sopenharmony_ci .cra_blocksize = AES_BLOCK_SIZE, 5862306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | 5962306a36Sopenharmony_ci CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | 6062306a36Sopenharmony_ci CRYPTO_ALG_NEED_FALLBACK, 6162306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), 6262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 6362306a36Sopenharmony_ci .cra_alignmask = 0xf, 6462306a36Sopenharmony_ci .cra_init = meson_cipher_init, 6562306a36Sopenharmony_ci .cra_exit = meson_cipher_exit, 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci .min_keysize = AES_MIN_KEY_SIZE, 6862306a36Sopenharmony_ci .max_keysize = AES_MAX_KEY_SIZE, 6962306a36Sopenharmony_ci .ivsize = AES_BLOCK_SIZE, 7062306a36Sopenharmony_ci .setkey = meson_aes_setkey, 7162306a36Sopenharmony_ci .encrypt = meson_skencrypt, 7262306a36Sopenharmony_ci .decrypt = meson_skdecrypt, 7362306a36Sopenharmony_ci }, 7462306a36Sopenharmony_ci .alg.skcipher.op = { 7562306a36Sopenharmony_ci .do_one_request = meson_handle_cipher_request, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci}, 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci .type = CRYPTO_ALG_TYPE_SKCIPHER, 8062306a36Sopenharmony_ci .blockmode = MESON_OPMODE_ECB, 8162306a36Sopenharmony_ci .alg.skcipher.base = { 8262306a36Sopenharmony_ci .base = { 8362306a36Sopenharmony_ci .cra_name = "ecb(aes)", 8462306a36Sopenharmony_ci .cra_driver_name = "ecb-aes-gxl", 8562306a36Sopenharmony_ci .cra_priority = 400, 8662306a36Sopenharmony_ci .cra_blocksize = AES_BLOCK_SIZE, 8762306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | 8862306a36Sopenharmony_ci CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | 8962306a36Sopenharmony_ci CRYPTO_ALG_NEED_FALLBACK, 9062306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), 9162306a36Sopenharmony_ci .cra_module = THIS_MODULE, 9262306a36Sopenharmony_ci .cra_alignmask = 0xf, 9362306a36Sopenharmony_ci .cra_init = meson_cipher_init, 9462306a36Sopenharmony_ci .cra_exit = meson_cipher_exit, 9562306a36Sopenharmony_ci }, 9662306a36Sopenharmony_ci .min_keysize = AES_MIN_KEY_SIZE, 9762306a36Sopenharmony_ci .max_keysize = AES_MAX_KEY_SIZE, 9862306a36Sopenharmony_ci .setkey = meson_aes_setkey, 9962306a36Sopenharmony_ci .encrypt = meson_skencrypt, 10062306a36Sopenharmony_ci .decrypt = meson_skdecrypt, 10162306a36Sopenharmony_ci }, 10262306a36Sopenharmony_ci .alg.skcipher.op = { 10362306a36Sopenharmony_ci .do_one_request = meson_handle_cipher_request, 10462306a36Sopenharmony_ci }, 10562306a36Sopenharmony_ci}, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int meson_debugfs_show(struct seq_file *seq, void *v) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct meson_dev *mc __maybe_unused = seq->private; 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) 11462306a36Sopenharmony_ci seq_printf(seq, "Channel %d: nreq %lu\n", i, 11562306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 11662306a36Sopenharmony_ci mc->chanlist[i].stat_req); 11762306a36Sopenharmony_ci#else 11862306a36Sopenharmony_ci 0ul); 11962306a36Sopenharmony_ci#endif 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 12262306a36Sopenharmony_ci switch (mc_algs[i].type) { 12362306a36Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 12462306a36Sopenharmony_ci seq_printf(seq, "%s %s %lu %lu\n", 12562306a36Sopenharmony_ci mc_algs[i].alg.skcipher.base.base.cra_driver_name, 12662306a36Sopenharmony_ci mc_algs[i].alg.skcipher.base.base.cra_name, 12762306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 12862306a36Sopenharmony_ci mc_algs[i].stat_req, mc_algs[i].stat_fb); 12962306a36Sopenharmony_ci#else 13062306a36Sopenharmony_ci 0ul, 0ul); 13162306a36Sopenharmony_ci#endif 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(meson_debugfs); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void meson_free_chanlist(struct meson_dev *mc, int i) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci while (i >= 0) { 14262306a36Sopenharmony_ci crypto_engine_exit(mc->chanlist[i].engine); 14362306a36Sopenharmony_ci if (mc->chanlist[i].tl) 14462306a36Sopenharmony_ci dma_free_coherent(mc->dev, sizeof(struct meson_desc) * MAXDESC, 14562306a36Sopenharmony_ci mc->chanlist[i].tl, 14662306a36Sopenharmony_ci mc->chanlist[i].t_phy); 14762306a36Sopenharmony_ci i--; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Allocate the channel list structure 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int meson_allocate_chanlist(struct meson_dev *mc) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int i, err; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mc->chanlist = devm_kcalloc(mc->dev, MAXFLOW, 15962306a36Sopenharmony_ci sizeof(struct meson_flow), GFP_KERNEL); 16062306a36Sopenharmony_ci if (!mc->chanlist) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) { 16462306a36Sopenharmony_ci init_completion(&mc->chanlist[i].complete); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci mc->chanlist[i].engine = crypto_engine_alloc_init(mc->dev, true); 16762306a36Sopenharmony_ci if (!mc->chanlist[i].engine) { 16862306a36Sopenharmony_ci dev_err(mc->dev, "Cannot allocate engine\n"); 16962306a36Sopenharmony_ci i--; 17062306a36Sopenharmony_ci err = -ENOMEM; 17162306a36Sopenharmony_ci goto error_engine; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci err = crypto_engine_start(mc->chanlist[i].engine); 17462306a36Sopenharmony_ci if (err) { 17562306a36Sopenharmony_ci dev_err(mc->dev, "Cannot start engine\n"); 17662306a36Sopenharmony_ci goto error_engine; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci mc->chanlist[i].tl = dma_alloc_coherent(mc->dev, 17962306a36Sopenharmony_ci sizeof(struct meson_desc) * MAXDESC, 18062306a36Sopenharmony_ci &mc->chanlist[i].t_phy, 18162306a36Sopenharmony_ci GFP_KERNEL); 18262306a36Sopenharmony_ci if (!mc->chanlist[i].tl) { 18362306a36Sopenharmony_ci err = -ENOMEM; 18462306a36Sopenharmony_ci goto error_engine; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_cierror_engine: 18962306a36Sopenharmony_ci meson_free_chanlist(mc, i); 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int meson_register_algs(struct meson_dev *mc) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int err, i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 19862306a36Sopenharmony_ci mc_algs[i].mc = mc; 19962306a36Sopenharmony_ci switch (mc_algs[i].type) { 20062306a36Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 20162306a36Sopenharmony_ci err = crypto_engine_register_skcipher(&mc_algs[i].alg.skcipher); 20262306a36Sopenharmony_ci if (err) { 20362306a36Sopenharmony_ci dev_err(mc->dev, "Fail to register %s\n", 20462306a36Sopenharmony_ci mc_algs[i].alg.skcipher.base.base.cra_name); 20562306a36Sopenharmony_ci mc_algs[i].mc = NULL; 20662306a36Sopenharmony_ci return err; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void meson_unregister_algs(struct meson_dev *mc) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci int i; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 22062306a36Sopenharmony_ci if (!mc_algs[i].mc) 22162306a36Sopenharmony_ci continue; 22262306a36Sopenharmony_ci switch (mc_algs[i].type) { 22362306a36Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 22462306a36Sopenharmony_ci crypto_engine_unregister_skcipher(&mc_algs[i].alg.skcipher); 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int meson_crypto_probe(struct platform_device *pdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct meson_dev *mc; 23362306a36Sopenharmony_ci int err, i; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 23662306a36Sopenharmony_ci if (!mc) 23762306a36Sopenharmony_ci return -ENOMEM; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci mc->dev = &pdev->dev; 24062306a36Sopenharmony_ci platform_set_drvdata(pdev, mc); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mc->base = devm_platform_ioremap_resource(pdev, 0); 24362306a36Sopenharmony_ci if (IS_ERR(mc->base)) { 24462306a36Sopenharmony_ci err = PTR_ERR(mc->base); 24562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot request MMIO err=%d\n", err); 24662306a36Sopenharmony_ci return err; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci mc->busclk = devm_clk_get(&pdev->dev, "blkmv"); 24962306a36Sopenharmony_ci if (IS_ERR(mc->busclk)) { 25062306a36Sopenharmony_ci err = PTR_ERR(mc->busclk); 25162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot get core clock err=%d\n", err); 25262306a36Sopenharmony_ci return err; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) { 25662306a36Sopenharmony_ci mc->irqs[i] = platform_get_irq(pdev, i); 25762306a36Sopenharmony_ci if (mc->irqs[i] < 0) 25862306a36Sopenharmony_ci return mc->irqs[i]; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, mc->irqs[i], meson_irq_handler, 0, 26162306a36Sopenharmony_ci "gxl-crypto", mc); 26262306a36Sopenharmony_ci if (err < 0) { 26362306a36Sopenharmony_ci dev_err(mc->dev, "Cannot request IRQ for flow %d\n", i); 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci err = clk_prepare_enable(mc->busclk); 26962306a36Sopenharmony_ci if (err != 0) { 27062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot prepare_enable busclk\n"); 27162306a36Sopenharmony_ci return err; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci err = meson_allocate_chanlist(mc); 27562306a36Sopenharmony_ci if (err) 27662306a36Sopenharmony_ci goto error_flow; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci err = meson_register_algs(mc); 27962306a36Sopenharmony_ci if (err) 28062306a36Sopenharmony_ci goto error_alg; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG)) { 28362306a36Sopenharmony_ci struct dentry *dbgfs_dir; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dbgfs_dir = debugfs_create_dir("gxl-crypto", NULL); 28662306a36Sopenharmony_ci debugfs_create_file("stats", 0444, dbgfs_dir, mc, &meson_debugfs_fops); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 28962306a36Sopenharmony_ci mc->dbgfs_dir = dbgfs_dir; 29062306a36Sopenharmony_ci#endif 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_cierror_alg: 29562306a36Sopenharmony_ci meson_unregister_algs(mc); 29662306a36Sopenharmony_cierror_flow: 29762306a36Sopenharmony_ci meson_free_chanlist(mc, MAXFLOW - 1); 29862306a36Sopenharmony_ci clk_disable_unprepare(mc->busclk); 29962306a36Sopenharmony_ci return err; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int meson_crypto_remove(struct platform_device *pdev) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct meson_dev *mc = platform_get_drvdata(pdev); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 30762306a36Sopenharmony_ci debugfs_remove_recursive(mc->dbgfs_dir); 30862306a36Sopenharmony_ci#endif 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci meson_unregister_algs(mc); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci meson_free_chanlist(mc, MAXFLOW - 1); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci clk_disable_unprepare(mc->busclk); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct of_device_id meson_crypto_of_match_table[] = { 31962306a36Sopenharmony_ci { .compatible = "amlogic,gxl-crypto", }, 32062306a36Sopenharmony_ci {} 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_crypto_of_match_table); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic struct platform_driver meson_crypto_driver = { 32562306a36Sopenharmony_ci .probe = meson_crypto_probe, 32662306a36Sopenharmony_ci .remove = meson_crypto_remove, 32762306a36Sopenharmony_ci .driver = { 32862306a36Sopenharmony_ci .name = "gxl-crypto", 32962306a36Sopenharmony_ci .of_match_table = meson_crypto_of_match_table, 33062306a36Sopenharmony_ci }, 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cimodule_platform_driver(meson_crypto_driver); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic GXL cryptographic offloader"); 33662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 33762306a36Sopenharmony_ciMODULE_AUTHOR("Corentin Labbe <clabbe@baylibre.com>"); 338