summaryrefslogtreecommitdiff
path: root/drivers/crypto/ti/dthev2-common.c
blob: c39d37933b9ee342d82de54da1dc003b1f299bd2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: GPL-2.0-only
/*
 * K3 DTHE V2 crypto accelerator driver
 *
 * Copyright (C) Texas Instruments 2025 - https://www.ti.com
 * Author: T Pratham <t-pratham@ti.com>
 */

#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <crypto/engine.h>
#include <crypto/internal/aead.h>
#include <crypto/internal/skcipher.h>

#include "dthev2-common.h"

#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>

#define DRIVER_NAME	"dthev2"

static struct dthe_list dthe_dev_list = {
	.dev_list = LIST_HEAD_INIT(dthe_dev_list.dev_list),
	.lock = __SPIN_LOCK_UNLOCKED(dthe_dev_list.lock),
};

struct dthe_data *dthe_get_dev(struct dthe_tfm_ctx *ctx)
{
	struct dthe_data *dev_data;

	if (ctx->dev_data)
		return ctx->dev_data;

	spin_lock_bh(&dthe_dev_list.lock);
	dev_data = list_first_entry(&dthe_dev_list.dev_list, struct dthe_data, list);
	if (dev_data)
		list_move_tail(&dev_data->list, &dthe_dev_list.dev_list);
	spin_unlock_bh(&dthe_dev_list.lock);

	return dev_data;
}

static int dthe_dma_init(struct dthe_data *dev_data)
{
	int ret;
	struct dma_slave_config cfg;

	dev_data->dma_aes_rx = NULL;
	dev_data->dma_aes_tx = NULL;
	dev_data->dma_sha_tx = NULL;

	dev_data->dma_aes_rx = dma_request_chan(dev_data->dev, "rx");
	if (IS_ERR(dev_data->dma_aes_rx)) {
		return dev_err_probe(dev_data->dev, PTR_ERR(dev_data->dma_aes_rx),
				     "Unable to request rx DMA channel\n");
	}

	dev_data->dma_aes_tx = dma_request_chan(dev_data->dev, "tx1");
	if (IS_ERR(dev_data->dma_aes_tx)) {
		ret = dev_err_probe(dev_data->dev, PTR_ERR(dev_data->dma_aes_tx),
				    "Unable to request tx1 DMA channel\n");
		goto err_dma_aes_tx;
	}

	dev_data->dma_sha_tx = dma_request_chan(dev_data->dev, "tx2");
	if (IS_ERR(dev_data->dma_sha_tx)) {
		ret = dev_err_probe(dev_data->dev, PTR_ERR(dev_data->dma_sha_tx),
				    "Unable to request tx2 DMA channel\n");
		goto err_dma_sha_tx;
	}

	memzero_explicit(&cfg, sizeof(cfg));

	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	cfg.src_maxburst = 4;

	ret = dmaengine_slave_config(dev_data->dma_aes_rx, &cfg);
	if (ret) {
		dev_err(dev_data->dev, "Can't configure IN dmaengine slave: %d\n", ret);
		goto err_dma_config;
	}

	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	cfg.dst_maxburst = 4;

	ret = dmaengine_slave_config(dev_data->dma_aes_tx, &cfg);
	if (ret) {
		dev_err(dev_data->dev, "Can't configure OUT dmaengine slave: %d\n", ret);
		goto err_dma_config;
	}

	return 0;

err_dma_config:
	dma_release_channel(dev_data->dma_sha_tx);
err_dma_sha_tx:
	dma_release_channel(dev_data->dma_aes_tx);
err_dma_aes_tx:
	dma_release_channel(dev_data->dma_aes_rx);

	return ret;
}

static int dthe_register_algs(void)
{
	return dthe_register_aes_algs();
}

static void dthe_unregister_algs(void)
{
	dthe_unregister_aes_algs();
}

static int dthe_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct dthe_data *dev_data;
	int ret;

	dev_data = devm_kzalloc(dev, sizeof(*dev_data), GFP_KERNEL);
	if (!dev_data)
		return -ENOMEM;

	dev_data->dev = dev;
	dev_data->regs = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(dev_data->regs))
		return PTR_ERR(dev_data->regs);

	platform_set_drvdata(pdev, dev_data);

	spin_lock(&dthe_dev_list.lock);
	list_add(&dev_data->list, &dthe_dev_list.dev_list);
	spin_unlock(&dthe_dev_list.lock);

	ret = dthe_dma_init(dev_data);
	if (ret)
		goto probe_dma_err;

	dev_data->engine = crypto_engine_alloc_init(dev, 1);
	if (!dev_data->engine) {
		ret = -ENOMEM;
		goto probe_engine_err;
	}

	ret = crypto_engine_start(dev_data->engine);
	if (ret) {
		dev_err(dev, "Failed to start crypto engine\n");
		goto probe_engine_start_err;
	}

	ret = dthe_register_algs();
	if (ret) {
		dev_err(dev, "Failed to register algs\n");
		goto probe_engine_start_err;
	}

	return 0;

probe_engine_start_err:
	crypto_engine_exit(dev_data->engine);
probe_engine_err:
	dma_release_channel(dev_data->dma_aes_rx);
	dma_release_channel(dev_data->dma_aes_tx);
	dma_release_channel(dev_data->dma_sha_tx);
probe_dma_err:
	spin_lock(&dthe_dev_list.lock);
	list_del(&dev_data->list);
	spin_unlock(&dthe_dev_list.lock);

	return ret;
}

static void dthe_remove(struct platform_device *pdev)
{
	struct dthe_data *dev_data = platform_get_drvdata(pdev);

	spin_lock(&dthe_dev_list.lock);
	list_del(&dev_data->list);
	spin_unlock(&dthe_dev_list.lock);

	dthe_unregister_algs();

	crypto_engine_exit(dev_data->engine);

	dma_release_channel(dev_data->dma_aes_rx);
	dma_release_channel(dev_data->dma_aes_tx);
	dma_release_channel(dev_data->dma_sha_tx);
}

static const struct of_device_id dthe_of_match[] = {
	{ .compatible = "ti,am62l-dthev2", },
	{},
};
MODULE_DEVICE_TABLE(of, dthe_of_match);

static struct platform_driver dthe_driver = {
	.probe	= dthe_probe,
	.remove	= dthe_remove,
	.driver = {
		.name		= DRIVER_NAME,
		.of_match_table	= dthe_of_match,
	},
};

module_platform_driver(dthe_driver);

MODULE_AUTHOR("T Pratham <t-pratham@ti.com>");
MODULE_DESCRIPTION("Texas Instruments DTHE V2 driver");
MODULE_LICENSE("GPL");