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