]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/crypto/omap-sha1-md5.c
Merge branch 'omap-fixes'
[linux-2.6-omap-h63xx.git] / drivers / crypto / omap-sha1-md5.c
1 /*
2  * Cryptographic API.
3  *
4  * Support for OMAP SHA1/MD5 HW acceleration.
5  *
6  * Copyright (c) 2007 Instituto Nokia de Tecnologia - INdT
7  * Author: David Cohen <david.cohen@indt.org.br>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as published
11  * by the Free Software Foundation.
12  *
13  * This driver is based on padlock-sha.c driver.
14  */
15
16 #include <asm/arch-omap/irqs.h>
17 #include <crypto/algapi.h>
18 #include <crypto/sha.h>
19 #include <linux/err.h>
20 #include <linux/device.h>
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/errno.h>
24 #include <linux/cryptohash.h>
25 #include <linux/interrupt.h>
26 #include <linux/kernel.h>
27 #include <linux/clk.h>
28 #include <linux/irq.h>
29 #include <linux/platform_device.h>
30 #include <linux/scatterlist.h>
31
32 #define SHA_REG_DIGEST(x)               (0x00 + ((x) * 0x04))
33 #define SHA_REG_DIN(x)                  (0x1C + ((x) * 0x04))
34
35 #define SHA1_MD5_BLOCK_SIZE             SHA1_BLOCK_SIZE
36 #define MD5_DIGEST_SIZE                 16
37
38 #define SHA_REG_DIGCNT                  0x14
39
40 #define SHA_REG_CTRL                    0x18
41 #define SHA_REG_CTRL_LENGTH             (0xFFFFFFFF << 5)
42 #define SHA_REG_CTRL_CLOSE_HASH         (1 << 4)
43 #define SHA_REG_CTRL_ALGO_CONST         (1 << 3)
44 #define SHA_REG_CTRL_ALGO               (1 << 2)
45 #define SHA_REG_CTRL_INPUT_READY        (1 << 1)
46 #define SHA_REG_CTRL_OUTPUT_READY       (1 << 0)
47
48 #define SHA_REG_REV                     0x5C
49 #define SHA_REG_REV_MAJOR               0xF0
50 #define SHA_REG_REV_MINOR               0x0F
51
52 #define SHA_REG_MASK                    0x60
53 #define SHA_REG_MASK_DMA_EN             (1 << 3)
54 #define SHA_REG_MASK_IT_EN              (1 << 2)
55 #define SHA_REG_MASK_SOFTRESET          (1 << 1)
56 #define SHA_REG_AUTOIDLE                (1 << 0)
57
58 #define SHA_REG_SYSSTATUS               0x64
59 #define SHA_REG_SYSSTATUS_RESETDONE     (1 << 0)
60
61 #define DRIVER_NAME                     "OMAP SHA1/MD5"
62
63 struct omap_sha1_md5_ctx {
64         unsigned int            type_algo;
65         unsigned int            bufcnt;
66         unsigned int            digcnt;
67         int                     algo_const;
68         int                     bypass;
69         int                     digsize;
70         u8                      hash[SHA1_DIGEST_SIZE];
71         u8                      buffer[SHA1_BLOCK_SIZE];
72         struct                  hash_desc fallback;
73 };
74
75 struct omap_sha1_md5_dev {
76         unsigned long           base_address;
77         int                     irq;
78         int                     digready;
79         struct clk              *sha1_ick;
80         struct omap_sha1_md5_ctx
81                                 *hw_ctx;
82         struct device           *dev;
83         wait_queue_head_t       wq;
84 };
85
86 static struct omap_sha1_md5_dev *sha1_md5_data;
87
88 #define SHA_REG_IOADDR(d, x) (void *)IO_ADDRESS((d)->base_address + (x))
89
90 static u32 omap_sha1_md5_read(struct omap_sha1_md5_dev *data, u32 offset)
91 {
92         return __raw_readl(SHA_REG_IOADDR(data, offset));
93 }
94
95 static void omap_sha1_md5_write(struct omap_sha1_md5_dev *data,
96                                         u32 value, u32 offset)
97 {
98         __raw_writel(value, SHA_REG_IOADDR(data, offset));
99 }
100
101 static void omap_sha1_md5_write_mask(struct omap_sha1_md5_dev *data,
102                                         u32 value, u32 mask, u32 address)
103 {
104         u32 val;
105
106         val = omap_sha1_md5_read(data, address);
107         val &= ~mask;
108         val |= value;
109         omap_sha1_md5_write(data, val, address);
110 }
111
112 static inline void omap_sha1_md5_enable_clk(struct crypto_tfm *tfm)
113 {
114         struct omap_sha1_md5_dev *data = sha1_md5_data;
115
116         clk_enable(data->sha1_ick);
117 }
118
119 static inline void omap_sha1_md5_disable_clk(struct crypto_tfm *tfm)
120 {
121         struct omap_sha1_md5_dev *data = sha1_md5_data;
122
123         clk_disable(data->sha1_ick);
124 }
125
126 static void omap_sha1_md5_copy_hash(struct crypto_tfm *tfm)
127 {
128         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
129         struct omap_sha1_md5_dev *data = sha1_md5_data;
130
131         u32 *hash = (u32 *)ctx->hash;
132
133         if (ctx->type_algo) {
134                 /* SHA1 results are in big endian */
135                 hash[0] = be32_to_cpu(
136                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(0)));
137                 hash[1] = be32_to_cpu(
138                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(1)));
139                 hash[2] = be32_to_cpu(
140                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(2)));
141                 hash[3] = be32_to_cpu(
142                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(3)));
143                 hash[4] = be32_to_cpu(
144                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(4)));
145         } else {
146                 /* MD5 results are in little endian */
147                 hash[0] = le32_to_cpu(
148                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(0)));
149                 hash[1] = le32_to_cpu(
150                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(1)));
151                 hash[2] = le32_to_cpu(
152                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(2)));
153                 hash[3] = le32_to_cpu(
154                                 omap_sha1_md5_read(data, SHA_REG_DIGEST(3)));
155         }
156 }
157
158 static void omap_sha1_md5_bypass(struct crypto_tfm *tfm,
159                                 u8 *data, unsigned int length)
160 {
161         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
162
163         if (unlikely(!ctx->bypass))
164                 return;
165
166         if (ctx->bypass == 1) {
167                 crypto_hash_init(&ctx->fallback);
168                 ctx->bypass++;
169         }
170
171         if (length) {
172                 struct scatterlist sg;
173
174                 sg_set_buf(&sg, data, length);
175                 crypto_hash_update(&ctx->fallback, &sg, sg.length);
176         }
177 }
178
179 static void omap_sha1_md5_digest_buffer(struct crypto_tfm *tfm,
180                                 u8 *buf, unsigned int len, int close_hash)
181 {
182         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
183         struct omap_sha1_md5_dev *data = sha1_md5_data;
184         unsigned int algo_const = 0;
185         int c;
186         u32 *buffer = (u32 *)buf;
187
188         if (unlikely(ctx->bypass)) {
189                 omap_sha1_md5_bypass(tfm, buf, len);
190                 return;
191         }
192
193         if (unlikely(ctx->algo_const)) {
194                 algo_const = SHA_REG_CTRL_ALGO_CONST;
195                 ctx->algo_const = 0;
196         } else
197                 omap_sha1_md5_write(data, ctx->digcnt, SHA_REG_DIGCNT);
198
199         if (unlikely(close_hash))
200                 close_hash = SHA_REG_CTRL_CLOSE_HASH;
201
202         /* Setting ALGO_CONST only for the first iteration
203          * and CLOSE_HASH only for the last one. */
204         omap_sha1_md5_write_mask(data,
205                         ctx->type_algo | algo_const | close_hash | (len << 5),
206                         SHA_REG_CTRL_ALGO_CONST | SHA_REG_CTRL_CLOSE_HASH |
207                         SHA_REG_CTRL_ALGO | SHA_REG_CTRL_LENGTH,
208                         SHA_REG_CTRL);
209
210         ctx->digcnt += len;
211         while (!(omap_sha1_md5_read(data, SHA_REG_CTRL)
212                 & SHA_REG_CTRL_INPUT_READY));
213
214         if (len % 4)
215                 len = (len/4) + 1;
216         else
217                 len /= 4;
218         for (c = 0; c < len; c++)
219                 omap_sha1_md5_write(data, buffer[c], SHA_REG_DIN(c));
220 }
221
222 static void omap_sha1_md5_append_buffer(struct crypto_tfm *tfm,
223                                 const uint8_t *data, unsigned int length)
224 {
225         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
226
227         BUG_ON((ctx->bufcnt + length) > SHA1_MD5_BLOCK_SIZE);
228
229         memcpy(&ctx->buffer[ctx->bufcnt], data, length);
230         ctx->bufcnt += length;
231 }
232
233 static void omap_sha1_md5_dia_update(struct crypto_tfm *tfm,
234                                 const uint8_t *data, unsigned int length)
235 {
236         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
237
238         /* We need to save the last buffer <= 64 to digest it with
239          * CLOSE_HASH = 1 */
240         if (ctx->bufcnt && ((ctx->bufcnt + length) > SHA1_MD5_BLOCK_SIZE)) {
241                 unsigned int c = SHA1_MD5_BLOCK_SIZE - ctx->bufcnt;
242
243                 omap_sha1_md5_append_buffer(tfm, data, c);
244                 data += c;
245                 length -= c;
246                 if (length) {
247                         ctx->bufcnt = 0;
248                         omap_sha1_md5_digest_buffer(tfm, ctx->buffer,
249                                         SHA1_MD5_BLOCK_SIZE, 0);
250                 }
251         }
252
253         while (length > SHA1_MD5_BLOCK_SIZE) {
254                 /* Revisit: use DMA here */
255                 omap_sha1_md5_digest_buffer(tfm, (u8 *)data,
256                                 SHA1_MD5_BLOCK_SIZE, 0);
257                 length -= SHA1_MD5_BLOCK_SIZE;
258                 data += SHA1_MD5_BLOCK_SIZE;
259         }
260
261         if (length)
262                 omap_sha1_md5_append_buffer(tfm, data, length);
263 }
264
265 static void omap_sha1_md5_start_reset(struct crypto_tfm *tfm)
266 {
267         struct omap_sha1_md5_dev *data = sha1_md5_data;
268
269         omap_sha1_md5_write_mask(data, SHA_REG_MASK_SOFTRESET,
270                         SHA_REG_MASK_SOFTRESET, SHA_REG_MASK);
271 }
272
273 static void omap_sha1_md5_wait_reset(struct crypto_tfm *tfm)
274 {
275         struct omap_sha1_md5_dev *data = sha1_md5_data;
276
277         while (!(omap_sha1_md5_read(data, SHA_REG_SYSSTATUS)
278                         & SHA_REG_SYSSTATUS_RESETDONE));
279 }
280
281 static void omap_sha1_md5_dia_init(struct crypto_tfm *tfm)
282 {
283         struct omap_sha1_md5_dev *data = sha1_md5_data;
284         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
285
286         if (unlikely(data->hw_ctx))
287                 ctx->bypass = 1;
288         else {
289                 data->hw_ctx = ctx;
290                 ctx->bypass = 0;
291                 omap_sha1_md5_enable_clk(tfm);
292                 omap_sha1_md5_start_reset(tfm);
293                 data->digready = 0;
294         }
295
296         if (ctx->bypass) {
297                 omap_sha1_md5_bypass(tfm, NULL, 0);
298                 return;
299         }
300
301         ctx->algo_const = 1;
302         ctx->bufcnt = 0;
303         ctx->digcnt = 0;
304
305         omap_sha1_md5_wait_reset(tfm);
306         omap_sha1_md5_write_mask(data, SHA_REG_MASK_IT_EN,
307                 SHA_REG_MASK_DMA_EN | SHA_REG_MASK_IT_EN, SHA_REG_MASK);
308 }
309
310 static void omap_sha1_md5_dia_final(struct crypto_tfm *tfm, uint8_t *out)
311 {
312         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
313         struct omap_sha1_md5_dev *data = sha1_md5_data;
314         int digsize = ctx->digsize;
315
316         /* The buffer should be >= 9 */
317         if (((ctx->digcnt + ctx->bufcnt) < 9) && !ctx->bypass)
318                 ctx->bypass = 1;
319
320         omap_sha1_md5_digest_buffer(tfm, ctx->buffer, ctx->bufcnt, 1);
321
322         if (unlikely(ctx->bypass)) {
323                 crypto_hash_final(&ctx->fallback, out);
324                 ctx->bypass = 0;
325                 goto bypass;
326         } else
327                 data->digready = 1;
328
329         wait_event_interruptible(data->wq, (data->digready == 2));
330         omap_sha1_md5_copy_hash(tfm);
331
332         memcpy(out, ctx->hash, digsize);
333
334 bypass:
335         if (data->hw_ctx == ctx) {
336                 omap_sha1_md5_disable_clk(tfm);
337                 data->hw_ctx = NULL;
338         }
339 }
340
341 static irqreturn_t omap_sha1_md5_irq(int irq, void *dev_id)
342 {
343         struct omap_sha1_md5_dev *data = dev_id;
344
345         omap_sha1_md5_write_mask(data, SHA_REG_CTRL_OUTPUT_READY,
346                         SHA_REG_CTRL_OUTPUT_READY, SHA_REG_CTRL);
347
348         if (likely(!data->digready))
349                 return IRQ_HANDLED;
350
351         if (data->hw_ctx == NULL) {
352                 dev_err(data->dev, "unknown interrupt.\n");
353                 return IRQ_HANDLED;
354         }
355
356         data->digready = 2;
357         wake_up_interruptible(&data->wq);
358
359         return IRQ_HANDLED;
360 }
361
362 static int omap_sha1_md5_cra_init(struct crypto_tfm *tfm)
363 {
364         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
365         struct omap_sha1_md5_dev *data = sha1_md5_data;
366         const char *fallback_driver_name = tfm->__crt_alg->cra_name;
367         struct crypto_hash *fallback_tfm;
368
369         /* Allocate a fallback and abort if it failed. */
370         fallback_tfm = crypto_alloc_hash(fallback_driver_name, 0,
371                                          CRYPTO_ALG_ASYNC |
372                                          CRYPTO_ALG_NEED_FALLBACK);
373         if (IS_ERR(fallback_tfm)) {
374                 dev_err(data->dev, "fallback driver '%s' could not be"
375                                 "loaded.\n", fallback_driver_name);
376                 return PTR_ERR(fallback_tfm);
377         }
378
379         ctx->fallback.tfm = fallback_tfm;
380
381         return 0;
382 }
383
384 static int omap_sha1_cra_init(struct crypto_tfm *tfm)
385 {
386         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
387
388         ctx->type_algo = SHA_REG_CTRL_ALGO;
389         ctx->digsize = SHA1_DIGEST_SIZE;
390
391         return omap_sha1_md5_cra_init(tfm);
392 }
393
394 static int omap_md5_cra_init(struct crypto_tfm *tfm)
395 {
396         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
397
398         ctx->type_algo = 0;
399         ctx->digsize = MD5_DIGEST_SIZE;
400
401         return omap_sha1_md5_cra_init(tfm);
402 }
403
404 static void omap_sha1_md5_cra_exit(struct crypto_tfm *tfm)
405 {
406         struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
407
408         crypto_free_hash(ctx->fallback.tfm);
409         ctx->fallback.tfm = NULL;
410 }
411
412 static struct crypto_alg omap_sha1_alg = {
413         .cra_name               =       "sha1",
414         .cra_driver_name        =       "omap-sha1",
415         .cra_flags              =       CRYPTO_ALG_TYPE_DIGEST |
416                                         CRYPTO_ALG_NEED_FALLBACK,
417         .cra_blocksize          =       SHA1_MD5_BLOCK_SIZE,
418         .cra_ctxsize            =       sizeof(struct omap_sha1_md5_ctx),
419         .cra_module             =       THIS_MODULE,
420         .cra_list               =       LIST_HEAD_INIT(omap_sha1_alg.cra_list),
421         .cra_init               =       omap_sha1_cra_init,
422         .cra_exit               =       omap_sha1_md5_cra_exit,
423         .cra_u                  =       {
424                 .digest = {
425                         .dia_digestsize =       SHA1_DIGEST_SIZE,
426                         .dia_init       =       omap_sha1_md5_dia_init,
427                         .dia_update     =       omap_sha1_md5_dia_update,
428                         .dia_final      =       omap_sha1_md5_dia_final,
429                 }
430         }
431 };
432
433 static struct crypto_alg omap_md5_alg = {
434         .cra_name               =       "md5",
435         .cra_driver_name        =       "omap-md5",
436         .cra_flags              =       CRYPTO_ALG_TYPE_DIGEST |
437                                         CRYPTO_ALG_NEED_FALLBACK,
438         .cra_blocksize          =       SHA1_MD5_BLOCK_SIZE,
439         .cra_ctxsize            =       sizeof(struct omap_sha1_md5_ctx),
440         .cra_module             =       THIS_MODULE,
441         .cra_list               =       LIST_HEAD_INIT(omap_md5_alg.cra_list),
442         .cra_init               =       omap_md5_cra_init,
443         .cra_exit               =       omap_sha1_md5_cra_exit,
444         .cra_u                  =       {
445                 .digest = {
446                         .dia_digestsize =       MD5_DIGEST_SIZE,
447                         .dia_init       =       omap_sha1_md5_dia_init,
448                         .dia_update     =       omap_sha1_md5_dia_update,
449                         .dia_final      =       omap_sha1_md5_dia_final,
450                 }
451         }
452 };
453
454 static int omap_sha1_md5_probe(struct platform_device *pdev)
455 {
456         struct omap_sha1_md5_dev *data;
457         struct device *dev = &pdev->dev;
458         struct resource *res;
459         int rc;
460
461         rc = crypto_register_alg(&omap_sha1_alg);
462         if (rc)
463                 goto sha1_err;
464         rc = crypto_register_alg(&omap_md5_alg);
465         if (rc)
466                 goto md5_err;
467
468         data = kzalloc(sizeof(struct omap_sha1_md5_dev), GFP_KERNEL);
469         if (data == NULL) {
470                 dev_err(dev, "unable to alloc data struct.\n");
471                 goto data_err;
472         }
473         platform_set_drvdata(pdev, data);
474         data->dev = dev;
475
476         /* Get the base address */
477         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
478         if (!res) {
479                 dev_err(dev, "invalid resource type\n");
480                 rc = -ENODEV;
481                 goto res_err;
482         }
483         data->base_address = res->start;
484
485         /* Set the private data */
486         sha1_md5_data = data;
487
488         /* Get the IRQ */
489         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
490         if (!res) {
491                 dev_err(dev, "invalid resource type\n");
492                 rc = -ENODEV;
493                 goto res_err;
494         }
495         data->irq = res->start;
496
497         rc = request_irq(res->start, omap_sha1_md5_irq,
498                         IRQF_TRIGGER_LOW, DRIVER_NAME, data);
499         if (rc) {
500                 dev_err(dev, "unable to request irq.\n");
501                 goto res_err;
502         }
503
504         /* Initializing the clock */
505         data->sha1_ick = clk_get(0, "sha_ick");
506         if (!data->sha1_ick) {
507                 dev_err(dev, "clock intialization failed.\n");
508                 rc = -ENODEV;
509                 goto clk_err;
510         }
511
512         init_waitqueue_head(&data->wq);
513
514         dev_info(dev, "hw accel on OMAP rev %u.%u\n",
515                 (omap_sha1_md5_read(data, SHA_REG_REV) & SHA_REG_REV_MAJOR)>>4,
516                 omap_sha1_md5_read(data, SHA_REG_REV) & SHA_REG_REV_MINOR);
517
518         return 0;
519
520 clk_err:
521         free_irq(data->irq, data);
522 res_err:
523         kfree(data);
524 data_err:
525         crypto_unregister_alg(&omap_md5_alg);
526 md5_err:
527         crypto_unregister_alg(&omap_sha1_alg);
528 sha1_err:
529         dev_err(dev, "initialization failed.\n");
530         return rc;
531 }
532
533 static int omap_sha1_md5_remove(struct platform_device *pdev)
534 {
535         struct omap_sha1_md5_dev *data = platform_get_drvdata(pdev);
536
537         free_irq(data->irq, data);
538         kfree(data);
539         crypto_unregister_alg(&omap_sha1_alg);
540         crypto_unregister_alg(&omap_md5_alg);
541
542         return 0;
543 }
544
545 static struct platform_driver omap_sha1_md5_driver = {
546         .probe  = omap_sha1_md5_probe,
547         .remove = omap_sha1_md5_remove,
548         .driver = {
549                 .name   = DRIVER_NAME,
550                 .owner  = THIS_MODULE,
551         },
552 };
553
554 static int __init omap_sha1_md5_init(void)
555 {
556         int ret;
557
558         ret = platform_driver_register(&omap_sha1_md5_driver);
559         if (ret)
560                 return ret;
561
562         return 0;
563 }
564
565 static void __exit omap_sha1_md5_exit(void)
566 {
567         platform_driver_unregister(&omap_sha1_md5_driver);
568 }
569
570 module_init(omap_sha1_md5_init);
571 module_exit(omap_sha1_md5_exit);
572
573 MODULE_DESCRIPTION("OMAP SHA1/MD5 hw acceleration support.");
574 MODULE_LICENSE("GPL");
575 MODULE_AUTHOR("David Cohen");