18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Crypto acceleration support for Rockchip RK3288
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Zain Wang <zain.wang@rock-chips.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Some ideas are from marvell-cesa.c and s5p-sss.c driver.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "rk3288_crypto.h"
138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci#include <linux/crypto.h>
198c2ecf20Sopenharmony_ci#include <linux/reset.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int rk_crypto_enable_clk(struct rk_crypto_info *dev)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	int err;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dev->sclk);
268c2ecf20Sopenharmony_ci	if (err) {
278c2ecf20Sopenharmony_ci		dev_err(dev->dev, "[%s:%d], Couldn't enable clock sclk\n",
288c2ecf20Sopenharmony_ci			__func__, __LINE__);
298c2ecf20Sopenharmony_ci		goto err_return;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dev->aclk);
328c2ecf20Sopenharmony_ci	if (err) {
338c2ecf20Sopenharmony_ci		dev_err(dev->dev, "[%s:%d], Couldn't enable clock aclk\n",
348c2ecf20Sopenharmony_ci			__func__, __LINE__);
358c2ecf20Sopenharmony_ci		goto err_aclk;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dev->hclk);
388c2ecf20Sopenharmony_ci	if (err) {
398c2ecf20Sopenharmony_ci		dev_err(dev->dev, "[%s:%d], Couldn't enable clock hclk\n",
408c2ecf20Sopenharmony_ci			__func__, __LINE__);
418c2ecf20Sopenharmony_ci		goto err_hclk;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dev->dmaclk);
448c2ecf20Sopenharmony_ci	if (err) {
458c2ecf20Sopenharmony_ci		dev_err(dev->dev, "[%s:%d], Couldn't enable clock dmaclk\n",
468c2ecf20Sopenharmony_ci			__func__, __LINE__);
478c2ecf20Sopenharmony_ci		goto err_dmaclk;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci	return err;
508c2ecf20Sopenharmony_cierr_dmaclk:
518c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->hclk);
528c2ecf20Sopenharmony_cierr_hclk:
538c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->aclk);
548c2ecf20Sopenharmony_cierr_aclk:
558c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->sclk);
568c2ecf20Sopenharmony_cierr_return:
578c2ecf20Sopenharmony_ci	return err;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void rk_crypto_disable_clk(struct rk_crypto_info *dev)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->dmaclk);
638c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->hclk);
648c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->aclk);
658c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->sclk);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic irqreturn_t rk_crypto_irq_handle(int irq, void *dev_id)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct rk_crypto_info *dev  = platform_get_drvdata(dev_id);
718c2ecf20Sopenharmony_ci	u32 interrupt_status;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	interrupt_status = CRYPTO_READ(dev, RK_CRYPTO_INTSTS);
748c2ecf20Sopenharmony_ci	CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, interrupt_status);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dev->status = 1;
778c2ecf20Sopenharmony_ci	if (interrupt_status & 0x0a) {
788c2ecf20Sopenharmony_ci		dev_warn(dev->dev, "DMA Error\n");
798c2ecf20Sopenharmony_ci		dev->status = 0;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	complete(&dev->complete);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct rk_crypto_tmp *rk_cipher_algs[] = {
878c2ecf20Sopenharmony_ci	&rk_ecb_aes_alg,
888c2ecf20Sopenharmony_ci	&rk_cbc_aes_alg,
898c2ecf20Sopenharmony_ci	&rk_ecb_des_alg,
908c2ecf20Sopenharmony_ci	&rk_cbc_des_alg,
918c2ecf20Sopenharmony_ci	&rk_ecb_des3_ede_alg,
928c2ecf20Sopenharmony_ci	&rk_cbc_des3_ede_alg,
938c2ecf20Sopenharmony_ci	&rk_ahash_sha1,
948c2ecf20Sopenharmony_ci	&rk_ahash_sha256,
958c2ecf20Sopenharmony_ci	&rk_ahash_md5,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int rk_crypto_register(struct rk_crypto_info *crypto_info)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	unsigned int i, k;
1018c2ecf20Sopenharmony_ci	int err = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
1048c2ecf20Sopenharmony_ci		rk_cipher_algs[i]->dev = crypto_info;
1058c2ecf20Sopenharmony_ci		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
1068c2ecf20Sopenharmony_ci			err = crypto_register_skcipher(
1078c2ecf20Sopenharmony_ci					&rk_cipher_algs[i]->alg.skcipher);
1088c2ecf20Sopenharmony_ci		else
1098c2ecf20Sopenharmony_ci			err = crypto_register_ahash(
1108c2ecf20Sopenharmony_ci					&rk_cipher_algs[i]->alg.hash);
1118c2ecf20Sopenharmony_ci		if (err)
1128c2ecf20Sopenharmony_ci			goto err_cipher_algs;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cierr_cipher_algs:
1178c2ecf20Sopenharmony_ci	for (k = 0; k < i; k++) {
1188c2ecf20Sopenharmony_ci		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
1198c2ecf20Sopenharmony_ci			crypto_unregister_skcipher(&rk_cipher_algs[k]->alg.skcipher);
1208c2ecf20Sopenharmony_ci		else
1218c2ecf20Sopenharmony_ci			crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	return err;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void rk_crypto_unregister(void)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	unsigned int i;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
1318c2ecf20Sopenharmony_ci		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
1328c2ecf20Sopenharmony_ci			crypto_unregister_skcipher(&rk_cipher_algs[i]->alg.skcipher);
1338c2ecf20Sopenharmony_ci		else
1348c2ecf20Sopenharmony_ci			crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void rk_crypto_action(void *data)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct rk_crypto_info *crypto_info = data;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	reset_control_assert(crypto_info->rst);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic const struct of_device_id crypto_of_id_table[] = {
1468c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk3288-crypto" },
1478c2ecf20Sopenharmony_ci	{}
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, crypto_of_id_table);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int rk_crypto_probe(struct platform_device *pdev)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1548c2ecf20Sopenharmony_ci	struct rk_crypto_info *crypto_info;
1558c2ecf20Sopenharmony_ci	int err = 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	crypto_info = devm_kzalloc(&pdev->dev,
1588c2ecf20Sopenharmony_ci				   sizeof(*crypto_info), GFP_KERNEL);
1598c2ecf20Sopenharmony_ci	if (!crypto_info) {
1608c2ecf20Sopenharmony_ci		err = -ENOMEM;
1618c2ecf20Sopenharmony_ci		goto err_crypto;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	crypto_info->rst = devm_reset_control_get(dev, "crypto-rst");
1658c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->rst)) {
1668c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->rst);
1678c2ecf20Sopenharmony_ci		goto err_crypto;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	reset_control_assert(crypto_info->rst);
1718c2ecf20Sopenharmony_ci	usleep_range(10, 20);
1728c2ecf20Sopenharmony_ci	reset_control_deassert(crypto_info->rst);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	err = devm_add_action_or_reset(dev, rk_crypto_action, crypto_info);
1758c2ecf20Sopenharmony_ci	if (err)
1768c2ecf20Sopenharmony_ci		goto err_crypto;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	crypto_info->reg = devm_platform_ioremap_resource(pdev, 0);
1798c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->reg)) {
1808c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->reg);
1818c2ecf20Sopenharmony_ci		goto err_crypto;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	crypto_info->aclk = devm_clk_get(&pdev->dev, "aclk");
1858c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->aclk)) {
1868c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->aclk);
1878c2ecf20Sopenharmony_ci		goto err_crypto;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	crypto_info->hclk = devm_clk_get(&pdev->dev, "hclk");
1918c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->hclk)) {
1928c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->hclk);
1938c2ecf20Sopenharmony_ci		goto err_crypto;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	crypto_info->sclk = devm_clk_get(&pdev->dev, "sclk");
1978c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->sclk)) {
1988c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->sclk);
1998c2ecf20Sopenharmony_ci		goto err_crypto;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	crypto_info->dmaclk = devm_clk_get(&pdev->dev, "apb_pclk");
2038c2ecf20Sopenharmony_ci	if (IS_ERR(crypto_info->dmaclk)) {
2048c2ecf20Sopenharmony_ci		err = PTR_ERR(crypto_info->dmaclk);
2058c2ecf20Sopenharmony_ci		goto err_crypto;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	crypto_info->irq = platform_get_irq(pdev, 0);
2098c2ecf20Sopenharmony_ci	if (crypto_info->irq < 0) {
2108c2ecf20Sopenharmony_ci		dev_warn(crypto_info->dev,
2118c2ecf20Sopenharmony_ci			 "control Interrupt is not available.\n");
2128c2ecf20Sopenharmony_ci		err = crypto_info->irq;
2138c2ecf20Sopenharmony_ci		goto err_crypto;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	err = devm_request_irq(&pdev->dev, crypto_info->irq,
2178c2ecf20Sopenharmony_ci			       rk_crypto_irq_handle, IRQF_SHARED,
2188c2ecf20Sopenharmony_ci			       "rk-crypto", pdev);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (err) {
2218c2ecf20Sopenharmony_ci		dev_err(crypto_info->dev, "irq request failed.\n");
2228c2ecf20Sopenharmony_ci		goto err_crypto;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	crypto_info->dev = &pdev->dev;
2268c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, crypto_info);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	crypto_info->engine = crypto_engine_alloc_init(&pdev->dev, true);
2298c2ecf20Sopenharmony_ci	crypto_engine_start(crypto_info->engine);
2308c2ecf20Sopenharmony_ci	init_completion(&crypto_info->complete);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	rk_crypto_enable_clk(crypto_info);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	err = rk_crypto_register(crypto_info);
2358c2ecf20Sopenharmony_ci	if (err) {
2368c2ecf20Sopenharmony_ci		dev_err(dev, "err in register alg");
2378c2ecf20Sopenharmony_ci		goto err_register_alg;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dev_info(dev, "Crypto Accelerator successfully registered\n");
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cierr_register_alg:
2448c2ecf20Sopenharmony_ci	crypto_engine_exit(crypto_info->engine);
2458c2ecf20Sopenharmony_cierr_crypto:
2468c2ecf20Sopenharmony_ci	dev_err(dev, "Crypto Accelerator not successfully registered\n");
2478c2ecf20Sopenharmony_ci	return err;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int rk_crypto_remove(struct platform_device *pdev)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct rk_crypto_info *crypto_tmp = platform_get_drvdata(pdev);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	rk_crypto_unregister();
2558c2ecf20Sopenharmony_ci	rk_crypto_disable_clk(crypto_tmp);
2568c2ecf20Sopenharmony_ci	crypto_engine_exit(crypto_tmp->engine);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic struct platform_driver crypto_driver = {
2618c2ecf20Sopenharmony_ci	.probe		= rk_crypto_probe,
2628c2ecf20Sopenharmony_ci	.remove		= rk_crypto_remove,
2638c2ecf20Sopenharmony_ci	.driver		= {
2648c2ecf20Sopenharmony_ci		.name	= "rk3288-crypto",
2658c2ecf20Sopenharmony_ci		.of_match_table	= crypto_of_id_table,
2668c2ecf20Sopenharmony_ci	},
2678c2ecf20Sopenharmony_ci};
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cimodule_platform_driver(crypto_driver);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zain Wang <zain.wang@rock-chips.com>");
2728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for Rockchip's cryptographic engine");
2738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
274