162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cryptographic API.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Support for StarFive hardware cryptographic engine.
662306a36Sopenharmony_ci * Copyright (c) 2022 StarFive Technology
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <crypto/engine.h>
1162306a36Sopenharmony_ci#include "jh7110-cryp.h"
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/iopoll.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2262306a36Sopenharmony_ci#include <linux/reset.h>
2362306a36Sopenharmony_ci#include <linux/spinlock.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DRIVER_NAME             "jh7110-crypto"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct starfive_dev_list {
2862306a36Sopenharmony_ci	struct list_head        dev_list;
2962306a36Sopenharmony_ci	spinlock_t              lock; /* protect dev_list */
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct starfive_dev_list dev_list = {
3362306a36Sopenharmony_ci	.dev_list = LIST_HEAD_INIT(dev_list.dev_list),
3462306a36Sopenharmony_ci	.lock     = __SPIN_LOCK_UNLOCKED(dev_list.lock),
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct starfive_cryp_dev *cryp = NULL, *tmp;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	spin_lock_bh(&dev_list.lock);
4262306a36Sopenharmony_ci	if (!ctx->cryp) {
4362306a36Sopenharmony_ci		list_for_each_entry(tmp, &dev_list.dev_list, list) {
4462306a36Sopenharmony_ci			cryp = tmp;
4562306a36Sopenharmony_ci			break;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci		ctx->cryp = cryp;
4862306a36Sopenharmony_ci	} else {
4962306a36Sopenharmony_ci		cryp = ctx->cryp;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	spin_unlock_bh(&dev_list.lock);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return cryp;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic u16 side_chan;
5862306a36Sopenharmony_cimodule_param(side_chan, ushort, 0);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(side_chan, "Enable side channel mitigation for AES module.\n"
6062306a36Sopenharmony_ci			    "Enabling this feature will reduce speed performance.\n"
6162306a36Sopenharmony_ci			    " 0 - Disabled\n"
6262306a36Sopenharmony_ci			    " other - Enabled");
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int starfive_dma_init(struct starfive_cryp_dev *cryp)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	dma_cap_mask_t mask;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	dma_cap_zero(mask);
6962306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	cryp->tx = dma_request_chan(cryp->dev, "tx");
7262306a36Sopenharmony_ci	if (IS_ERR(cryp->tx))
7362306a36Sopenharmony_ci		return dev_err_probe(cryp->dev, PTR_ERR(cryp->tx),
7462306a36Sopenharmony_ci				     "Error requesting tx dma channel.\n");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	cryp->rx = dma_request_chan(cryp->dev, "rx");
7762306a36Sopenharmony_ci	if (IS_ERR(cryp->rx)) {
7862306a36Sopenharmony_ci		dma_release_channel(cryp->tx);
7962306a36Sopenharmony_ci		return dev_err_probe(cryp->dev, PTR_ERR(cryp->rx),
8062306a36Sopenharmony_ci				     "Error requesting rx dma channel.\n");
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void starfive_dma_cleanup(struct starfive_cryp_dev *cryp)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	dma_release_channel(cryp->tx);
8962306a36Sopenharmony_ci	dma_release_channel(cryp->rx);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic irqreturn_t starfive_cryp_irq(int irq, void *priv)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 status;
9562306a36Sopenharmony_ci	u32 mask;
9662306a36Sopenharmony_ci	struct starfive_cryp_dev *cryp = (struct starfive_cryp_dev *)priv;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	mask = readl(cryp->base + STARFIVE_IE_MASK_OFFSET);
9962306a36Sopenharmony_ci	status = readl(cryp->base + STARFIVE_IE_FLAG_OFFSET);
10062306a36Sopenharmony_ci	if (status & STARFIVE_IE_FLAG_AES_DONE) {
10162306a36Sopenharmony_ci		mask |= STARFIVE_IE_MASK_AES_DONE;
10262306a36Sopenharmony_ci		writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET);
10362306a36Sopenharmony_ci		tasklet_schedule(&cryp->aes_done);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (status & STARFIVE_IE_FLAG_HASH_DONE) {
10762306a36Sopenharmony_ci		mask |= STARFIVE_IE_MASK_HASH_DONE;
10862306a36Sopenharmony_ci		writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET);
10962306a36Sopenharmony_ci		tasklet_schedule(&cryp->hash_done);
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (status & STARFIVE_IE_FLAG_PKA_DONE) {
11362306a36Sopenharmony_ci		mask |= STARFIVE_IE_MASK_PKA_DONE;
11462306a36Sopenharmony_ci		writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET);
11562306a36Sopenharmony_ci		complete(&cryp->pka_done);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return IRQ_HANDLED;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int starfive_cryp_probe(struct platform_device *pdev)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct starfive_cryp_dev *cryp;
12462306a36Sopenharmony_ci	struct resource *res;
12562306a36Sopenharmony_ci	int irq;
12662306a36Sopenharmony_ci	int ret;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL);
12962306a36Sopenharmony_ci	if (!cryp)
13062306a36Sopenharmony_ci		return -ENOMEM;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	platform_set_drvdata(pdev, cryp);
13362306a36Sopenharmony_ci	cryp->dev = &pdev->dev;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	cryp->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
13662306a36Sopenharmony_ci	if (IS_ERR(cryp->base))
13762306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(cryp->base),
13862306a36Sopenharmony_ci				     "Error remapping memory for platform device\n");
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	tasklet_init(&cryp->aes_done, starfive_aes_done_task, (unsigned long)cryp);
14162306a36Sopenharmony_ci	tasklet_init(&cryp->hash_done, starfive_hash_done_task, (unsigned long)cryp);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	cryp->phys_base = res->start;
14462306a36Sopenharmony_ci	cryp->dma_maxburst = 32;
14562306a36Sopenharmony_ci	cryp->side_chan = side_chan;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	cryp->hclk = devm_clk_get(&pdev->dev, "hclk");
14862306a36Sopenharmony_ci	if (IS_ERR(cryp->hclk))
14962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(cryp->hclk),
15062306a36Sopenharmony_ci				     "Error getting hardware reference clock\n");
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	cryp->ahb = devm_clk_get(&pdev->dev, "ahb");
15362306a36Sopenharmony_ci	if (IS_ERR(cryp->ahb))
15462306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(cryp->ahb),
15562306a36Sopenharmony_ci				     "Error getting ahb reference clock\n");
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	cryp->rst = devm_reset_control_get_shared(cryp->dev, NULL);
15862306a36Sopenharmony_ci	if (IS_ERR(cryp->rst))
15962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst),
16062306a36Sopenharmony_ci				     "Error getting hardware reset line\n");
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	init_completion(&cryp->pka_done);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
16562306a36Sopenharmony_ci	if (irq < 0)
16662306a36Sopenharmony_ci		return irq;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, starfive_cryp_irq, 0, pdev->name,
16962306a36Sopenharmony_ci			       (void *)cryp);
17062306a36Sopenharmony_ci	if (ret)
17162306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, ret,
17262306a36Sopenharmony_ci				     "Failed to register interrupt handler\n");
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	clk_prepare_enable(cryp->hclk);
17562306a36Sopenharmony_ci	clk_prepare_enable(cryp->ahb);
17662306a36Sopenharmony_ci	reset_control_deassert(cryp->rst);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	spin_lock(&dev_list.lock);
17962306a36Sopenharmony_ci	list_add(&cryp->list, &dev_list.dev_list);
18062306a36Sopenharmony_ci	spin_unlock(&dev_list.lock);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = starfive_dma_init(cryp);
18362306a36Sopenharmony_ci	if (ret)
18462306a36Sopenharmony_ci		goto err_dma_init;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Initialize crypto engine */
18762306a36Sopenharmony_ci	cryp->engine = crypto_engine_alloc_init(&pdev->dev, 1);
18862306a36Sopenharmony_ci	if (!cryp->engine) {
18962306a36Sopenharmony_ci		ret = -ENOMEM;
19062306a36Sopenharmony_ci		goto err_engine;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ret = crypto_engine_start(cryp->engine);
19462306a36Sopenharmony_ci	if (ret)
19562306a36Sopenharmony_ci		goto err_engine_start;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = starfive_aes_register_algs();
19862306a36Sopenharmony_ci	if (ret)
19962306a36Sopenharmony_ci		goto err_algs_aes;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ret = starfive_hash_register_algs();
20262306a36Sopenharmony_ci	if (ret)
20362306a36Sopenharmony_ci		goto err_algs_hash;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = starfive_rsa_register_algs();
20662306a36Sopenharmony_ci	if (ret)
20762306a36Sopenharmony_ci		goto err_algs_rsa;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cierr_algs_rsa:
21262306a36Sopenharmony_ci	starfive_hash_unregister_algs();
21362306a36Sopenharmony_cierr_algs_hash:
21462306a36Sopenharmony_ci	starfive_aes_unregister_algs();
21562306a36Sopenharmony_cierr_algs_aes:
21662306a36Sopenharmony_ci	crypto_engine_stop(cryp->engine);
21762306a36Sopenharmony_cierr_engine_start:
21862306a36Sopenharmony_ci	crypto_engine_exit(cryp->engine);
21962306a36Sopenharmony_cierr_engine:
22062306a36Sopenharmony_ci	starfive_dma_cleanup(cryp);
22162306a36Sopenharmony_cierr_dma_init:
22262306a36Sopenharmony_ci	spin_lock(&dev_list.lock);
22362306a36Sopenharmony_ci	list_del(&cryp->list);
22462306a36Sopenharmony_ci	spin_unlock(&dev_list.lock);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	clk_disable_unprepare(cryp->hclk);
22762306a36Sopenharmony_ci	clk_disable_unprepare(cryp->ahb);
22862306a36Sopenharmony_ci	reset_control_assert(cryp->rst);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	tasklet_kill(&cryp->aes_done);
23162306a36Sopenharmony_ci	tasklet_kill(&cryp->hash_done);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return ret;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void starfive_cryp_remove(struct platform_device *pdev)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct starfive_cryp_dev *cryp = platform_get_drvdata(pdev);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	starfive_aes_unregister_algs();
24162306a36Sopenharmony_ci	starfive_hash_unregister_algs();
24262306a36Sopenharmony_ci	starfive_rsa_unregister_algs();
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	tasklet_kill(&cryp->aes_done);
24562306a36Sopenharmony_ci	tasklet_kill(&cryp->hash_done);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	crypto_engine_stop(cryp->engine);
24862306a36Sopenharmony_ci	crypto_engine_exit(cryp->engine);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	starfive_dma_cleanup(cryp);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	spin_lock(&dev_list.lock);
25362306a36Sopenharmony_ci	list_del(&cryp->list);
25462306a36Sopenharmony_ci	spin_unlock(&dev_list.lock);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	clk_disable_unprepare(cryp->hclk);
25762306a36Sopenharmony_ci	clk_disable_unprepare(cryp->ahb);
25862306a36Sopenharmony_ci	reset_control_assert(cryp->rst);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct of_device_id starfive_dt_ids[] __maybe_unused = {
26262306a36Sopenharmony_ci	{ .compatible = "starfive,jh7110-crypto", .data = NULL},
26362306a36Sopenharmony_ci	{},
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, starfive_dt_ids);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic struct platform_driver starfive_cryp_driver = {
26862306a36Sopenharmony_ci	.probe  = starfive_cryp_probe,
26962306a36Sopenharmony_ci	.remove_new = starfive_cryp_remove,
27062306a36Sopenharmony_ci	.driver = {
27162306a36Sopenharmony_ci		.name           = DRIVER_NAME,
27262306a36Sopenharmony_ci		.of_match_table = starfive_dt_ids,
27362306a36Sopenharmony_ci	},
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cimodule_platform_driver(starfive_cryp_driver);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
27962306a36Sopenharmony_ciMODULE_DESCRIPTION("StarFive JH7110 Cryptographic Module");
280