Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  jh7110-trng.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * TRNG driver for the StarFive JH7110 SoC
 *
 * Copyright (C) 2022 StarFive Technology Co.
 */


#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/reset.h>

/* trng register offset */
#define STARFIVE_CTRL   0x00
#define STARFIVE_STAT   0x04
#define STARFIVE_MODE   0x08
#define STARFIVE_SMODE   0x0C
#define STARFIVE_IE   0x10
#define STARFIVE_ISTAT   0x14
#define STARFIVE_RAND0   0x20
#define STARFIVE_RAND1   0x24
#define STARFIVE_RAND2   0x28
#define STARFIVE_RAND3   0x2C
#define STARFIVE_RAND4   0x30
#define STARFIVE_RAND5   0x34
#define STARFIVE_RAND6   0x38
#define STARFIVE_RAND7   0x3C
#define STARFIVE_AUTO_RQSTS  0x60
#define STARFIVE_AUTO_AGE  0x64

/* CTRL CMD */
#define STARFIVE_CTRL_EXEC_NOP  0x0
#define STARFIVE_CTRL_GENE_RANDNUM 0x1
#define STARFIVE_CTRL_EXEC_RANDRESEED 0x2

/* STAT */
#define STARFIVE_STAT_NONCE_MODE BIT(2)
#define STARFIVE_STAT_R256  BIT(3)
#define STARFIVE_STAT_MISSION_MODE BIT(8)
#define STARFIVE_STAT_SEEDED  BIT(9)
#define STARFIVE_STAT_LAST_RESEED(x) ((x) << 16)
#define STARFIVE_STAT_SRVC_RQST  BIT(27)
#define STARFIVE_STAT_RAND_GENERATING BIT(30)
#define STARFIVE_STAT_RAND_SEEDING BIT(31)

/* MODE */
#define STARFIVE_MODE_R256  BIT(3)

/* SMODE */
#define STARFIVE_SMODE_NONCE_MODE BIT(2)
#define STARFIVE_SMODE_MISSION_MODE BIT(8)
#define STARFIVE_SMODE_MAX_REJECTS(x) ((x) << 16)

/* IE */
#define STARFIVE_IE_RAND_RDY_EN  BIT(0)
#define STARFIVE_IE_SEED_DONE_EN BIT(1)
#define STARFIVE_IE_LFSR_LOCKUP_EN BIT(4)
#define STARFIVE_IE_GLBL_EN  BIT(31)

#define STARFIVE_IE_ALL   (STARFIVE_IE_GLBL_EN | \
      STARFIVE_IE_RAND_RDY_EN | \
      STARFIVE_IE_SEED_DONE_EN | \
      STARFIVE_IE_LFSR_LOCKUP_EN)

/* ISTAT */
#define STARFIVE_ISTAT_RAND_RDY  BIT(0)
#define STARFIVE_ISTAT_SEED_DONE BIT(1)
#define STARFIVE_ISTAT_LFSR_LOCKUP BIT(4)

#define STARFIVE_RAND_LEN  sizeof(u32)

#define to_trng(p)   container_of(p, struct starfive_trng, rng)

enum reseed {
 RANDOM_RESEED,
 NONCE_RESEED,
};

enum mode {
 PRNG_128BIT,
 PRNG_256BIT,
};

struct starfive_trng {
 struct device  *dev;
 void __iomem  *base;
 struct clk  *hclk;
 struct clk  *ahb;
 struct reset_control *rst;
 struct hwrng  rng;
 struct completion random_done;
 struct completion reseed_done;
 u32   mode;
 u32   mission;
 u32   reseed;
 /* protects against concurrent write to ctrl register */
 spinlock_t  write_lock;
};

static u16 autoreq;
module_param(autoreq, ushort, 0);
MODULE_PARM_DESC(autoreq, "Auto-reseeding after random number requests by host reaches specified counter:\n"
    " 0 - disable counter\n"
    " other - reload value for internal counter");

static u16 autoage;
module_param(autoage, ushort, 0);
MODULE_PARM_DESC(autoage, "Auto-reseeding after specified timer countdowns to 0:\n"
    " 0 - disable timer\n"
    " other - reload value for internal timer");

