18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * amlgoic-core.c - hardware cryptographic offloader for Amlogic GXL SoC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 Corentin Labbe <clabbe@baylibre.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Core file which registers crypto algorithms supported by the hardware. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/crypto.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "amlogic-gxl.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic irqreturn_t meson_irq_handler(int irq, void *data) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct meson_dev *mc = (struct meson_dev *)data; 268c2ecf20Sopenharmony_ci int flow; 278c2ecf20Sopenharmony_ci u32 p; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci for (flow = 0; flow < MAXFLOW; flow++) { 308c2ecf20Sopenharmony_ci if (mc->irqs[flow] == irq) { 318c2ecf20Sopenharmony_ci p = readl(mc->base + ((0x04 + flow) << 2)); 328c2ecf20Sopenharmony_ci if (p) { 338c2ecf20Sopenharmony_ci writel_relaxed(0xF, mc->base + ((0x4 + flow) << 2)); 348c2ecf20Sopenharmony_ci mc->chanlist[flow].status = 1; 358c2ecf20Sopenharmony_ci complete(&mc->chanlist[flow].complete); 368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci dev_err(mc->dev, "%s %d Got irq for flow %d but ctrl is empty\n", __func__, irq, flow); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci dev_err(mc->dev, "%s %d from unknown irq\n", __func__, irq); 438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic struct meson_alg_template mc_algs[] = { 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci .type = CRYPTO_ALG_TYPE_SKCIPHER, 498c2ecf20Sopenharmony_ci .blockmode = MESON_OPMODE_CBC, 508c2ecf20Sopenharmony_ci .alg.skcipher = { 518c2ecf20Sopenharmony_ci .base = { 528c2ecf20Sopenharmony_ci .cra_name = "cbc(aes)", 538c2ecf20Sopenharmony_ci .cra_driver_name = "cbc-aes-gxl", 548c2ecf20Sopenharmony_ci .cra_priority = 400, 558c2ecf20Sopenharmony_ci .cra_blocksize = AES_BLOCK_SIZE, 568c2ecf20Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | 578c2ecf20Sopenharmony_ci CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | 588c2ecf20Sopenharmony_ci CRYPTO_ALG_NEED_FALLBACK, 598c2ecf20Sopenharmony_ci .cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), 608c2ecf20Sopenharmony_ci .cra_module = THIS_MODULE, 618c2ecf20Sopenharmony_ci .cra_alignmask = 0xf, 628c2ecf20Sopenharmony_ci .cra_init = meson_cipher_init, 638c2ecf20Sopenharmony_ci .cra_exit = meson_cipher_exit, 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci .min_keysize = AES_MIN_KEY_SIZE, 668c2ecf20Sopenharmony_ci .max_keysize = AES_MAX_KEY_SIZE, 678c2ecf20Sopenharmony_ci .ivsize = AES_BLOCK_SIZE, 688c2ecf20Sopenharmony_ci .setkey = meson_aes_setkey, 698c2ecf20Sopenharmony_ci .encrypt = meson_skencrypt, 708c2ecf20Sopenharmony_ci .decrypt = meson_skdecrypt, 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci}, 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci .type = CRYPTO_ALG_TYPE_SKCIPHER, 758c2ecf20Sopenharmony_ci .blockmode = MESON_OPMODE_ECB, 768c2ecf20Sopenharmony_ci .alg.skcipher = { 778c2ecf20Sopenharmony_ci .base = { 788c2ecf20Sopenharmony_ci .cra_name = "ecb(aes)", 798c2ecf20Sopenharmony_ci .cra_driver_name = "ecb-aes-gxl", 808c2ecf20Sopenharmony_ci .cra_priority = 400, 818c2ecf20Sopenharmony_ci .cra_blocksize = AES_BLOCK_SIZE, 828c2ecf20Sopenharmony_ci .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | 838c2ecf20Sopenharmony_ci CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY | 848c2ecf20Sopenharmony_ci CRYPTO_ALG_NEED_FALLBACK, 858c2ecf20Sopenharmony_ci .cra_ctxsize = sizeof(struct meson_cipher_tfm_ctx), 868c2ecf20Sopenharmony_ci .cra_module = THIS_MODULE, 878c2ecf20Sopenharmony_ci .cra_alignmask = 0xf, 888c2ecf20Sopenharmony_ci .cra_init = meson_cipher_init, 898c2ecf20Sopenharmony_ci .cra_exit = meson_cipher_exit, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci .min_keysize = AES_MIN_KEY_SIZE, 928c2ecf20Sopenharmony_ci .max_keysize = AES_MAX_KEY_SIZE, 938c2ecf20Sopenharmony_ci .setkey = meson_aes_setkey, 948c2ecf20Sopenharmony_ci .encrypt = meson_skencrypt, 958c2ecf20Sopenharmony_ci .decrypt = meson_skdecrypt, 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci}, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 1018c2ecf20Sopenharmony_cistatic int meson_debugfs_show(struct seq_file *seq, void *v) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct meson_dev *mc = seq->private; 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) 1078c2ecf20Sopenharmony_ci seq_printf(seq, "Channel %d: nreq %lu\n", i, mc->chanlist[i].stat_req); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 1108c2ecf20Sopenharmony_ci switch (mc_algs[i].type) { 1118c2ecf20Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 1128c2ecf20Sopenharmony_ci seq_printf(seq, "%s %s %lu %lu\n", 1138c2ecf20Sopenharmony_ci mc_algs[i].alg.skcipher.base.cra_driver_name, 1148c2ecf20Sopenharmony_ci mc_algs[i].alg.skcipher.base.cra_name, 1158c2ecf20Sopenharmony_ci mc_algs[i].stat_req, mc_algs[i].stat_fb); 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(meson_debugfs); 1228c2ecf20Sopenharmony_ci#endif 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void meson_free_chanlist(struct meson_dev *mc, int i) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci while (i >= 0) { 1278c2ecf20Sopenharmony_ci crypto_engine_exit(mc->chanlist[i].engine); 1288c2ecf20Sopenharmony_ci if (mc->chanlist[i].tl) 1298c2ecf20Sopenharmony_ci dma_free_coherent(mc->dev, sizeof(struct meson_desc) * MAXDESC, 1308c2ecf20Sopenharmony_ci mc->chanlist[i].tl, 1318c2ecf20Sopenharmony_ci mc->chanlist[i].t_phy); 1328c2ecf20Sopenharmony_ci i--; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * Allocate the channel list structure 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic int meson_allocate_chanlist(struct meson_dev *mc) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int i, err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mc->chanlist = devm_kcalloc(mc->dev, MAXFLOW, 1448c2ecf20Sopenharmony_ci sizeof(struct meson_flow), GFP_KERNEL); 1458c2ecf20Sopenharmony_ci if (!mc->chanlist) 1468c2ecf20Sopenharmony_ci return -ENOMEM; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) { 1498c2ecf20Sopenharmony_ci init_completion(&mc->chanlist[i].complete); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci mc->chanlist[i].engine = crypto_engine_alloc_init(mc->dev, true); 1528c2ecf20Sopenharmony_ci if (!mc->chanlist[i].engine) { 1538c2ecf20Sopenharmony_ci dev_err(mc->dev, "Cannot allocate engine\n"); 1548c2ecf20Sopenharmony_ci i--; 1558c2ecf20Sopenharmony_ci err = -ENOMEM; 1568c2ecf20Sopenharmony_ci goto error_engine; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci err = crypto_engine_start(mc->chanlist[i].engine); 1598c2ecf20Sopenharmony_ci if (err) { 1608c2ecf20Sopenharmony_ci dev_err(mc->dev, "Cannot start engine\n"); 1618c2ecf20Sopenharmony_ci goto error_engine; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci mc->chanlist[i].tl = dma_alloc_coherent(mc->dev, 1648c2ecf20Sopenharmony_ci sizeof(struct meson_desc) * MAXDESC, 1658c2ecf20Sopenharmony_ci &mc->chanlist[i].t_phy, 1668c2ecf20Sopenharmony_ci GFP_KERNEL); 1678c2ecf20Sopenharmony_ci if (!mc->chanlist[i].tl) { 1688c2ecf20Sopenharmony_ci err = -ENOMEM; 1698c2ecf20Sopenharmony_ci goto error_engine; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_cierror_engine: 1748c2ecf20Sopenharmony_ci meson_free_chanlist(mc, i); 1758c2ecf20Sopenharmony_ci return err; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int meson_register_algs(struct meson_dev *mc) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci int err, i; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 1838c2ecf20Sopenharmony_ci mc_algs[i].mc = mc; 1848c2ecf20Sopenharmony_ci switch (mc_algs[i].type) { 1858c2ecf20Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 1868c2ecf20Sopenharmony_ci err = crypto_register_skcipher(&mc_algs[i].alg.skcipher); 1878c2ecf20Sopenharmony_ci if (err) { 1888c2ecf20Sopenharmony_ci dev_err(mc->dev, "Fail to register %s\n", 1898c2ecf20Sopenharmony_ci mc_algs[i].alg.skcipher.base.cra_name); 1908c2ecf20Sopenharmony_ci mc_algs[i].mc = NULL; 1918c2ecf20Sopenharmony_ci return err; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void meson_unregister_algs(struct meson_dev *mc) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mc_algs); i++) { 2058c2ecf20Sopenharmony_ci if (!mc_algs[i].mc) 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci switch (mc_algs[i].type) { 2088c2ecf20Sopenharmony_ci case CRYPTO_ALG_TYPE_SKCIPHER: 2098c2ecf20Sopenharmony_ci crypto_unregister_skcipher(&mc_algs[i].alg.skcipher); 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int meson_crypto_probe(struct platform_device *pdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct meson_dev *mc; 2188c2ecf20Sopenharmony_ci int err, i; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!pdev->dev.of_node) 2218c2ecf20Sopenharmony_ci return -ENODEV; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!mc) 2258c2ecf20Sopenharmony_ci return -ENOMEM; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci mc->dev = &pdev->dev; 2288c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mc); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mc->base = devm_platform_ioremap_resource(pdev, 0); 2318c2ecf20Sopenharmony_ci if (IS_ERR(mc->base)) { 2328c2ecf20Sopenharmony_ci err = PTR_ERR(mc->base); 2338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot request MMIO err=%d\n", err); 2348c2ecf20Sopenharmony_ci return err; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci mc->busclk = devm_clk_get(&pdev->dev, "blkmv"); 2378c2ecf20Sopenharmony_ci if (IS_ERR(mc->busclk)) { 2388c2ecf20Sopenharmony_ci err = PTR_ERR(mc->busclk); 2398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot get core clock err=%d\n", err); 2408c2ecf20Sopenharmony_ci return err; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (i = 0; i < MAXFLOW; i++) { 2448c2ecf20Sopenharmony_ci mc->irqs[i] = platform_get_irq(pdev, i); 2458c2ecf20Sopenharmony_ci if (mc->irqs[i] < 0) 2468c2ecf20Sopenharmony_ci return mc->irqs[i]; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, mc->irqs[i], meson_irq_handler, 0, 2498c2ecf20Sopenharmony_ci "gxl-crypto", mc); 2508c2ecf20Sopenharmony_ci if (err < 0) { 2518c2ecf20Sopenharmony_ci dev_err(mc->dev, "Cannot request IRQ for flow %d\n", i); 2528c2ecf20Sopenharmony_ci return err; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci err = clk_prepare_enable(mc->busclk); 2578c2ecf20Sopenharmony_ci if (err != 0) { 2588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot prepare_enable busclk\n"); 2598c2ecf20Sopenharmony_ci return err; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = meson_allocate_chanlist(mc); 2638c2ecf20Sopenharmony_ci if (err) 2648c2ecf20Sopenharmony_ci goto error_flow; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci err = meson_register_algs(mc); 2678c2ecf20Sopenharmony_ci if (err) 2688c2ecf20Sopenharmony_ci goto error_alg; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 2718c2ecf20Sopenharmony_ci mc->dbgfs_dir = debugfs_create_dir("gxl-crypto", NULL); 2728c2ecf20Sopenharmony_ci debugfs_create_file("stats", 0444, mc->dbgfs_dir, mc, &meson_debugfs_fops); 2738c2ecf20Sopenharmony_ci#endif 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_cierror_alg: 2778c2ecf20Sopenharmony_ci meson_unregister_algs(mc); 2788c2ecf20Sopenharmony_cierror_flow: 2798c2ecf20Sopenharmony_ci meson_free_chanlist(mc, MAXFLOW - 1); 2808c2ecf20Sopenharmony_ci clk_disable_unprepare(mc->busclk); 2818c2ecf20Sopenharmony_ci return err; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int meson_crypto_remove(struct platform_device *pdev) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct meson_dev *mc = platform_get_drvdata(pdev); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG 2898c2ecf20Sopenharmony_ci debugfs_remove_recursive(mc->dbgfs_dir); 2908c2ecf20Sopenharmony_ci#endif 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci meson_unregister_algs(mc); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci meson_free_chanlist(mc, MAXFLOW - 1); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci clk_disable_unprepare(mc->busclk); 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct of_device_id meson_crypto_of_match_table[] = { 3018c2ecf20Sopenharmony_ci { .compatible = "amlogic,gxl-crypto", }, 3028c2ecf20Sopenharmony_ci {} 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_crypto_of_match_table); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic struct platform_driver meson_crypto_driver = { 3078c2ecf20Sopenharmony_ci .probe = meson_crypto_probe, 3088c2ecf20Sopenharmony_ci .remove = meson_crypto_remove, 3098c2ecf20Sopenharmony_ci .driver = { 3108c2ecf20Sopenharmony_ci .name = "gxl-crypto", 3118c2ecf20Sopenharmony_ci .of_match_table = meson_crypto_of_match_table, 3128c2ecf20Sopenharmony_ci }, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cimodule_platform_driver(meson_crypto_driver); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic GXL cryptographic offloader"); 3188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corentin Labbe <clabbe@baylibre.com>"); 320