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/hw_random.h>
  27#include <linux/io.h>
  28#include <linux/timeriomem-rng.h>
  29#include <linux/jiffies.h>
  30#include <linux/sched.h>
  31#include <linux/timer.h>
  32#include <linux/completion.h>
  33
  34static struct timeriomem_rng_data *timeriomem_rng_data;
  35
  36static void timeriomem_rng_trigger(unsigned long);
  37static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0);
  38
  39/*
  40 * have data return 1, however return 0 if we have nothing
  41 */
  42static int timeriomem_rng_data_present(struct hwrng *rng, int wait)
  43{
  44        if (rng->priv == 0)
  45                return 1;
  46
  47        if (!wait || timeriomem_rng_data->present)
  48                return timeriomem_rng_data->present;
  49
  50        wait_for_completion(&timeriomem_rng_data->completion);
  51
  52        return 1;
  53}
  54
  55static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
  56{
  57        unsigned long cur;
  58        s32 delay;
  59
  60        *data = readl(timeriomem_rng_data->address);
  61
  62        if (rng->priv != 0) {
  63                cur = jiffies;
  64
  65                delay = cur - timeriomem_rng_timer.expires;
  66                delay = rng->priv - (delay % rng->priv);
  67
  68                timeriomem_rng_timer.expires = cur + delay;
  69                timeriomem_rng_data->present = 0;
  70
  71                init_completion(&timeriomem_rng_data->completion);
  72                add_timer(&timeriomem_rng_timer);
  73        }
  74
  75        return 4;
  76}
  77
  78static void timeriomem_rng_trigger(unsigned long dummy)
  79{
  80        timeriomem_rng_data->present = 1;
  81        complete(&timeriomem_rng_data->completion);
  82}
  83
  84static struct hwrng timeriomem_rng_ops = {
  85        .name           = "timeriomem",
  86        .data_present   = timeriomem_rng_data_present,
  87        .data_read      = timeriomem_rng_data_read,
  88        .priv           = 0,
  89};
  90
  91static int __devinit timeriomem_rng_probe(struct platform_device *pdev)
  92{
  93        struct resource *res;
  94        int ret;
  95
  96        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  97
  98        if (!res)
  99                return -ENOENT;
 100
 101        timeriomem_rng_data = pdev->dev.platform_data;
 102
 103        timeriomem_rng_data->address = ioremap(res->start,
 104                                                res->end - res->start + 1);
 105        if (!timeriomem_rng_data->address)
 106                return -EIO;
 107
 108        if (timeriomem_rng_data->period != 0
 109                && usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
 110                timeriomem_rng_timer.expires = jiffies;
 111
 112                timeriomem_rng_ops.priv = usecs_to_jiffies(
 113                                                timeriomem_rng_data->period);
 114        }
 115        timeriomem_rng_data->present = 1;
 116
 117        ret = hwrng_register(&timeriomem_rng_ops);
 118        if (ret)
 119                goto failed;
 120
 121        dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
 122                        timeriomem_rng_data->address,
 123                        timeriomem_rng_data->period);
 124
 125        return 0;
 126
 127failed:
 128        dev_err(&pdev->dev, "problem registering\n");
 129        iounmap(timeriomem_rng_data->address);
 130
 131        return ret;
 132}
 133
 134static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
 135{
 136        del_timer_sync(&timeriomem_rng_timer);
 137        hwrng_unregister(&timeriomem_rng_ops);
 138
 139        iounmap(timeriomem_rng_data->address);
 140
 141        return 0;
 142}
 143
 144static struct platform_driver timeriomem_rng_driver = {
 145        .driver = {
 146                .name           = "timeriomem_rng",
 147                .owner          = THIS_MODULE,
 148        },
 149        .probe          = timeriomem_rng_probe,
 150        .remove         = __devexit_p(timeriomem_rng_remove),
 151};
 152
 153static int __init timeriomem_rng_init(void)
 154{
 155        return platform_driver_register(&timeriomem_rng_driver);
 156}
 157
 158static void __exit timeriomem_rng_exit(void)
 159{
 160        platform_driver_unregister(&timeriomem_rng_driver);
 161}
 162
 163module_init(timeriomem_rng_init);
 164module_exit(timeriomem_rng_exit);
 165
 166MODULE_LICENSE("GPL");
 167MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
 168MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
 169