Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
433cd2c6 ZW |
2 | /* |
3 | * Crypto acceleration support for Rockchip RK3288 | |
4 | * | |
5 | * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd | |
6 | * | |
7 | * Author: Zain Wang <zain.wang@rock-chips.com> | |
8 | * | |
433cd2c6 ZW |
9 | * Some ideas are from marvell-cesa.c and s5p-sss.c driver. |
10 | */ | |
11 | ||
12 | #include "rk3288_crypto.h" | |
13 | #include <linux/module.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/clk.h> | |
17 | #include <linux/crypto.h> | |
18 | #include <linux/reset.h> | |
19 | ||
20 | static int rk_crypto_enable_clk(struct rk_crypto_info *dev) | |
21 | { | |
22 | int err; | |
23 | ||
24 | err = clk_prepare_enable(dev->sclk); | |
25 | if (err) { | |
26 | dev_err(dev->dev, "[%s:%d], Couldn't enable clock sclk\n", | |
27 | __func__, __LINE__); | |
28 | goto err_return; | |
29 | } | |
30 | err = clk_prepare_enable(dev->aclk); | |
31 | if (err) { | |
32 | dev_err(dev->dev, "[%s:%d], Couldn't enable clock aclk\n", | |
33 | __func__, __LINE__); | |
34 | goto err_aclk; | |
35 | } | |
36 | err = clk_prepare_enable(dev->hclk); | |
37 | if (err) { | |
38 | dev_err(dev->dev, "[%s:%d], Couldn't enable clock hclk\n", | |
39 | __func__, __LINE__); | |
40 | goto err_hclk; | |
41 | } | |
42 | err = clk_prepare_enable(dev->dmaclk); | |
43 | if (err) { | |
44 | dev_err(dev->dev, "[%s:%d], Couldn't enable clock dmaclk\n", | |
45 | __func__, __LINE__); | |
46 | goto err_dmaclk; | |
47 | } | |
48 | return err; | |
49 | err_dmaclk: | |
50 | clk_disable_unprepare(dev->hclk); | |
51 | err_hclk: | |
52 | clk_disable_unprepare(dev->aclk); | |
53 | err_aclk: | |
54 | clk_disable_unprepare(dev->sclk); | |
55 | err_return: | |
56 | return err; | |
57 | } | |
58 | ||
59 | static void rk_crypto_disable_clk(struct rk_crypto_info *dev) | |
60 | { | |
61 | clk_disable_unprepare(dev->dmaclk); | |
62 | clk_disable_unprepare(dev->hclk); | |
63 | clk_disable_unprepare(dev->aclk); | |
64 | clk_disable_unprepare(dev->sclk); | |
65 | } | |
66 | ||
67 | static int check_alignment(struct scatterlist *sg_src, | |
68 | struct scatterlist *sg_dst, | |
69 | int align_mask) | |
70 | { | |
71 | int in, out, align; | |
72 | ||
73 | in = IS_ALIGNED((uint32_t)sg_src->offset, 4) && | |
74 | IS_ALIGNED((uint32_t)sg_src->length, align_mask); | |
75 | if (!sg_dst) | |
76 | return in; | |
77 | out = IS_ALIGNED((uint32_t)sg_dst->offset, 4) && | |
78 | IS_ALIGNED((uint32_t)sg_dst->length, align_mask); | |
79 | align = in && out; | |
80 | ||
81 | return (align && (sg_src->length == sg_dst->length)); | |
82 | } | |
83 | ||
84 | static int rk_load_data(struct rk_crypto_info *dev, | |
85 | struct scatterlist *sg_src, | |
86 | struct scatterlist *sg_dst) | |
87 | { | |
88 | unsigned int count; | |
89 | ||
90 | dev->aligned = dev->aligned ? | |
91 | check_alignment(sg_src, sg_dst, dev->align_size) : | |
92 | dev->aligned; | |
93 | if (dev->aligned) { | |
94 | count = min(dev->left_bytes, sg_src->length); | |
95 | dev->left_bytes -= count; | |
96 | ||
97 | if (!dma_map_sg(dev->dev, sg_src, 1, DMA_TO_DEVICE)) { | |
98 | dev_err(dev->dev, "[%s:%d] dma_map_sg(src) error\n", | |
99 | __func__, __LINE__); | |
100 | return -EINVAL; | |
101 | } | |
102 | dev->addr_in = sg_dma_address(sg_src); | |
103 | ||
104 | if (sg_dst) { | |
105 | if (!dma_map_sg(dev->dev, sg_dst, 1, DMA_FROM_DEVICE)) { | |
106 | dev_err(dev->dev, | |
107 | "[%s:%d] dma_map_sg(dst) error\n", | |
108 | __func__, __LINE__); | |
109 | dma_unmap_sg(dev->dev, sg_src, 1, | |
110 | DMA_TO_DEVICE); | |
111 | return -EINVAL; | |
112 | } | |
113 | dev->addr_out = sg_dma_address(sg_dst); | |
114 | } | |
115 | } else { | |
116 | count = (dev->left_bytes > PAGE_SIZE) ? | |
117 | PAGE_SIZE : dev->left_bytes; | |
118 | ||
4359669a | 119 | if (!sg_pcopy_to_buffer(dev->first, dev->src_nents, |
433cd2c6 ZW |
120 | dev->addr_vir, count, |
121 | dev->total - dev->left_bytes)) { | |
122 | dev_err(dev->dev, "[%s:%d] pcopy err\n", | |
123 | __func__, __LINE__); | |
124 | return -EINVAL; | |
125 | } | |
126 | dev->left_bytes -= count; | |
127 | sg_init_one(&dev->sg_tmp, dev->addr_vir, count); | |
128 | if (!dma_map_sg(dev->dev, &dev->sg_tmp, 1, DMA_TO_DEVICE)) { | |
129 | dev_err(dev->dev, "[%s:%d] dma_map_sg(sg_tmp) error\n", | |
130 | __func__, __LINE__); | |
131 | return -ENOMEM; | |
132 | } | |
133 | dev->addr_in = sg_dma_address(&dev->sg_tmp); | |
134 | ||
135 | if (sg_dst) { | |
136 | if (!dma_map_sg(dev->dev, &dev->sg_tmp, 1, | |
137 | DMA_FROM_DEVICE)) { | |
138 | dev_err(dev->dev, | |
139 | "[%s:%d] dma_map_sg(sg_tmp) error\n", | |
140 | __func__, __LINE__); | |
141 | dma_unmap_sg(dev->dev, &dev->sg_tmp, 1, | |
142 | DMA_TO_DEVICE); | |
143 | return -ENOMEM; | |
144 | } | |
145 | dev->addr_out = sg_dma_address(&dev->sg_tmp); | |
146 | } | |
147 | } | |
148 | dev->count = count; | |
149 | return 0; | |
150 | } | |
151 | ||
152 | static void rk_unload_data(struct rk_crypto_info *dev) | |
153 | { | |
154 | struct scatterlist *sg_in, *sg_out; | |
155 | ||
156 | sg_in = dev->aligned ? dev->sg_src : &dev->sg_tmp; | |
157 | dma_unmap_sg(dev->dev, sg_in, 1, DMA_TO_DEVICE); | |
158 | ||
159 | if (dev->sg_dst) { | |
160 | sg_out = dev->aligned ? dev->sg_dst : &dev->sg_tmp; | |
161 | dma_unmap_sg(dev->dev, sg_out, 1, DMA_FROM_DEVICE); | |
162 | } | |
163 | } | |
164 | ||
165 | static irqreturn_t rk_crypto_irq_handle(int irq, void *dev_id) | |
166 | { | |
167 | struct rk_crypto_info *dev = platform_get_drvdata(dev_id); | |
168 | u32 interrupt_status; | |
433cd2c6 ZW |
169 | |
170 | spin_lock(&dev->lock); | |
171 | interrupt_status = CRYPTO_READ(dev, RK_CRYPTO_INTSTS); | |
172 | CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, interrupt_status); | |
641eacd1 | 173 | |
433cd2c6 ZW |
174 | if (interrupt_status & 0x0a) { |
175 | dev_warn(dev->dev, "DMA Error\n"); | |
641eacd1 | 176 | dev->err = -EFAULT; |
433cd2c6 | 177 | } |
641eacd1 ZW |
178 | tasklet_schedule(&dev->done_task); |
179 | ||
433cd2c6 ZW |
180 | spin_unlock(&dev->lock); |
181 | return IRQ_HANDLED; | |
182 | } | |
183 | ||
5a7801f6 ZW |
184 | static int rk_crypto_enqueue(struct rk_crypto_info *dev, |
185 | struct crypto_async_request *async_req) | |
186 | { | |
187 | unsigned long flags; | |
188 | int ret; | |
189 | ||
190 | spin_lock_irqsave(&dev->lock, flags); | |
191 | ret = crypto_enqueue_request(&dev->queue, async_req); | |
192 | if (dev->busy) { | |
193 | spin_unlock_irqrestore(&dev->lock, flags); | |
194 | return ret; | |
195 | } | |
196 | dev->busy = true; | |
197 | spin_unlock_irqrestore(&dev->lock, flags); | |
198 | tasklet_schedule(&dev->queue_task); | |
199 | ||
200 | return ret; | |
201 | } | |
202 | ||
641eacd1 | 203 | static void rk_crypto_queue_task_cb(unsigned long data) |
433cd2c6 ZW |
204 | { |
205 | struct rk_crypto_info *dev = (struct rk_crypto_info *)data; | |
5a7801f6 ZW |
206 | struct crypto_async_request *async_req, *backlog; |
207 | unsigned long flags; | |
433cd2c6 ZW |
208 | int err = 0; |
209 | ||
641eacd1 | 210 | dev->err = 0; |
5a7801f6 ZW |
211 | spin_lock_irqsave(&dev->lock, flags); |
212 | backlog = crypto_get_backlog(&dev->queue); | |
213 | async_req = crypto_dequeue_request(&dev->queue); | |
214 | ||
215 | if (!async_req) { | |
216 | dev->busy = false; | |
217 | spin_unlock_irqrestore(&dev->lock, flags); | |
218 | return; | |
219 | } | |
220 | spin_unlock_irqrestore(&dev->lock, flags); | |
221 | ||
222 | if (backlog) { | |
223 | backlog->complete(backlog, -EINPROGRESS); | |
224 | backlog = NULL; | |
225 | } | |
226 | ||
227 | dev->async_req = async_req; | |
433cd2c6 ZW |
228 | err = dev->start(dev); |
229 | if (err) | |
5a7801f6 | 230 | dev->complete(dev->async_req, err); |
433cd2c6 ZW |
231 | } |
232 | ||
641eacd1 ZW |
233 | static void rk_crypto_done_task_cb(unsigned long data) |
234 | { | |
235 | struct rk_crypto_info *dev = (struct rk_crypto_info *)data; | |
236 | ||
237 | if (dev->err) { | |
5a7801f6 | 238 | dev->complete(dev->async_req, dev->err); |
641eacd1 ZW |
239 | return; |
240 | } | |
241 | ||
242 | dev->err = dev->update(dev); | |
243 | if (dev->err) | |
5a7801f6 | 244 | dev->complete(dev->async_req, dev->err); |
641eacd1 ZW |
245 | } |
246 | ||
433cd2c6 ZW |
247 | static struct rk_crypto_tmp *rk_cipher_algs[] = { |
248 | &rk_ecb_aes_alg, | |
249 | &rk_cbc_aes_alg, | |
250 | &rk_ecb_des_alg, | |
251 | &rk_cbc_des_alg, | |
252 | &rk_ecb_des3_ede_alg, | |
253 | &rk_cbc_des3_ede_alg, | |
bfd927ff ZW |
254 | &rk_ahash_sha1, |
255 | &rk_ahash_sha256, | |
256 | &rk_ahash_md5, | |
433cd2c6 ZW |
257 | }; |
258 | ||
259 | static int rk_crypto_register(struct rk_crypto_info *crypto_info) | |
260 | { | |
261 | unsigned int i, k; | |
262 | int err = 0; | |
263 | ||
264 | for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) { | |
265 | rk_cipher_algs[i]->dev = crypto_info; | |
bfd927ff | 266 | if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER) |
ce0183cb AB |
267 | err = crypto_register_skcipher( |
268 | &rk_cipher_algs[i]->alg.skcipher); | |
bfd927ff ZW |
269 | else |
270 | err = crypto_register_ahash( | |
271 | &rk_cipher_algs[i]->alg.hash); | |
433cd2c6 ZW |
272 | if (err) |
273 | goto err_cipher_algs; | |
274 | } | |
275 | return 0; | |
276 | ||
277 | err_cipher_algs: | |
bfd927ff ZW |
278 | for (k = 0; k < i; k++) { |
279 | if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER) | |
ce0183cb | 280 | crypto_unregister_skcipher(&rk_cipher_algs[k]->alg.skcipher); |
bfd927ff ZW |
281 | else |
282 | crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash); | |
283 | } | |
433cd2c6 ZW |
284 | return err; |
285 | } | |
286 | ||
287 | static void rk_crypto_unregister(void) | |
288 | { | |
289 | unsigned int i; | |
290 | ||
bfd927ff ZW |
291 | for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) { |
292 | if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER) | |
ce0183cb | 293 | crypto_unregister_skcipher(&rk_cipher_algs[i]->alg.skcipher); |
bfd927ff ZW |
294 | else |
295 | crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash); | |
296 | } | |
433cd2c6 ZW |
297 | } |
298 | ||
299 | static void rk_crypto_action(void *data) | |
300 | { | |
301 | struct rk_crypto_info *crypto_info = data; | |
302 | ||
303 | reset_control_assert(crypto_info->rst); | |
304 | } | |
305 | ||
306 | static const struct of_device_id crypto_of_id_table[] = { | |
307 | { .compatible = "rockchip,rk3288-crypto" }, | |
308 | {} | |
309 | }; | |
310 | MODULE_DEVICE_TABLE(of, crypto_of_id_table); | |
311 | ||
312 | static int rk_crypto_probe(struct platform_device *pdev) | |
313 | { | |
433cd2c6 ZW |
314 | struct device *dev = &pdev->dev; |
315 | struct rk_crypto_info *crypto_info; | |
316 | int err = 0; | |
317 | ||
318 | crypto_info = devm_kzalloc(&pdev->dev, | |
319 | sizeof(*crypto_info), GFP_KERNEL); | |
320 | if (!crypto_info) { | |
321 | err = -ENOMEM; | |
322 | goto err_crypto; | |
323 | } | |
324 | ||
325 | crypto_info->rst = devm_reset_control_get(dev, "crypto-rst"); | |
326 | if (IS_ERR(crypto_info->rst)) { | |
327 | err = PTR_ERR(crypto_info->rst); | |
328 | goto err_crypto; | |
329 | } | |
330 | ||
331 | reset_control_assert(crypto_info->rst); | |
332 | usleep_range(10, 20); | |
333 | reset_control_deassert(crypto_info->rst); | |
334 | ||
16d56963 SM |
335 | err = devm_add_action_or_reset(dev, rk_crypto_action, crypto_info); |
336 | if (err) | |
433cd2c6 | 337 | goto err_crypto; |
433cd2c6 ZW |
338 | |
339 | spin_lock_init(&crypto_info->lock); | |
340 | ||
72174473 | 341 | crypto_info->reg = devm_platform_ioremap_resource(pdev, 0); |
433cd2c6 ZW |
342 | if (IS_ERR(crypto_info->reg)) { |
343 | err = PTR_ERR(crypto_info->reg); | |
344 | goto err_crypto; | |
345 | } | |
346 | ||
347 | crypto_info->aclk = devm_clk_get(&pdev->dev, "aclk"); | |
348 | if (IS_ERR(crypto_info->aclk)) { | |
349 | err = PTR_ERR(crypto_info->aclk); | |
350 | goto err_crypto; | |
351 | } | |
352 | ||
353 | crypto_info->hclk = devm_clk_get(&pdev->dev, "hclk"); | |
354 | if (IS_ERR(crypto_info->hclk)) { | |
355 | err = PTR_ERR(crypto_info->hclk); | |
356 | goto err_crypto; | |
357 | } | |
358 | ||
359 | crypto_info->sclk = devm_clk_get(&pdev->dev, "sclk"); | |
360 | if (IS_ERR(crypto_info->sclk)) { | |
361 | err = PTR_ERR(crypto_info->sclk); | |
362 | goto err_crypto; | |
363 | } | |
364 | ||
365 | crypto_info->dmaclk = devm_clk_get(&pdev->dev, "apb_pclk"); | |
366 | if (IS_ERR(crypto_info->dmaclk)) { | |
367 | err = PTR_ERR(crypto_info->dmaclk); | |
368 | goto err_crypto; | |
369 | } | |
370 | ||
371 | crypto_info->irq = platform_get_irq(pdev, 0); | |
372 | if (crypto_info->irq < 0) { | |
373 | dev_warn(crypto_info->dev, | |
374 | "control Interrupt is not available.\n"); | |
375 | err = crypto_info->irq; | |
376 | goto err_crypto; | |
377 | } | |
378 | ||
379 | err = devm_request_irq(&pdev->dev, crypto_info->irq, | |
380 | rk_crypto_irq_handle, IRQF_SHARED, | |
381 | "rk-crypto", pdev); | |
382 | ||
383 | if (err) { | |
384 | dev_err(crypto_info->dev, "irq request failed.\n"); | |
385 | goto err_crypto; | |
386 | } | |
387 | ||
388 | crypto_info->dev = &pdev->dev; | |
389 | platform_set_drvdata(pdev, crypto_info); | |
390 | ||
641eacd1 ZW |
391 | tasklet_init(&crypto_info->queue_task, |
392 | rk_crypto_queue_task_cb, (unsigned long)crypto_info); | |
393 | tasklet_init(&crypto_info->done_task, | |
394 | rk_crypto_done_task_cb, (unsigned long)crypto_info); | |
433cd2c6 ZW |
395 | crypto_init_queue(&crypto_info->queue, 50); |
396 | ||
397 | crypto_info->enable_clk = rk_crypto_enable_clk; | |
398 | crypto_info->disable_clk = rk_crypto_disable_clk; | |
399 | crypto_info->load_data = rk_load_data; | |
400 | crypto_info->unload_data = rk_unload_data; | |
5a7801f6 ZW |
401 | crypto_info->enqueue = rk_crypto_enqueue; |
402 | crypto_info->busy = false; | |
433cd2c6 ZW |
403 | |
404 | err = rk_crypto_register(crypto_info); | |
405 | if (err) { | |
406 | dev_err(dev, "err in register alg"); | |
407 | goto err_register_alg; | |
408 | } | |
409 | ||
410 | dev_info(dev, "Crypto Accelerator successfully registered\n"); | |
411 | return 0; | |
412 | ||
413 | err_register_alg: | |
641eacd1 ZW |
414 | tasklet_kill(&crypto_info->queue_task); |
415 | tasklet_kill(&crypto_info->done_task); | |
433cd2c6 ZW |
416 | err_crypto: |
417 | return err; | |
418 | } | |
419 | ||
420 | static int rk_crypto_remove(struct platform_device *pdev) | |
421 | { | |
422 | struct rk_crypto_info *crypto_tmp = platform_get_drvdata(pdev); | |
423 | ||
424 | rk_crypto_unregister(); | |
641eacd1 ZW |
425 | tasklet_kill(&crypto_tmp->done_task); |
426 | tasklet_kill(&crypto_tmp->queue_task); | |
433cd2c6 ZW |
427 | return 0; |
428 | } | |
429 | ||
430 | static struct platform_driver crypto_driver = { | |
431 | .probe = rk_crypto_probe, | |
432 | .remove = rk_crypto_remove, | |
433 | .driver = { | |
434 | .name = "rk3288-crypto", | |
435 | .of_match_table = crypto_of_id_table, | |
436 | }, | |
437 | }; | |
438 | ||
439 | module_platform_driver(crypto_driver); | |
440 | ||
441 | MODULE_AUTHOR("Zain Wang <zain.wang@rock-chips.com>"); | |
442 | MODULE_DESCRIPTION("Support for Rockchip's cryptographic engine"); | |
443 | MODULE_LICENSE("GPL"); |