Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[muen/linux.git] / drivers / crypto / amcc / crypto4xx_core.c
index acf79889d903fede67a7e35706c6956f4c5ac111..06574a884715a2ecaf5026572d294c34fa811496 100644 (file)
 #include <crypto/ctr.h>
 #include <crypto/gcm.h>
 #include <crypto/sha.h>
+#include <crypto/rng.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/skcipher.h>
 #include <crypto/internal/aead.h>
+#include <crypto/internal/rng.h>
 #include <crypto/internal/skcipher.h>
 #include "crypto4xx_reg_def.h"
 #include "crypto4xx_core.h"
@@ -1035,6 +1037,10 @@ static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev,
                        rc = crypto_register_ahash(&alg->alg.u.hash);
                        break;
 
+               case CRYPTO_ALG_TYPE_RNG:
+                       rc = crypto_register_rng(&alg->alg.u.rng);
+                       break;
+
                default:
                        rc = crypto_register_skcipher(&alg->alg.u.cipher);
                        break;
@@ -1064,6 +1070,10 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev)
                        crypto_unregister_aead(&alg->alg.u.aead);
                        break;
 
+               case CRYPTO_ALG_TYPE_RNG:
+                       crypto_unregister_rng(&alg->alg.u.rng);
+                       break;
+
                default:
                        crypto_unregister_skcipher(&alg->alg.u.cipher);
                }
@@ -1122,6 +1132,69 @@ static irqreturn_t crypto4xx_ce_interrupt_handler_revb(int irq, void *data)
                PPC4XX_TMO_ERR_INT);
 }
 
+static int ppc4xx_prng_data_read(struct crypto4xx_device *dev,
+                                u8 *data, unsigned int max)
+{
+       unsigned int i, curr = 0;
+       u32 val[2];
+
+       do {
+               /* trigger PRN generation */
+               writel(PPC4XX_PRNG_CTRL_AUTO_EN,
+                      dev->ce_base + CRYPTO4XX_PRNG_CTRL);
+
+               for (i = 0; i < 1024; i++) {
+                       /* usually 19 iterations are enough */
+                       if ((readl(dev->ce_base + CRYPTO4XX_PRNG_STAT) &
+                            CRYPTO4XX_PRNG_STAT_BUSY))
+                               continue;
+
+                       val[0] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_0);
+                       val[1] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_1);
+                       break;
+               }
+               if (i == 1024)
+                       return -ETIMEDOUT;
+
+               if ((max - curr) >= 8) {
+                       memcpy(data, &val, 8);
+                       data += 8;
+                       curr += 8;
+               } else {
+                       /* copy only remaining bytes */
+                       memcpy(data, &val, max - curr);
+                       break;
+               }
+       } while (curr < max);
+
+       return curr;
+}
+
+static int crypto4xx_prng_generate(struct crypto_rng *tfm,
+                                  const u8 *src, unsigned int slen,
+                                  u8 *dstn, unsigned int dlen)
+{
+       struct rng_alg *alg = crypto_rng_alg(tfm);
+       struct crypto4xx_alg *amcc_alg;
+       struct crypto4xx_device *dev;
+       int ret;
+
+       amcc_alg = container_of(alg, struct crypto4xx_alg, alg.u.rng);
+       dev = amcc_alg->dev;
+
+       mutex_lock(&dev->core_dev->rng_lock);
+       ret = ppc4xx_prng_data_read(dev, dstn, dlen);
+       mutex_unlock(&dev->core_dev->rng_lock);
+       return ret;
+}
+
+
+static int crypto4xx_prng_seed(struct crypto_rng *tfm, const u8 *seed,
+                       unsigned int slen)
+{
+       return 0;
+}
+
 /**
  * Supported Crypto Algorithms
  */
@@ -1291,6 +1364,18 @@ static struct crypto4xx_alg_common crypto4xx_alg[] = {
                        .cra_module     = THIS_MODULE,
                },
        } },
+       { .type = CRYPTO_ALG_TYPE_RNG, .u.rng = {
+               .base = {
+                       .cra_name               = "stdrng",
+                       .cra_driver_name        = "crypto4xx_rng",
+                       .cra_priority           = 300,
+                       .cra_ctxsize            = 0,
+                       .cra_module             = THIS_MODULE,
+               },
+               .generate               = crypto4xx_prng_generate,
+               .seed                   = crypto4xx_prng_seed,
+               .seedsize               = 0,
+       } },
 };
 
 /**
@@ -1360,6 +1445,7 @@ static int crypto4xx_probe(struct platform_device *ofdev)
        core_dev->dev->core_dev = core_dev;
        core_dev->dev->is_revb = is_revb;
        core_dev->device = dev;
+       mutex_init(&core_dev->rng_lock);
        spin_lock_init(&core_dev->lock);
        INIT_LIST_HEAD(&core_dev->dev->alg_list);
        ratelimit_default_init(&core_dev->dev->aead_ratelimit);
@@ -1439,6 +1525,7 @@ static int crypto4xx_remove(struct platform_device *ofdev)
        tasklet_kill(&core_dev->tasklet);
        /* Un-register with Linux CryptoAPI */
        crypto4xx_unregister_alg(core_dev->dev);
+       mutex_destroy(&core_dev->rng_lock);
        /* Free all allocated memory */
        crypto4xx_stop_all(core_dev);