static inline int starfive_trng_wait_idle(struct starfive_trng *trng)
{
 u32 stat;

 return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
       !(stat & (STARFIVE_STAT_RAND_GENERATING |
          STARFIVE_STAT_RAND_SEEDING)),
       10, 100000);
}

static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)
{
 /* clear register: ISTAT */
 u32 data = readl(trng->base + STARFIVE_ISTAT);

 writel(data, trng->base + STARFIVE_ISTAT);
}

static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, bool wait)
{
 int wait_time = 1000;

 /* allow up to 40 us for wait == 0 */
 if (!wait)
  wait_time = 40;

 switch (cmd) {
 case STARFIVE_CTRL_GENE_RANDNUM:
  reinit_completion(&trng->random_done);
  spin_lock_irq(&trng->write_lock);
  writel(cmd, trng->base + STARFIVE_CTRL);
  spin_unlock_irq(&trng->write_lock);
  if (!wait_for_completion_timeout(&trng->random_done, usecs_to_jiffies(wait_time)))
   return -ETIMEDOUT;
  break;
 case STARFIVE_CTRL_EXEC_RANDRESEED:
  reinit_completion(&trng->reseed_done);
  spin_lock_irq(&trng->write_lock);
  writel(cmd, trng->base + STARFIVE_CTRL);
  spin_unlock_irq(&trng->write_lock);
  if (!wait_for_completion_timeout(&trng->reseed_done, usecs_to_jiffies(wait_time)))
   return -ETIMEDOUT;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static int starfive_trng_init(struct hwrng *rng)
{
 struct starfive_trng *trng = to_trng(rng);
 u32 mode, intr = 0;

 /* setup Auto Request/Age register */
 writel(autoage, trng->base + STARFIVE_AUTO_AGE);
 writel(autoreq, trng->base + STARFIVE_AUTO_RQSTS);

 /* clear register: ISTAT */
 starfive_trng_irq_mask_clear(trng);

 intr |= STARFIVE_IE_ALL;
 writel(intr, trng->base + STARFIVE_IE);

 mode  = readl(trng->base + STARFIVE_MODE);

 switch (trng->mode) {
 case PRNG_128BIT:
  mode &= ~STARFIVE_MODE_R256;
  break;
 case PRNG_256BIT:
  mode |= STARFIVE_MODE_R256;
  break;
 default:
  mode |= STARFIVE_MODE_R256;
  break;
 }

 writel(mode, trng->base + STARFIVE_MODE);

 return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);
}

static irqreturn_t starfive_trng_irq(int irq, void *priv)
{
 u32 status;
 struct starfive_trng *trng = (struct starfive_trng *)priv;

 status = readl(trng->base + STARFIVE_ISTAT);
 if (status & STARFIVE_ISTAT_RAND_RDY) {
  writel(STARFIVE_ISTAT_RAND_RDY, trng->base + STARFIVE_ISTAT);
  complete(&trng->random_done);
 }

 if (status & STARFIVE_ISTAT_SEED_DONE) {
  writel(STARFIVE_ISTAT_SEED_DONE, trng->base + STARFIVE_ISTAT);
  complete(&trng->reseed_done);
 }

 if (status & STARFIVE_ISTAT_LFSR_LOCKUP) {
  writel(STARFIVE_ISTAT_LFSR_LOCKUP, trng->base + STARFIVE_ISTAT);
  /* SEU occurred, reseeding required*/
  spin_lock(&trng->write_lock);
  writel(STARFIVE_CTRL_EXEC_RANDRESEED, trng->base + STARFIVE_CTRL);
  spin_unlock(&trng->write_lock);
 }

 return IRQ_HANDLED;
}

static void starfive_trng_cleanup(struct hwrng *rng)
{
 struct starfive_trng *trng = to_trng(rng);

 writel(0, trng->base + STARFIVE_CTRL);

 reset_control_assert(trng->rst);
 clk_disable_unprepare(trng->hclk);
 clk_disable_unprepare(trng->ahb);
}

static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
 struct starfive_trng *trng = to_trng(rng);
 int ret;

 pm_runtime_get_sync(trng->dev);

 if (trng->mode == PRNG_256BIT)
  max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
 else
  max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));

 if (wait) {
  ret = starfive_trng_wait_idle(trng);
  if (ret)
   return -ETIMEDOUT;
 }

 ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);
 if (ret)
  return ret;

 memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);

 pm_runtime_put_sync_autosuspend(trng->dev);

 return max;
}

