18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <crypto/algapi.h> 158c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h> 168c2ecf20Sopenharmony_ci#include <crypto/sha.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "core.h" 198c2ecf20Sopenharmony_ci#include "cipher.h" 208c2ecf20Sopenharmony_ci#include "sha.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define QCE_MAJOR_VERSION5 0x05 238c2ecf20Sopenharmony_ci#define QCE_QUEUE_LENGTH 1 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct qce_algo_ops *qce_ops[] = { 268c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_QCE_SKCIPHER 278c2ecf20Sopenharmony_ci &skcipher_ops, 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_QCE_SHA 308c2ecf20Sopenharmony_ci &ahash_ops, 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void qce_unregister_algs(struct qce_device *qce) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci const struct qce_algo_ops *ops; 378c2ecf20Sopenharmony_ci int i; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 408c2ecf20Sopenharmony_ci ops = qce_ops[i]; 418c2ecf20Sopenharmony_ci ops->unregister_algs(qce); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int qce_register_algs(struct qce_device *qce) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci const struct qce_algo_ops *ops; 488c2ecf20Sopenharmony_ci int i, ret = -ENODEV; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 518c2ecf20Sopenharmony_ci ops = qce_ops[i]; 528c2ecf20Sopenharmony_ci ret = ops->register_algs(qce); 538c2ecf20Sopenharmony_ci if (ret) 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int qce_handle_request(struct crypto_async_request *async_req) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci int ret = -EINVAL, i; 638c2ecf20Sopenharmony_ci const struct qce_algo_ops *ops; 648c2ecf20Sopenharmony_ci u32 type = crypto_tfm_alg_type(async_req->tfm); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qce_ops); i++) { 678c2ecf20Sopenharmony_ci ops = qce_ops[i]; 688c2ecf20Sopenharmony_ci if (type != ops->type) 698c2ecf20Sopenharmony_ci continue; 708c2ecf20Sopenharmony_ci ret = ops->async_req_handle(async_req); 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int qce_handle_queue(struct qce_device *qce, 788c2ecf20Sopenharmony_ci struct crypto_async_request *req) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct crypto_async_request *async_req, *backlog; 818c2ecf20Sopenharmony_ci unsigned long flags; 828c2ecf20Sopenharmony_ci int ret = 0, err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci spin_lock_irqsave(&qce->lock, flags); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (req) 878c2ecf20Sopenharmony_ci ret = crypto_enqueue_request(&qce->queue, req); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* busy, do not dequeue request */ 908c2ecf20Sopenharmony_ci if (qce->req) { 918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci backlog = crypto_get_backlog(&qce->queue); 968c2ecf20Sopenharmony_ci async_req = crypto_dequeue_request(&qce->queue); 978c2ecf20Sopenharmony_ci if (async_req) 988c2ecf20Sopenharmony_ci qce->req = async_req; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!async_req) 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (backlog) { 1068c2ecf20Sopenharmony_ci spin_lock_bh(&qce->lock); 1078c2ecf20Sopenharmony_ci backlog->complete(backlog, -EINPROGRESS); 1088c2ecf20Sopenharmony_ci spin_unlock_bh(&qce->lock); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci err = qce_handle_request(async_req); 1128c2ecf20Sopenharmony_ci if (err) { 1138c2ecf20Sopenharmony_ci qce->result = err; 1148c2ecf20Sopenharmony_ci tasklet_schedule(&qce->done_tasklet); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void qce_tasklet_req_done(unsigned long data) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct qce_device *qce = (struct qce_device *)data; 1238c2ecf20Sopenharmony_ci struct crypto_async_request *req; 1248c2ecf20Sopenharmony_ci unsigned long flags; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci spin_lock_irqsave(&qce->lock, flags); 1278c2ecf20Sopenharmony_ci req = qce->req; 1288c2ecf20Sopenharmony_ci qce->req = NULL; 1298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qce->lock, flags); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (req) 1328c2ecf20Sopenharmony_ci req->complete(req, qce->result); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci qce_handle_queue(qce, NULL); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int qce_async_request_enqueue(struct qce_device *qce, 1388c2ecf20Sopenharmony_ci struct crypto_async_request *req) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return qce_handle_queue(qce, req); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void qce_async_request_done(struct qce_device *qce, int ret) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci qce->result = ret; 1468c2ecf20Sopenharmony_ci tasklet_schedule(&qce->done_tasklet); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int qce_check_version(struct qce_device *qce) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci u32 major, minor, step; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci qce_get_version(qce, &major, &minor, &step); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * the driver does not support v5 with minor 0 because it has special 1578c2ecf20Sopenharmony_ci * alignment requirements. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci if (major != QCE_MAJOR_VERSION5 || minor == 0) 1608c2ecf20Sopenharmony_ci return -ENODEV; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci qce->burst_size = QCE_BAM_BURST_SIZE; 1638c2ecf20Sopenharmony_ci qce->pipe_pair_id = 1; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dev_dbg(qce->dev, "Crypto device found, version %d.%d.%d\n", 1668c2ecf20Sopenharmony_ci major, minor, step); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int qce_crypto_probe(struct platform_device *pdev) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1748c2ecf20Sopenharmony_ci struct qce_device *qce; 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci qce = devm_kzalloc(dev, sizeof(*qce), GFP_KERNEL); 1788c2ecf20Sopenharmony_ci if (!qce) 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci qce->dev = dev; 1828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, qce); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci qce->base = devm_platform_ioremap_resource(pdev, 0); 1858c2ecf20Sopenharmony_ci if (IS_ERR(qce->base)) 1868c2ecf20Sopenharmony_ci return PTR_ERR(qce->base); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 1898c2ecf20Sopenharmony_ci if (ret < 0) 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci qce->core = devm_clk_get(qce->dev, "core"); 1938c2ecf20Sopenharmony_ci if (IS_ERR(qce->core)) 1948c2ecf20Sopenharmony_ci return PTR_ERR(qce->core); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci qce->iface = devm_clk_get(qce->dev, "iface"); 1978c2ecf20Sopenharmony_ci if (IS_ERR(qce->iface)) 1988c2ecf20Sopenharmony_ci return PTR_ERR(qce->iface); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci qce->bus = devm_clk_get(qce->dev, "bus"); 2018c2ecf20Sopenharmony_ci if (IS_ERR(qce->bus)) 2028c2ecf20Sopenharmony_ci return PTR_ERR(qce->bus); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qce->core); 2058c2ecf20Sopenharmony_ci if (ret) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qce->iface); 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci goto err_clks_core; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qce->bus); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto err_clks_iface; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = qce_dma_request(qce->dev, &qce->dma); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci goto err_clks; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = qce_check_version(qce); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci goto err_clks; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci spin_lock_init(&qce->lock); 2258c2ecf20Sopenharmony_ci tasklet_init(&qce->done_tasklet, qce_tasklet_req_done, 2268c2ecf20Sopenharmony_ci (unsigned long)qce); 2278c2ecf20Sopenharmony_ci crypto_init_queue(&qce->queue, QCE_QUEUE_LENGTH); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci qce->async_req_enqueue = qce_async_request_enqueue; 2308c2ecf20Sopenharmony_ci qce->async_req_done = qce_async_request_done; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = qce_register_algs(qce); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci goto err_dma; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cierr_dma: 2398c2ecf20Sopenharmony_ci qce_dma_release(&qce->dma); 2408c2ecf20Sopenharmony_cierr_clks: 2418c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->bus); 2428c2ecf20Sopenharmony_cierr_clks_iface: 2438c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->iface); 2448c2ecf20Sopenharmony_cierr_clks_core: 2458c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->core); 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int qce_crypto_remove(struct platform_device *pdev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct qce_device *qce = platform_get_drvdata(pdev); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci tasklet_kill(&qce->done_tasklet); 2548c2ecf20Sopenharmony_ci qce_unregister_algs(qce); 2558c2ecf20Sopenharmony_ci qce_dma_release(&qce->dma); 2568c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->bus); 2578c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->iface); 2588c2ecf20Sopenharmony_ci clk_disable_unprepare(qce->core); 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic const struct of_device_id qce_crypto_of_match[] = { 2638c2ecf20Sopenharmony_ci { .compatible = "qcom,crypto-v5.1", }, 2648c2ecf20Sopenharmony_ci {} 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qce_crypto_of_match); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct platform_driver qce_crypto_driver = { 2698c2ecf20Sopenharmony_ci .probe = qce_crypto_probe, 2708c2ecf20Sopenharmony_ci .remove = qce_crypto_remove, 2718c2ecf20Sopenharmony_ci .driver = { 2728c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 2738c2ecf20Sopenharmony_ci .of_match_table = qce_crypto_of_match, 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_cimodule_platform_driver(qce_crypto_driver); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm crypto engine driver"); 2808c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" KBUILD_MODNAME); 2818c2ecf20Sopenharmony_ciMODULE_AUTHOR("The Linux Foundation"); 282