linux/drivers/char/hw_random/timeriomem-rng.c
<<
>>
Prefs
   1/*
   2 * drivers/char/hw_random/timeriomem-rng.c
   3 *
   4 * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
   5 *
   6 * Derived from drivers/char/hw_random/omap-rng.c
   7 *   Copyright 2005 (c) MontaVista Software, Inc.
   8 *   Author: Deepak Saxena <dsaxena@plexity.net>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 *
  14 * Overview:
  15 *   This driver is useful for platforms that have an IO range that provides
  16 *   periodic random data from a single IO memory address.  All the platform
  17 *   has to do is provide the address and 'wait time' that new data becomes
  18 *   available.
  19 *
  20 * TODO: add support for reading sizes other than 32bits and masking
  21 */
  22
  23#include <linux/module.h>
  24#include <linux/kernel.h>
  25#include <linux/platform_device.h>
  26#include <linux/of.h>
  27#include <linux/hw_random.h>
  28#include <linux/io.h>
  29#include <linux/slab.h>
  30#include <linux/timeriomem-rng.h>
  31#include <linux/jiffies.h>
  32#include <linux/sched.h>
  33#include <linux/timer.h>
  34#include <linux/completion.h>
  35
  36struct timeriomem_rng_private_data {
  37        void __iomem            *io_base;
  38        unsigned int            expires;
  39        unsigned int            period;
  40        unsigned int            present:1;
  41
  42        struct timer_list       timer;
  43        struct completion       completion;
  44
  45        struct hwrng            timeriomem_rng_ops;
  46};
  47
  48#define to_rng_priv(rng) \
  49                ((struct timeriomem_rng_private_data *)rng->priv)
  50
  51/*
  52 * have data return 1, however return 0 if we have nothing
  53 */
  54static int timeriomem_rng_data_present(struct hwrng *rng, int wait)
  55{
  56        struct timeriomem_rng_private_data *priv = to_rng_priv(rng);
  57
  58        if (!wait || priv->present)
  59                return priv->present;
  60
  61        wait_for_completion(&priv->completion);
  62
  63        return 1;
  64}
  65
  66static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
  67{
  68        struct timeriomem_rng_private_data *priv = to_rng_priv(rng);
  69        unsigned long cur;
  70        s32 delay;
  71
  72        *data = readl(priv->io_base);
  73
  74        cur = jiffies;
  75
  76        delay = cur - priv->expires;
  77        delay = priv->period - (delay % priv->period);
  78
  79        priv->expires = cur + delay;
  80        priv->present = 0;
  81
  82        reinit_completion(&priv->completion);
  83        mod_timer(&priv->timer, priv->expires);
  84
  85        return 4;
  86}
  87
  88static void timeriomem_rng_trigger(unsigned long data)
  89{
  90        struct timeriomem_rng_private_data *priv
  91                        = (struct timeriomem_rng_private_data *)data;
  92
  93        priv->present = 1;
  94        complete(&priv->completion);
  95}
  96
  97static int timeriomem_rng_probe(struct platform_device *pdev)
  98{
  99        struct timeriomem_rng_data *pdata = pdev->dev.platform_data;
 100        struct timeriomem_rng_private_data *priv;
 101        struct resource *res;
 102        int err = 0;
 103        int period;
 104
 105        if (!pdev->dev.of_node && !pdata) {
 106                dev_err(&pdev->dev, "timeriomem_rng_data is missing\n");
 107                return -EINVAL;
 108        }
 109
 110        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 111        if (!res)
 112                return -ENXIO;
 113
 114        if (res->start % 4 != 0 || resource_size(res) != 4) {
 115                dev_err(&pdev->dev,
 116                        "address must be four bytes wide and aligned\n");
 117                return -EINVAL;
 118        }
 119
 120        /* Allocate memory for the device structure (and zero it) */
 121        priv = devm_kzalloc(&pdev->dev,
 122                        sizeof(struct timeriomem_rng_private_data), GFP_KERNEL);
 123        if (!priv)
 124                return -ENOMEM;
 125
 126        platform_set_drvdata(pdev, priv);
 127
 128        if (pdev->dev.of_node) {
 129                int i;
 130
 131                if (!of_property_read_u32(pdev->dev.of_node,
 132                                                "period", &i))
 133                        period = i;
 134                else {
 135                        dev_err(&pdev->dev, "missing period\n");
 136                        return -EINVAL;
 137                }
 138        } else {
 139                period = pdata->period;
 140        }
 141
 142        priv->period = usecs_to_jiffies(period);
 143        if (priv->period < 1) {
 144                dev_err(&pdev->dev, "period is less than one jiffy\n");
 145                return -EINVAL;
 146        }
 147
 148        priv->expires   = jiffies;
 149        priv->present   = 1;
 150
 151        init_completion(&priv->completion);
 152        complete(&priv->completion);
 153
 154        setup_timer(&priv->timer, timeriomem_rng_trigger, (unsigned long)priv);
 155
 156        priv->timeriomem_rng_ops.name           = dev_name(&pdev->dev);
 157        priv->timeriomem_rng_ops.data_present   = timeriomem_rng_data_present;
 158        priv->timeriomem_rng_ops.data_read      = timeriomem_rng_data_read;
 159        priv->timeriomem_rng_ops.priv           = (unsigned long)priv;
 160
 161        priv->io_base = devm_ioremap_resource(&pdev->dev, res);
 162        if (IS_ERR(priv->io_base)) {
 163                err = PTR_ERR(priv->io_base);
 164                goto out_timer;
 165        }
 166
 167        err = hwrng_register(&priv->timeriomem_rng_ops);
 168        if (err) {
 169                dev_err(&pdev->dev, "problem registering\n");
 170                goto out_timer;
 171        }
 172
 173        dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
 174                        priv->io_base, period);
 175
 176        return 0;
 177
 178out_timer:
 179        del_timer_sync(&priv->timer);
 180        return err;
 181}
 182
 183static int timeriomem_rng_remove(struct platform_device *pdev)
 184{
 185        struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev);
 186
 187        hwrng_unregister(&priv->timeriomem_rng_ops);
 188
 189        del_timer_sync(&priv->timer);
 190
 191        return 0;
 192}
 193
 194static const struct of_device_id timeriomem_rng_match[] = {
 195        { .compatible = "timeriomem_rng" },
 196        {},
 197};
 198MODULE_DEVICE_TABLE(of, timeriomem_rng_match);
 199
 200static struct platform_driver timeriomem_rng_driver = {
 201        .driver = {
 202                .name           = "timeriomem_rng",
 203                .of_match_table = timeriomem_rng_match,
 204        },
 205        .probe          = timeriomem_rng_probe,
 206        .remove         = timeriomem_rng_remove,
 207};
 208
 209module_platform_driver(timeriomem_rng_driver);
 210
 211MODULE_LICENSE("GPL");
 212MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
 213MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
 214