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