Path: blob/master/drivers/char/hw_random/timeriomem-rng.c
29269 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* drivers/char/hw_random/timeriomem-rng.c3*4* Copyright (C) 2009 Alexander Clouter <[email protected]>5*6* Derived from drivers/char/hw_random/omap-rng.c7* Copyright 2005 (c) MontaVista Software, Inc.8* Author: Deepak Saxena <[email protected]>9*10* Overview:11* This driver is useful for platforms that have an IO range that provides12* periodic random data from a single IO memory address. All the platform13* has to do is provide the address and 'wait time' that new data becomes14* available.15*16* TODO: add support for reading sizes other than 32bits and masking17*/1819#include <linux/completion.h>20#include <linux/delay.h>21#include <linux/hrtimer.h>22#include <linux/hw_random.h>23#include <linux/io.h>24#include <linux/ktime.h>25#include <linux/module.h>26#include <linux/of.h>27#include <linux/platform_device.h>28#include <linux/slab.h>29#include <linux/time.h>30#include <linux/timeriomem-rng.h>3132struct timeriomem_rng_private {33void __iomem *io_base;34ktime_t period;35unsigned int present:1;3637struct hrtimer timer;38struct completion completion;3940struct hwrng rng_ops;41};4243static int timeriomem_rng_read(struct hwrng *hwrng, void *data,44size_t max, bool wait)45{46struct timeriomem_rng_private *priv =47container_of(hwrng, struct timeriomem_rng_private, rng_ops);48int retval = 0;49int period_us = ktime_to_us(priv->period);5051/*52* There may not have been enough time for new data to be generated53* since the last request. If the caller doesn't want to wait, let them54* bail out. Otherwise, wait for the completion. If the new data has55* already been generated, the completion should already be available.56*/57if (!wait && !priv->present)58return 0;5960wait_for_completion(&priv->completion);6162do {63/*64* After the first read, all additional reads will need to wait65* for the RNG to generate new data. Since the period can have66* a wide range of values (1us to 1s have been observed), allow67* for 1% tolerance in the sleep time rather than a fixed value.68*/69if (retval > 0)70usleep_range(period_us,71period_us + max(1, period_us / 100));7273*(u32 *)data = readl(priv->io_base);74retval += sizeof(u32);75data += sizeof(u32);76max -= sizeof(u32);77} while (wait && max > sizeof(u32));7879/*80* Block any new callers until the RNG has had time to generate new81* data.82*/83priv->present = 0;84reinit_completion(&priv->completion);85hrtimer_forward_now(&priv->timer, priv->period);86hrtimer_restart(&priv->timer);8788return retval;89}9091static enum hrtimer_restart timeriomem_rng_trigger(struct hrtimer *timer)92{93struct timeriomem_rng_private *priv94= container_of(timer, struct timeriomem_rng_private, timer);9596priv->present = 1;97complete(&priv->completion);9899return HRTIMER_NORESTART;100}101102static int timeriomem_rng_probe(struct platform_device *pdev)103{104struct timeriomem_rng_data *pdata = pdev->dev.platform_data;105struct timeriomem_rng_private *priv;106struct resource *res;107int err = 0;108int period;109110if (!pdev->dev.of_node && !pdata) {111dev_err(&pdev->dev, "timeriomem_rng_data is missing\n");112return -EINVAL;113}114115/* Allocate memory for the device structure (and zero it) */116priv = devm_kzalloc(&pdev->dev,117sizeof(struct timeriomem_rng_private), GFP_KERNEL);118if (!priv)119return -ENOMEM;120121platform_set_drvdata(pdev, priv);122123priv->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);124if (IS_ERR(priv->io_base))125return PTR_ERR(priv->io_base);126127if (res->start % 4 != 0 || resource_size(res) < 4) {128dev_err(&pdev->dev,129"address must be at least four bytes wide and 32-bit aligned\n");130return -EINVAL;131}132133if (pdev->dev.of_node) {134int i;135136if (!of_property_read_u32(pdev->dev.of_node,137"period", &i))138period = i;139else {140dev_err(&pdev->dev, "missing period\n");141return -EINVAL;142}143144if (!of_property_read_u32(pdev->dev.of_node,145"quality", &i))146priv->rng_ops.quality = i;147} else {148period = pdata->period;149priv->rng_ops.quality = pdata->quality;150}151152priv->period = ns_to_ktime(period * NSEC_PER_USEC);153init_completion(&priv->completion);154hrtimer_setup(&priv->timer, timeriomem_rng_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);155156priv->rng_ops.name = dev_name(&pdev->dev);157priv->rng_ops.read = timeriomem_rng_read;158159/* Assume random data is already available. */160priv->present = 1;161complete(&priv->completion);162163err = devm_hwrng_register(&pdev->dev, &priv->rng_ops);164if (err) {165dev_err(&pdev->dev, "problem registering\n");166return err;167}168169dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",170priv->io_base, period);171172return 0;173}174175static void timeriomem_rng_remove(struct platform_device *pdev)176{177struct timeriomem_rng_private *priv = platform_get_drvdata(pdev);178179hrtimer_cancel(&priv->timer);180}181182static const struct of_device_id timeriomem_rng_match[] = {183{ .compatible = "timeriomem_rng" },184{},185};186MODULE_DEVICE_TABLE(of, timeriomem_rng_match);187188static struct platform_driver timeriomem_rng_driver = {189.driver = {190.name = "timeriomem_rng",191.of_match_table = timeriomem_rng_match,192},193.probe = timeriomem_rng_probe,194.remove = timeriomem_rng_remove,195};196197module_platform_driver(timeriomem_rng_driver);198199MODULE_LICENSE("GPL");200MODULE_AUTHOR("Alexander Clouter <[email protected]>");201MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");202203204