162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 862306a36Sopenharmony_ci#include <linux/interconnect.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <crypto/algapi.h> 1662306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "core.h" 1962306a36Sopenharmony_ci#include "cipher.h" 2062306a36Sopenharmony_ci#include "sha.h" 2162306a36Sopenharmony_ci#include "aead.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define QCE_MAJOR_VERSION5 0x05 2462306a36Sopenharmony_ci#define QCE_QUEUE_LENGTH 1 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define QCE_DEFAULT_MEM_BANDWIDTH 393600 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct qce_algo_ops *qce_ops[] = { 2962306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_QCE_SKCIPHER 3062306a36Sopenharmony_ci &skcipher_ops, 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_QCE_SHA 3362306a36Sopenharmony_ci &ahash_ops, 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_QCE_AEAD 3662306a36Sopenharmony_ci &aead_ops, 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void qce_unregister_algs(struct qce_device *qce) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci const struct qce_algo_ops *ops; 4362306a36Sopenharmony_ci int i; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 4662306a36Sopenharmony_ci ops = qce_ops[i]; 4762306a36Sopenharmony_ci ops->unregister_algs(qce); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int qce_register_algs(struct qce_device *qce) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci const struct qce_algo_ops *ops; 5462306a36Sopenharmony_ci int i, ret = -ENODEV; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 5762306a36Sopenharmony_ci ops = qce_ops[i]; 5862306a36Sopenharmony_ci ret = ops->register_algs(qce); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int qce_handle_request(struct crypto_async_request *async_req) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int ret = -EINVAL, i; 6962306a36Sopenharmony_ci const struct qce_algo_ops *ops; 7062306a36Sopenharmony_ci u32 type = crypto_tfm_alg_type(async_req->tfm); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 7362306a36Sopenharmony_ci ops = qce_ops[i]; 7462306a36Sopenharmony_ci if (type != ops->type) 7562306a36Sopenharmony_ci continue; 7662306a36Sopenharmony_ci ret = ops->async_req_handle(async_req); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int qce_handle_queue(struct qce_device *qce, 8462306a36Sopenharmony_ci struct crypto_async_request *req) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct crypto_async_request *async_req, *backlog; 8762306a36Sopenharmony_ci unsigned long flags; 8862306a36Sopenharmony_ci int ret = 0, err; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci spin_lock_irqsave(&qce->lock, flags); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (req) 9362306a36Sopenharmony_ci ret = crypto_enqueue_request(&qce->queue, req); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* busy, do not dequeue request */ 9662306a36Sopenharmony_ci if (qce->req) { 9762306a36Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci backlog = crypto_get_backlog(&qce->queue); 10262306a36Sopenharmony_ci async_req = crypto_dequeue_request(&qce->queue); 10362306a36Sopenharmony_ci if (async_req) 10462306a36Sopenharmony_ci qce->req = async_req; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!async_req) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (backlog) { 11262306a36Sopenharmony_ci spin_lock_bh(&qce->lock); 11362306a36Sopenharmony_ci crypto_request_complete(backlog, -EINPROGRESS); 11462306a36Sopenharmony_ci spin_unlock_bh(&qce->lock); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci err = qce_handle_request(async_req); 11862306a36Sopenharmony_ci if (err) { 11962306a36Sopenharmony_ci qce->result = err; 12062306a36Sopenharmony_ci tasklet_schedule(&qce->done_tasklet); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void qce_tasklet_req_done(unsigned long data) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct qce_device *qce = (struct qce_device *)data; 12962306a36Sopenharmony_ci struct crypto_async_request *req; 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_irqsave(&qce->lock, flags); 13362306a36Sopenharmony_ci req = qce->req; 13462306a36Sopenharmony_ci qce->req = NULL; 13562306a36Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (req) 13862306a36Sopenharmony_ci crypto_request_complete(req, qce->result); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci qce_handle_queue(qce, NULL); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int qce_async_request_enqueue(struct qce_device *qce, 14462306a36Sopenharmony_ci struct crypto_async_request *req) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci return qce_handle_queue(qce, req); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void qce_async_request_done(struct qce_device *qce, int ret) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci qce->result = ret; 15262306a36Sopenharmony_ci tasklet_schedule(&qce->done_tasklet); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int qce_check_version(struct qce_device *qce) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 major, minor, step; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci qce_get_version(qce, &major, &minor, &step); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * the driver does not support v5 with minor 0 because it has special 16362306a36Sopenharmony_ci * alignment requirements. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (major != QCE_MAJOR_VERSION5 || minor == 0) 16662306a36Sopenharmony_ci return -ENODEV; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci qce->burst_size = QCE_BAM_BURST_SIZE; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Rx and tx pipes are treated as a pair inside CE. 17262306a36Sopenharmony_ci * Pipe pair number depends on the actual BAM dma pipe 17362306a36Sopenharmony_ci * that is used for transfers. The BAM dma pipes are passed 17462306a36Sopenharmony_ci * from the device tree and used to derive the pipe pair 17562306a36Sopenharmony_ci * id in the CE driver as follows. 17662306a36Sopenharmony_ci * BAM dma pipes(rx, tx) CE pipe pair id 17762306a36Sopenharmony_ci * 0,1 0 17862306a36Sopenharmony_ci * 2,3 1 17962306a36Sopenharmony_ci * 4,5 2 18062306a36Sopenharmony_ci * 6,7 3 18162306a36Sopenharmony_ci * ... 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci qce->pipe_pair_id = qce->dma.rxchan->chan_id >> 1; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci dev_dbg(qce->dev, "Crypto device found, version %d.%d.%d\n", 18662306a36Sopenharmony_ci major, minor, step); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int qce_crypto_probe(struct platform_device *pdev) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 19462306a36Sopenharmony_ci struct qce_device *qce; 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci qce = devm_kzalloc(dev, sizeof(*qce), GFP_KERNEL); 19862306a36Sopenharmony_ci if (!qce) 19962306a36Sopenharmony_ci return -ENOMEM; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci qce->dev = dev; 20262306a36Sopenharmony_ci platform_set_drvdata(pdev, qce); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci qce->base = devm_platform_ioremap_resource(pdev, 0); 20562306a36Sopenharmony_ci if (IS_ERR(qce->base)) 20662306a36Sopenharmony_ci return PTR_ERR(qce->base); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci qce->core = devm_clk_get_optional(qce->dev, "core"); 21362306a36Sopenharmony_ci if (IS_ERR(qce->core)) 21462306a36Sopenharmony_ci return PTR_ERR(qce->core); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci qce->iface = devm_clk_get_optional(qce->dev, "iface"); 21762306a36Sopenharmony_ci if (IS_ERR(qce->iface)) 21862306a36Sopenharmony_ci return PTR_ERR(qce->iface); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci qce->bus = devm_clk_get_optional(qce->dev, "bus"); 22162306a36Sopenharmony_ci if (IS_ERR(qce->bus)) 22262306a36Sopenharmony_ci return PTR_ERR(qce->bus); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci qce->mem_path = devm_of_icc_get(qce->dev, "memory"); 22562306a36Sopenharmony_ci if (IS_ERR(qce->mem_path)) 22662306a36Sopenharmony_ci return PTR_ERR(qce->mem_path); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ret = icc_set_bw(qce->mem_path, QCE_DEFAULT_MEM_BANDWIDTH, QCE_DEFAULT_MEM_BANDWIDTH); 22962306a36Sopenharmony_ci if (ret) 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = clk_prepare_enable(qce->core); 23362306a36Sopenharmony_ci if (ret) 23462306a36Sopenharmony_ci goto err_mem_path_disable; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = clk_prepare_enable(qce->iface); 23762306a36Sopenharmony_ci if (ret) 23862306a36Sopenharmony_ci goto err_clks_core; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = clk_prepare_enable(qce->bus); 24162306a36Sopenharmony_ci if (ret) 24262306a36Sopenharmony_ci goto err_clks_iface; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ret = qce_dma_request(qce->dev, &qce->dma); 24562306a36Sopenharmony_ci if (ret) 24662306a36Sopenharmony_ci goto err_clks; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = qce_check_version(qce); 24962306a36Sopenharmony_ci if (ret) 25062306a36Sopenharmony_ci goto err_clks; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci spin_lock_init(&qce->lock); 25362306a36Sopenharmony_ci tasklet_init(&qce->done_tasklet, qce_tasklet_req_done, 25462306a36Sopenharmony_ci (unsigned long)qce); 25562306a36Sopenharmony_ci crypto_init_queue(&qce->queue, QCE_QUEUE_LENGTH); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci qce->async_req_enqueue = qce_async_request_enqueue; 25862306a36Sopenharmony_ci qce->async_req_done = qce_async_request_done; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = qce_register_algs(qce); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci goto err_dma; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cierr_dma: 26762306a36Sopenharmony_ci qce_dma_release(&qce->dma); 26862306a36Sopenharmony_cierr_clks: 26962306a36Sopenharmony_ci clk_disable_unprepare(qce->bus); 27062306a36Sopenharmony_cierr_clks_iface: 27162306a36Sopenharmony_ci clk_disable_unprepare(qce->iface); 27262306a36Sopenharmony_cierr_clks_core: 27362306a36Sopenharmony_ci clk_disable_unprepare(qce->core); 27462306a36Sopenharmony_cierr_mem_path_disable: 27562306a36Sopenharmony_ci icc_set_bw(qce->mem_path, 0, 0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int qce_crypto_remove(struct platform_device *pdev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct qce_device *qce = platform_get_drvdata(pdev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci tasklet_kill(&qce->done_tasklet); 28562306a36Sopenharmony_ci qce_unregister_algs(qce); 28662306a36Sopenharmony_ci qce_dma_release(&qce->dma); 28762306a36Sopenharmony_ci clk_disable_unprepare(qce->bus); 28862306a36Sopenharmony_ci clk_disable_unprepare(qce->iface); 28962306a36Sopenharmony_ci clk_disable_unprepare(qce->core); 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct of_device_id qce_crypto_of_match[] = { 29462306a36Sopenharmony_ci { .compatible = "qcom,crypto-v5.1", }, 29562306a36Sopenharmony_ci { .compatible = "qcom,crypto-v5.4", }, 29662306a36Sopenharmony_ci { .compatible = "qcom,qce", }, 29762306a36Sopenharmony_ci {} 29862306a36Sopenharmony_ci}; 29962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qce_crypto_of_match); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic struct platform_driver qce_crypto_driver = { 30262306a36Sopenharmony_ci .probe = qce_crypto_probe, 30362306a36Sopenharmony_ci .remove = qce_crypto_remove, 30462306a36Sopenharmony_ci .driver = { 30562306a36Sopenharmony_ci .name = KBUILD_MODNAME, 30662306a36Sopenharmony_ci .of_match_table = qce_crypto_of_match, 30762306a36Sopenharmony_ci }, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_cimodule_platform_driver(qce_crypto_driver); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 31262306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm crypto engine driver"); 31362306a36Sopenharmony_ciMODULE_ALIAS("platform:" KBUILD_MODNAME); 31462306a36Sopenharmony_ciMODULE_AUTHOR("The Linux Foundation"); 315