linux/drivers/char/hw_random/picoxcell-rng.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-2011 Picochip Ltd., Jamie Iles
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * All enquiries to support@picochip.com
   9 */
  10#include <linux/clk.h>
  11#include <linux/delay.h>
  12#include <linux/err.h>
  13#include <linux/hw_random.h>
  14#include <linux/io.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18
  19#define DATA_REG_OFFSET         0x0200
  20#define CSR_REG_OFFSET          0x0278
  21#define CSR_OUT_EMPTY_MASK      (1 << 24)
  22#define CSR_FAULT_MASK          (1 << 1)
  23#define TRNG_BLOCK_RESET_MASK   (1 << 0)
  24#define TAI_REG_OFFSET          0x0380
  25
  26/*
  27 * The maximum amount of time in microseconds to spend waiting for data if the
  28 * core wants us to wait.  The TRNG should generate 32 bits every 320ns so a
  29 * timeout of 20us seems reasonable.  The TRNG does builtin tests of the data
  30 * for randomness so we can't always assume there is data present.
  31 */
  32#define PICO_TRNG_TIMEOUT               20
  33
  34static void __iomem *rng_base;
  35static struct clk *rng_clk;
  36struct device *rng_dev;
  37
  38static inline u32 picoxcell_trng_read_csr(void)
  39{
  40        return __raw_readl(rng_base + CSR_REG_OFFSET);
  41}
  42
  43static inline bool picoxcell_trng_is_empty(void)
  44{
  45        return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK;
  46}
  47
  48/*
  49 * Take the random number generator out of reset and make sure the interrupts
  50 * are masked. We shouldn't need to get large amounts of random bytes so just
  51 * poll the status register. The hardware generates 32 bits every 320ns so we
  52 * shouldn't have to wait long enough to warrant waiting for an IRQ.
  53 */
  54static void picoxcell_trng_start(void)
  55{
  56        __raw_writel(0, rng_base + TAI_REG_OFFSET);
  57        __raw_writel(0, rng_base + CSR_REG_OFFSET);
  58}
  59
  60static void picoxcell_trng_reset(void)
  61{
  62        __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET);
  63        __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET);
  64        picoxcell_trng_start();
  65}
  66
  67/*
  68 * Get some random data from the random number generator. The hw_random core
  69 * layer provides us with locking.
  70 */
  71static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max,
  72                               bool wait)
  73{
  74        int i;
  75
  76        /* Wait for some data to become available. */
  77        for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) {
  78                if (!wait)
  79                        return 0;
  80
  81                udelay(1);
  82        }
  83
  84        if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) {
  85                dev_err(rng_dev, "fault detected, resetting TRNG\n");
  86                picoxcell_trng_reset();
  87                return -EIO;
  88        }
  89
  90        if (i == PICO_TRNG_TIMEOUT)
  91                return 0;
  92
  93        *(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET);
  94        return sizeof(u32);
  95}
  96
  97static struct hwrng picoxcell_trng = {
  98        .name           = "picoxcell",
  99        .read           = picoxcell_trng_read,
 100};
 101
 102static int picoxcell_trng_probe(struct platform_device *pdev)
 103{
 104        int ret;
 105        struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 106
 107        if (!mem) {
 108                dev_warn(&pdev->dev, "no memory resource\n");
 109                return -ENOMEM;
 110        }
 111
 112        if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
 113                                     "picoxcell_trng")) {
 114                dev_warn(&pdev->dev, "unable to request io mem\n");
 115                return -EBUSY;
 116        }
 117
 118        rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
 119        if (!rng_base) {
 120                dev_warn(&pdev->dev, "unable to remap io mem\n");
 121                return -ENOMEM;
 122        }
 123
 124        rng_clk = clk_get(&pdev->dev, NULL);
 125        if (IS_ERR(rng_clk)) {
 126                dev_warn(&pdev->dev, "no clk\n");
 127                return PTR_ERR(rng_clk);
 128        }
 129
 130        ret = clk_enable(rng_clk);
 131        if (ret) {
 132                dev_warn(&pdev->dev, "unable to enable clk\n");
 133                goto err_enable;
 134        }
 135
 136        picoxcell_trng_start();
 137        ret = hwrng_register(&picoxcell_trng);
 138        if (ret)
 139                goto err_register;
 140
 141        rng_dev = &pdev->dev;
 142        dev_info(&pdev->dev, "pixoxcell random number generator active\n");
 143
 144        return 0;
 145
 146err_register:
 147        clk_disable(rng_clk);
 148err_enable:
 149        clk_put(rng_clk);
 150
 151        return ret;
 152}
 153
 154static int picoxcell_trng_remove(struct platform_device *pdev)
 155{
 156        hwrng_unregister(&picoxcell_trng);
 157        clk_disable(rng_clk);
 158        clk_put(rng_clk);
 159
 160        return 0;
 161}
 162
 163#ifdef CONFIG_PM
 164static int picoxcell_trng_suspend(struct device *dev)
 165{
 166        clk_disable(rng_clk);
 167
 168        return 0;
 169}
 170
 171static int picoxcell_trng_resume(struct device *dev)
 172{
 173        return clk_enable(rng_clk);
 174}
 175
 176static const struct dev_pm_ops picoxcell_trng_pm_ops = {
 177        .suspend        = picoxcell_trng_suspend,
 178        .resume         = picoxcell_trng_resume,
 179};
 180#endif /* CONFIG_PM */
 181
 182static struct platform_driver picoxcell_trng_driver = {
 183        .probe          = picoxcell_trng_probe,
 184        .remove         = picoxcell_trng_remove,
 185        .driver         = {
 186                .name   = "picoxcell-trng",
 187                .owner  = THIS_MODULE,
 188#ifdef CONFIG_PM
 189                .pm     = &picoxcell_trng_pm_ops,
 190#endif /* CONFIG_PM */
 191        },
 192};
 193
 194module_platform_driver(picoxcell_trng_driver);
 195
 196MODULE_LICENSE("GPL");
 197MODULE_AUTHOR("Jamie Iles");
 198MODULE_DESCRIPTION("Picochip picoXcell TRNG driver");
 199
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.