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, resource_size(res));
 104        if (!timeriomem_rng_data->address)
 105                return -EIO;
 106
 107        if (timeriomem_rng_data->period != 0
 108                && usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
 109                timeriomem_rng_timer.expires = jiffies;
 110
 111                timeriomem_rng_ops.priv = usecs_to_jiffies(
 112                                                timeriomem_rng_data->period);
 113        }
 114        timeriomem_rng_data->present = 1;
 115
 116        ret = hwrng_register(&timeriomem_rng_ops);
 117        if (ret)
 118                goto failed;
 119
 120        dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
 121                        timeriomem_rng_data->address,
 122                        timeriomem_rng_data->period);
 123
 124        return 0;
 125
 126failed:
 127        dev_err(&pdev->dev, "problem registering\n");
 128        iounmap(timeriomem_rng_data->address);
 129
 130        return ret;
 131}
 132
 133static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
 134{
 135        del_timer_sync(&timeriomem_rng_timer);
 136        hwrng_unregister(&timeriomem_rng_ops);
 137
 138        iounmap(timeriomem_rng_data->address);
 139
 140        return 0;
 141}
 142
 143static struct platform_driver timeriomem_rng_driver = {
 144        .driver = {
 145                .name           = "timeriomem_rng",
 146                .owner          = THIS_MODULE,
 147        },
 148        .probe          = timeriomem_rng_probe,
 149        .remove         = __devexit_p(timeriomem_rng_remove),
 150};
 151
 152module_platform_driver(timeriomem_rng_driver);
 153
 154MODULE_LICENSE("GPL");
 155MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
 156MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
 157