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/completion.h> 24 #include <linux/delay.h> 25 #include <linux/hrtimer.h> 26 #include <linux/hw_random.h> 27 #include <linux/io.h> 28 #include <linux/ktime.h> 29 #include <linux/module.h> 30 #include <linux/of.h> 31 #include <linux/platform_device.h> 32 #include <linux/slab.h> 33 #include <linux/time.h> 34 #include <linux/timeriomem-rng.h> 35 36 struct timeriomem_rng_private { 37 void __iomem *io_base; 38 ktime_t period; 39 unsigned int present:1; 40 41 struct hrtimer timer; 42 struct completion completion; 43 44 struct hwrng rng_ops; 45 }; 46 47 static int timeriomem_rng_read(struct hwrng *hwrng, void *data, 48 size_t max, bool wait) 49 { 50 struct timeriomem_rng_private *priv = 51 container_of(hwrng, struct timeriomem_rng_private, rng_ops); 52 int retval = 0; 53 int period_us = ktime_to_us(priv->period); 54 55 /* 56 * The RNG provides 32-bits per read. Ensure there is enough space for 57 * at minimum one read. 58 */ 59 if (max < sizeof(u32)) 60 return 0; 61 62 /* 63 * There may not have been enough time for new data to be generated 64 * since the last request. If the caller doesn't want to wait, let them 65 * bail out. Otherwise, wait for the completion. If the new data has 66 * already been generated, the completion should already be available. 67 */ 68 if (!wait && !priv->present) 69 return 0; 70 71 wait_for_completion(&priv->completion); 72 73 do { 74 /* 75 * After the first read, all additional reads will need to wait 76 * for the RNG to generate new data. Since the period can have 77 * a wide range of values (1us to 1s have been observed), allow 78 * for 1% tolerance in the sleep time rather than a fixed value. 79 */ 80 if (retval > 0) 81 usleep_range(period_us, 82 period_us + min(1, period_us / 100)); 83 84 *(u32 *)data = readl(priv->io_base); 85 retval += sizeof(u32); 86 data += sizeof(u32); 87 max -= sizeof(u32); 88 } while (wait && max > sizeof(u32)); 89 90 /* 91 * Block any new callers until the RNG has had time to generate new 92 * data. 93 */ 94 priv->present = 0; 95 reinit_completion(&priv->completion); 96 hrtimer_forward_now(&priv->timer, priv->period); 97 hrtimer_restart(&priv->timer); 98 99 return retval; 100 } 101 102 static enum hrtimer_restart timeriomem_rng_trigger(struct hrtimer *timer) 103 { 104 struct timeriomem_rng_private *priv 105 = container_of(timer, struct timeriomem_rng_private, timer); 106 107 priv->present = 1; 108 complete(&priv->completion); 109 110 return HRTIMER_NORESTART; 111 } 112 113 static int timeriomem_rng_probe(struct platform_device *pdev) 114 { 115 struct timeriomem_rng_data *pdata = pdev->dev.platform_data; 116 struct timeriomem_rng_private *priv; 117 struct resource *res; 118 int err = 0; 119 int period; 120 121 if (!pdev->dev.of_node && !pdata) { 122 dev_err(&pdev->dev, "timeriomem_rng_data is missing\n"); 123 return -EINVAL; 124 } 125 126 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 127 if (!res) 128 return -ENXIO; 129 130 if (res->start % 4 != 0 || resource_size(res) != 4) { 131 dev_err(&pdev->dev, 132 "address must be four bytes wide and aligned\n"); 133 return -EINVAL; 134 } 135 136 /* Allocate memory for the device structure (and zero it) */ 137 priv = devm_kzalloc(&pdev->dev, 138 sizeof(struct timeriomem_rng_private), GFP_KERNEL); 139 if (!priv) 140 return -ENOMEM; 141 142 platform_set_drvdata(pdev, priv); 143 144 if (pdev->dev.of_node) { 145 int i; 146 147 if (!of_property_read_u32(pdev->dev.of_node, 148 "period", &i)) 149 period = i; 150 else { 151 dev_err(&pdev->dev, "missing period\n"); 152 return -EINVAL; 153 } 154 155 if (!of_property_read_u32(pdev->dev.of_node, 156 "quality", &i)) 157 priv->rng_ops.quality = i; 158 else 159 priv->rng_ops.quality = 0; 160 } else { 161 period = pdata->period; 162 priv->rng_ops.quality = pdata->quality; 163 } 164 165 priv->period = ns_to_ktime(period * NSEC_PER_USEC); 166 init_completion(&priv->completion); 167 hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); 168 priv->timer.function = timeriomem_rng_trigger; 169 170 priv->rng_ops.name = dev_name(&pdev->dev); 171 priv->rng_ops.read = timeriomem_rng_read; 172 173 priv->io_base = devm_ioremap_resource(&pdev->dev, res); 174 if (IS_ERR(priv->io_base)) { 175 return PTR_ERR(priv->io_base); 176 } 177 178 /* Assume random data is already available. */ 179 priv->present = 1; 180 complete(&priv->completion); 181 182 err = hwrng_register(&priv->rng_ops); 183 if (err) { 184 dev_err(&pdev->dev, "problem registering\n"); 185 return err; 186 } 187 188 dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n", 189 priv->io_base, period); 190 191 return 0; 192 } 193 194 static int timeriomem_rng_remove(struct platform_device *pdev) 195 { 196 struct timeriomem_rng_private *priv = platform_get_drvdata(pdev); 197 198 hwrng_unregister(&priv->rng_ops); 199 hrtimer_cancel(&priv->timer); 200 201 return 0; 202 } 203 204 static const struct of_device_id timeriomem_rng_match[] = { 205 { .compatible = "timeriomem_rng" }, 206 {}, 207 }; 208 MODULE_DEVICE_TABLE(of, timeriomem_rng_match); 209 210 static struct platform_driver timeriomem_rng_driver = { 211 .driver = { 212 .name = "timeriomem_rng", 213 .of_match_table = timeriomem_rng_match, 214 }, 215 .probe = timeriomem_rng_probe, 216 .remove = timeriomem_rng_remove, 217 }; 218 219 module_platform_driver(timeriomem_rng_driver); 220 221 MODULE_LICENSE("GPL"); 222 MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>"); 223 MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver"); 224