blob: 47552c12e8c69e0b3d78981981a397bac4838fd1 [file] [log] [blame]
Boris BREZILLONf63601f2015-06-18 15:46:20 +02001/*
2 * Support for Marvell's Cryptographic Engine and Security Accelerator (CESA)
3 * that can be found on the following platform: Orion, Kirkwood, Armada. This
4 * driver supports the TDMA engine on platforms on which it is available.
5 *
6 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
7 * Author: Arnaud Ebalard <arno@natisbad.org>
8 *
9 * This work is based on an initial version written by
10 * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License version 2 as published
14 * by the Free Software Foundation.
15 */
16
17#include <linux/delay.h>
18#include <linux/genalloc.h>
19#include <linux/interrupt.h>
20#include <linux/io.h>
21#include <linux/kthread.h>
22#include <linux/mbus.h>
23#include <linux/platform_device.h>
24#include <linux/scatterlist.h>
25#include <linux/slab.h>
26#include <linux/module.h>
27#include <linux/clk.h>
28#include <linux/of.h>
29#include <linux/of_platform.h>
30#include <linux/of_irq.h>
31
32#include "cesa.h"
33
Romain Periere26df732016-06-21 10:08:31 +020034/* Limit of the crypto queue before reaching the backlog */
35#define CESA_CRYPTO_DEFAULT_MAX_QLEN 50
36
Boris BREZILLON64c55d42015-06-18 15:46:27 +020037static int allhwsupport = !IS_ENABLED(CONFIG_CRYPTO_DEV_MV_CESA);
38module_param_named(allhwsupport, allhwsupport, int, 0444);
39MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the mv_cesa driver)");
40
Boris BREZILLONf63601f2015-06-18 15:46:20 +020041struct mv_cesa_dev *cesa_dev;
42
43static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
44{
45 struct crypto_async_request *req, *backlog;
46 struct mv_cesa_ctx *ctx;
47
48 spin_lock_bh(&cesa_dev->lock);
49 backlog = crypto_get_backlog(&cesa_dev->queue);
50 req = crypto_dequeue_request(&cesa_dev->queue);
51 engine->req = req;
52 spin_unlock_bh(&cesa_dev->lock);
53
54 if (!req)
55 return;
56
57 if (backlog)
58 backlog->complete(backlog, -EINPROGRESS);
59
60 ctx = crypto_tfm_ctx(req->tfm);
61 ctx->ops->prepare(req, engine);
62 ctx->ops->step(req);
63}
64
65static irqreturn_t mv_cesa_int(int irq, void *priv)
66{
67 struct mv_cesa_engine *engine = priv;
68 struct crypto_async_request *req;
69 struct mv_cesa_ctx *ctx;
70 u32 status, mask;
71 irqreturn_t ret = IRQ_NONE;
72
73 while (true) {
74 int res;
75
76 mask = mv_cesa_get_int_mask(engine);
77 status = readl(engine->regs + CESA_SA_INT_STATUS);
78
79 if (!(status & mask))
80 break;
81
82 /*
83 * TODO: avoid clearing the FPGA_INT_STATUS if this not
84 * relevant on some platforms.
85 */
86 writel(~status, engine->regs + CESA_SA_FPGA_INT_STATUS);
87 writel(~status, engine->regs + CESA_SA_INT_STATUS);
88
89 ret = IRQ_HANDLED;
90 spin_lock_bh(&engine->lock);
91 req = engine->req;
92 spin_unlock_bh(&engine->lock);
93 if (req) {
94 ctx = crypto_tfm_ctx(req->tfm);
95 res = ctx->ops->process(req, status & mask);
96 if (res != -EINPROGRESS) {
97 spin_lock_bh(&engine->lock);
98 engine->req = NULL;
99 mv_cesa_dequeue_req_unlocked(engine);
100 spin_unlock_bh(&engine->lock);
101 ctx->ops->cleanup(req);
102 local_bh_disable();
103 req->complete(req, res);
104 local_bh_enable();
105 } else {
106 ctx->ops->step(req);
107 }
108 }
109 }
110
111 return ret;
112}
113
114int mv_cesa_queue_req(struct crypto_async_request *req)
115{
116 int ret;
117 int i;
118
119 spin_lock_bh(&cesa_dev->lock);
120 ret = crypto_enqueue_request(&cesa_dev->queue, req);
121 spin_unlock_bh(&cesa_dev->lock);
122
123 if (ret != -EINPROGRESS)
124 return ret;
125
126 for (i = 0; i < cesa_dev->caps->nengines; i++) {
127 spin_lock_bh(&cesa_dev->engines[i].lock);
128 if (!cesa_dev->engines[i].req)
129 mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
130 spin_unlock_bh(&cesa_dev->engines[i].lock);
131 }
132
133 return -EINPROGRESS;
134}
135
136static int mv_cesa_add_algs(struct mv_cesa_dev *cesa)
137{
138 int ret;
139 int i, j;
140
141 for (i = 0; i < cesa->caps->ncipher_algs; i++) {
142 ret = crypto_register_alg(cesa->caps->cipher_algs[i]);
143 if (ret)
144 goto err_unregister_crypto;
145 }
146
147 for (i = 0; i < cesa->caps->nahash_algs; i++) {
148 ret = crypto_register_ahash(cesa->caps->ahash_algs[i]);
149 if (ret)
150 goto err_unregister_ahash;
151 }
152
153 return 0;
154
155err_unregister_ahash:
156 for (j = 0; j < i; j++)
157 crypto_unregister_ahash(cesa->caps->ahash_algs[j]);
158 i = cesa->caps->ncipher_algs;
159
160err_unregister_crypto:
161 for (j = 0; j < i; j++)
162 crypto_unregister_alg(cesa->caps->cipher_algs[j]);
163
164 return ret;
165}
166
167static void mv_cesa_remove_algs(struct mv_cesa_dev *cesa)
168{
169 int i;
170
171 for (i = 0; i < cesa->caps->nahash_algs; i++)
172 crypto_unregister_ahash(cesa->caps->ahash_algs[i]);
173
174 for (i = 0; i < cesa->caps->ncipher_algs; i++)
175 crypto_unregister_alg(cesa->caps->cipher_algs[i]);
176}
177
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200178static struct crypto_alg *orion_cipher_algs[] = {
179 &mv_cesa_ecb_des_alg,
180 &mv_cesa_cbc_des_alg,
181 &mv_cesa_ecb_des3_ede_alg,
182 &mv_cesa_cbc_des3_ede_alg,
183 &mv_cesa_ecb_aes_alg,
184 &mv_cesa_cbc_aes_alg,
185};
186
187static struct ahash_alg *orion_ahash_algs[] = {
188 &mv_md5_alg,
189 &mv_sha1_alg,
190 &mv_ahmac_md5_alg,
191 &mv_ahmac_sha1_alg,
192};
193
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200194static struct crypto_alg *armada_370_cipher_algs[] = {
Boris BREZILLON7b3aaaa2015-06-18 15:46:22 +0200195 &mv_cesa_ecb_des_alg,
196 &mv_cesa_cbc_des_alg,
Arnaud Ebalard4ada4832015-06-18 15:46:23 +0200197 &mv_cesa_ecb_des3_ede_alg,
198 &mv_cesa_cbc_des3_ede_alg,
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200199 &mv_cesa_ecb_aes_alg,
200 &mv_cesa_cbc_aes_alg,
201};
202
203static struct ahash_alg *armada_370_ahash_algs[] = {
Arnaud Ebalard7aeef692015-06-18 15:46:24 +0200204 &mv_md5_alg,
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200205 &mv_sha1_alg,
Arnaud Ebalardf85a7622015-06-18 15:46:25 +0200206 &mv_sha256_alg,
Arnaud Ebalard7aeef692015-06-18 15:46:24 +0200207 &mv_ahmac_md5_alg,
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200208 &mv_ahmac_sha1_alg,
Arnaud Ebalardf85a7622015-06-18 15:46:25 +0200209 &mv_ahmac_sha256_alg,
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200210};
211
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200212static const struct mv_cesa_caps orion_caps = {
213 .nengines = 1,
214 .cipher_algs = orion_cipher_algs,
215 .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
216 .ahash_algs = orion_ahash_algs,
217 .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
218 .has_tdma = false,
219};
220
Arnaud Ebalard72404252015-06-18 15:46:29 +0200221static const struct mv_cesa_caps kirkwood_caps = {
222 .nengines = 1,
223 .cipher_algs = orion_cipher_algs,
224 .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
225 .ahash_algs = orion_ahash_algs,
226 .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
227 .has_tdma = true,
228};
229
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200230static const struct mv_cesa_caps armada_370_caps = {
231 .nengines = 1,
232 .cipher_algs = armada_370_cipher_algs,
233 .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
234 .ahash_algs = armada_370_ahash_algs,
235 .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200236 .has_tdma = true,
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200237};
238
Boris BREZILLON898c9d52015-06-18 15:46:26 +0200239static const struct mv_cesa_caps armada_xp_caps = {
240 .nengines = 2,
241 .cipher_algs = armada_370_cipher_algs,
242 .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
243 .ahash_algs = armada_370_ahash_algs,
244 .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
245 .has_tdma = true,
246};
247
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200248static const struct of_device_id mv_cesa_of_match_table[] = {
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200249 { .compatible = "marvell,orion-crypto", .data = &orion_caps },
Arnaud Ebalard72404252015-06-18 15:46:29 +0200250 { .compatible = "marvell,kirkwood-crypto", .data = &kirkwood_caps },
251 { .compatible = "marvell,dove-crypto", .data = &kirkwood_caps },
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200252 { .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
Boris BREZILLON898c9d52015-06-18 15:46:26 +0200253 { .compatible = "marvell,armada-xp-crypto", .data = &armada_xp_caps },
254 { .compatible = "marvell,armada-375-crypto", .data = &armada_xp_caps },
255 { .compatible = "marvell,armada-38x-crypto", .data = &armada_xp_caps },
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200256 {}
257};
258MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
259
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200260static void
261mv_cesa_conf_mbus_windows(struct mv_cesa_engine *engine,
262 const struct mbus_dram_target_info *dram)
263{
264 void __iomem *iobase = engine->regs;
265 int i;
266
267 for (i = 0; i < 4; i++) {
268 writel(0, iobase + CESA_TDMA_WINDOW_CTRL(i));
269 writel(0, iobase + CESA_TDMA_WINDOW_BASE(i));
270 }
271
272 for (i = 0; i < dram->num_cs; i++) {
273 const struct mbus_dram_window *cs = dram->cs + i;
274
275 writel(((cs->size - 1) & 0xffff0000) |
276 (cs->mbus_attr << 8) |
277 (dram->mbus_dram_target_id << 4) | 1,
278 iobase + CESA_TDMA_WINDOW_CTRL(i));
279 writel(cs->base, iobase + CESA_TDMA_WINDOW_BASE(i));
280 }
281}
282
283static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa)
284{
285 struct device *dev = cesa->dev;
286 struct mv_cesa_dev_dma *dma;
287
288 if (!cesa->caps->has_tdma)
289 return 0;
290
291 dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
292 if (!dma)
293 return -ENOMEM;
294
295 dma->tdma_desc_pool = dmam_pool_create("tdma_desc", dev,
296 sizeof(struct mv_cesa_tdma_desc),
297 16, 0);
298 if (!dma->tdma_desc_pool)
299 return -ENOMEM;
300
301 dma->op_pool = dmam_pool_create("cesa_op", dev,
302 sizeof(struct mv_cesa_op_ctx), 16, 0);
303 if (!dma->op_pool)
304 return -ENOMEM;
305
306 dma->cache_pool = dmam_pool_create("cesa_cache", dev,
307 CESA_MAX_HASH_BLOCK_SIZE, 1, 0);
308 if (!dma->cache_pool)
309 return -ENOMEM;
310
311 dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0);
Boris BREZILLON8a3978a2016-02-05 17:45:48 +0100312 if (!dma->padding_pool)
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200313 return -ENOMEM;
314
315 cesa->dma = dma;
316
317 return 0;
318}
319
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200320static int mv_cesa_get_sram(struct platform_device *pdev, int idx)
321{
322 struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
323 struct mv_cesa_engine *engine = &cesa->engines[idx];
324 const char *res_name = "sram";
325 struct resource *res;
326
Vladimir Zapolskiyabdd4a72015-06-30 15:00:07 -0700327 engine->pool = of_gen_pool_get(cesa->dev->of_node,
328 "marvell,crypto-srams", idx);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200329 if (engine->pool) {
330 engine->sram = gen_pool_dma_alloc(engine->pool,
331 cesa->sram_size,
332 &engine->sram_dma);
333 if (engine->sram)
334 return 0;
335
336 engine->pool = NULL;
337 return -ENOMEM;
338 }
339
340 if (cesa->caps->nengines > 1) {
341 if (!idx)
342 res_name = "sram0";
343 else
344 res_name = "sram1";
345 }
346
347 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
348 res_name);
349 if (!res || resource_size(res) < cesa->sram_size)
350 return -EINVAL;
351
352 engine->sram = devm_ioremap_resource(cesa->dev, res);
353 if (IS_ERR(engine->sram))
354 return PTR_ERR(engine->sram);
355
356 engine->sram_dma = phys_to_dma(cesa->dev,
357 (phys_addr_t)res->start);
358
359 return 0;
360}
361
362static void mv_cesa_put_sram(struct platform_device *pdev, int idx)
363{
364 struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
365 struct mv_cesa_engine *engine = &cesa->engines[idx];
366
367 if (!engine->pool)
368 return;
369
370 gen_pool_free(engine->pool, (unsigned long)engine->sram,
371 cesa->sram_size);
372}
373
374static int mv_cesa_probe(struct platform_device *pdev)
375{
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200376 const struct mv_cesa_caps *caps = &orion_caps;
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200377 const struct mbus_dram_target_info *dram;
378 const struct of_device_id *match;
379 struct device *dev = &pdev->dev;
380 struct mv_cesa_dev *cesa;
381 struct mv_cesa_engine *engines;
382 struct resource *res;
383 int irq, ret, i;
384 u32 sram_size;
385
386 if (cesa_dev) {
387 dev_err(&pdev->dev, "Only one CESA device authorized\n");
388 return -EEXIST;
389 }
390
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200391 if (dev->of_node) {
392 match = of_match_node(mv_cesa_of_match_table, dev->of_node);
393 if (!match || !match->data)
394 return -ENOTSUPP;
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200395
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200396 caps = match->data;
397 }
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200398
Arnaud Ebalard72404252015-06-18 15:46:29 +0200399 if ((caps == &orion_caps || caps == &kirkwood_caps) && !allhwsupport)
Boris BREZILLON0bf69482015-06-18 15:46:28 +0200400 return -ENOTSUPP;
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200401
402 cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
403 if (!cesa)
404 return -ENOMEM;
405
406 cesa->caps = caps;
407 cesa->dev = dev;
408
409 sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
410 of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
411 &sram_size);
412 if (sram_size < CESA_SA_MIN_SRAM_SIZE)
413 sram_size = CESA_SA_MIN_SRAM_SIZE;
414
415 cesa->sram_size = sram_size;
416 cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines),
417 GFP_KERNEL);
418 if (!cesa->engines)
419 return -ENOMEM;
420
421 spin_lock_init(&cesa->lock);
Romain Periere26df732016-06-21 10:08:31 +0200422 crypto_init_queue(&cesa->queue, CESA_CRYPTO_DEFAULT_MAX_QLEN);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200423 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
424 cesa->regs = devm_ioremap_resource(dev, res);
425 if (IS_ERR(cesa->regs))
Boris BREZILLONdfe97ad2016-03-17 10:47:10 +0100426 return PTR_ERR(cesa->regs);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200427
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200428 ret = mv_cesa_dev_dma_init(cesa);
429 if (ret)
430 return ret;
431
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200432 dram = mv_mbus_dram_info_nooverlap();
433
434 platform_set_drvdata(pdev, cesa);
435
436 for (i = 0; i < caps->nengines; i++) {
437 struct mv_cesa_engine *engine = &cesa->engines[i];
438 char res_name[7];
439
440 engine->id = i;
441 spin_lock_init(&engine->lock);
442
443 ret = mv_cesa_get_sram(pdev, i);
444 if (ret)
445 goto err_cleanup;
446
447 irq = platform_get_irq(pdev, i);
448 if (irq < 0) {
449 ret = irq;
450 goto err_cleanup;
451 }
452
453 /*
454 * Not all platforms can gate the CESA clocks: do not complain
455 * if the clock does not exist.
456 */
457 snprintf(res_name, sizeof(res_name), "cesa%d", i);
458 engine->clk = devm_clk_get(dev, res_name);
459 if (IS_ERR(engine->clk)) {
460 engine->clk = devm_clk_get(dev, NULL);
461 if (IS_ERR(engine->clk))
462 engine->clk = NULL;
463 }
464
465 snprintf(res_name, sizeof(res_name), "cesaz%d", i);
466 engine->zclk = devm_clk_get(dev, res_name);
467 if (IS_ERR(engine->zclk))
468 engine->zclk = NULL;
469
470 ret = clk_prepare_enable(engine->clk);
471 if (ret)
472 goto err_cleanup;
473
474 ret = clk_prepare_enable(engine->zclk);
475 if (ret)
476 goto err_cleanup;
477
478 engine->regs = cesa->regs + CESA_ENGINE_OFF(i);
479
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200480 if (dram && cesa->caps->has_tdma)
Romain Perier21ec7572016-04-19 17:09:20 +0200481 mv_cesa_conf_mbus_windows(engine, dram);
Boris BREZILLONdb509a42015-06-18 15:46:21 +0200482
Romain Perier21ec7572016-04-19 17:09:20 +0200483 writel(0, engine->regs + CESA_SA_INT_STATUS);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200484 writel(CESA_SA_CFG_STOP_DIG_ERR,
Romain Perier21ec7572016-04-19 17:09:20 +0200485 engine->regs + CESA_SA_CFG);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200486 writel(engine->sram_dma & CESA_SA_SRAM_MSK,
Romain Perier21ec7572016-04-19 17:09:20 +0200487 engine->regs + CESA_SA_DESC_P0);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200488
489 ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
490 IRQF_ONESHOT,
491 dev_name(&pdev->dev),
Romain Perier21ec7572016-04-19 17:09:20 +0200492 engine);
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200493 if (ret)
494 goto err_cleanup;
495 }
496
497 cesa_dev = cesa;
498
499 ret = mv_cesa_add_algs(cesa);
500 if (ret) {
501 cesa_dev = NULL;
502 goto err_cleanup;
503 }
504
505 dev_info(dev, "CESA device successfully registered\n");
506
507 return 0;
508
509err_cleanup:
510 for (i = 0; i < caps->nengines; i++) {
511 clk_disable_unprepare(cesa->engines[i].zclk);
512 clk_disable_unprepare(cesa->engines[i].clk);
513 mv_cesa_put_sram(pdev, i);
514 }
515
516 return ret;
517}
518
519static int mv_cesa_remove(struct platform_device *pdev)
520{
521 struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
522 int i;
523
524 mv_cesa_remove_algs(cesa);
525
526 for (i = 0; i < cesa->caps->nengines; i++) {
527 clk_disable_unprepare(cesa->engines[i].zclk);
528 clk_disable_unprepare(cesa->engines[i].clk);
529 mv_cesa_put_sram(pdev, i);
530 }
531
532 return 0;
533}
534
535static struct platform_driver marvell_cesa = {
536 .probe = mv_cesa_probe,
537 .remove = mv_cesa_remove,
538 .driver = {
Boris BREZILLONf63601f2015-06-18 15:46:20 +0200539 .name = "marvell-cesa",
540 .of_match_table = mv_cesa_of_match_table,
541 },
542};
543module_platform_driver(marvell_cesa);
544
545MODULE_ALIAS("platform:mv_crypto");
546MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
547MODULE_AUTHOR("Arnaud Ebalard <arno@natisbad.org>");
548MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
549MODULE_LICENSE("GPL v2");