static int starfive_trng_probe(struct platform_device *pdev)
{
 int ret;
 int irq;
 struct starfive_trng *trng;

 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
 if (!trng)
  return -ENOMEM;

 platform_set_drvdata(pdev, trng);
 trng->dev = &pdev->dev;

 trng->base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(trng->base))
  return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
         "Error remapping memory for platform device.\n");

 irq = platform_get_irq(pdev, 0);
 if (irq < 0)
  return irq;

 init_completion(&trng->random_done);
 init_completion(&trng->reseed_done);
 spin_lock_init(&trng->write_lock);

 ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name,
          (void *)trng);
 if (ret)
  return dev_err_probe(&pdev->dev, ret,
         "Failed to register interrupt handler\n");

 trng->hclk = devm_clk_get(&pdev->dev, "hclk");
 if (IS_ERR(trng->hclk))
  return dev_err_probe(&pdev->dev, PTR_ERR(trng->hclk),
         "Error getting hardware reference clock\n");

 trng->ahb = devm_clk_get(&pdev->dev, "ahb");
 if (IS_ERR(trng->ahb))
  return dev_err_probe(&pdev->dev, PTR_ERR(trng->ahb),
         "Error getting ahb reference clock\n");

 trng->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
 if (IS_ERR(trng->rst))
  return dev_err_probe(&pdev->dev, PTR_ERR(trng->rst),
         "Error getting hardware reset line\n");

 clk_prepare_enable(trng->hclk);
 clk_prepare_enable(trng->ahb);
 reset_control_deassert(trng->rst);

 trng->rng.name = dev_driver_string(&pdev->dev);
 trng->rng.init = starfive_trng_init;
 trng->rng.cleanup = starfive_trng_cleanup;
 trng->rng.read = starfive_trng_read;

 trng->mode = PRNG_256BIT;
 trng->mission = 1;
 trng->reseed = RANDOM_RESEED;

 pm_runtime_use_autosuspend(&pdev->dev);
 pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
 pm_runtime_enable(&pdev->dev);

 ret = devm_hwrng_register(&pdev->dev, &trng->rng);
 if (ret) {
  pm_runtime_disable(&pdev->dev);

  reset_control_assert(trng->rst);
  clk_disable_unprepare(trng->ahb);
  clk_disable_unprepare(trng->hclk);

  return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
 }

 return 0;
}

static int __maybe_unused starfive_trng_suspend(struct device *dev)
{
 struct starfive_trng *trng = dev_get_drvdata(dev);

 clk_disable_unprepare(trng->hclk);
 clk_disable_unprepare(trng->ahb);

 return 0;
}

static int __maybe_unused starfive_trng_resume(struct device *dev)
{
 struct starfive_trng *trng = dev_get_drvdata(dev);

 clk_prepare_enable(trng->hclk);
 clk_prepare_enable(trng->ahb);

 return 0;
}

static const struct dev_pm_ops starfive_trng_pm_ops = {
 SET_SYSTEM_SLEEP_PM_OPS(starfive_trng_suspend,
    starfive_trng_resume)
 SET_RUNTIME_PM_OPS(starfive_trng_suspend,
      starfive_trng_resume, NULL)
};

static const struct of_device_id trng_dt_ids[] __maybe_unused = {
 { .compatible = "starfive,jh7110-trng" },
 { }
};
MODULE_DEVICE_TABLE(of, trng_dt_ids);

static struct platform_driver starfive_trng_driver = {
 .probe = starfive_trng_probe,
 .driver = {
  .name  = "jh7110-trng",
  .pm  = &starfive_trng_pm_ops,
  .of_match_table = of_match_ptr(trng_dt_ids),
 },
};

module_platform_driver(starfive_trng_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("StarFive True Random Number Generator");

Messung V0.5
C=90 H=97 G=93

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge