Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.4-rc4 233 lines 5.0 kB view raw
1/* 2 * omap-rng.c - RNG driver for TI OMAP CPU family 3 * 4 * Author: Deepak Saxena <dsaxena@plexity.net> 5 * 6 * Copyright 2005 (c) MontaVista Software, Inc. 7 * 8 * Mostly based on original driver: 9 * 10 * Copyright (C) 2005 Nokia Corporation 11 * Author: Juha Yrjölä <juha.yrjola@nokia.com> 12 * 13 * This file is licensed under the terms of the GNU General Public 14 * License version 2. This program is licensed "as is" without any 15 * warranty of any kind, whether express or implied. 16 */ 17 18#include <linux/module.h> 19#include <linux/init.h> 20#include <linux/random.h> 21#include <linux/clk.h> 22#include <linux/err.h> 23#include <linux/platform_device.h> 24#include <linux/hw_random.h> 25#include <linux/delay.h> 26 27#include <asm/io.h> 28 29#include <plat/cpu.h> 30 31#define RNG_OUT_REG 0x00 /* Output register */ 32#define RNG_STAT_REG 0x04 /* Status register 33 [0] = STAT_BUSY */ 34#define RNG_ALARM_REG 0x24 /* Alarm register 35 [7:0] = ALARM_COUNTER */ 36#define RNG_CONFIG_REG 0x28 /* Configuration register 37 [11:6] = RESET_COUNT 38 [5:3] = RING2_DELAY 39 [2:0] = RING1_DELAY */ 40#define RNG_REV_REG 0x3c /* Revision register 41 [7:0] = REV_NB */ 42#define RNG_MASK_REG 0x40 /* Mask and reset register 43 [2] = IT_EN 44 [1] = SOFTRESET 45 [0] = AUTOIDLE */ 46#define RNG_SYSSTATUS 0x44 /* System status 47 [0] = RESETDONE */ 48 49static void __iomem *rng_base; 50static struct clk *rng_ick; 51static struct platform_device *rng_dev; 52 53static inline u32 omap_rng_read_reg(int reg) 54{ 55 return __raw_readl(rng_base + reg); 56} 57 58static inline void omap_rng_write_reg(int reg, u32 val) 59{ 60 __raw_writel(val, rng_base + reg); 61} 62 63static int omap_rng_data_present(struct hwrng *rng, int wait) 64{ 65 int data, i; 66 67 for (i = 0; i < 20; i++) { 68 data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1; 69 if (data || !wait) 70 break; 71 /* RNG produces data fast enough (2+ MBit/sec, even 72 * during "rngtest" loads, that these delays don't 73 * seem to trigger. We *could* use the RNG IRQ, but 74 * that'd be higher overhead ... so why bother? 75 */ 76 udelay(10); 77 } 78 return data; 79} 80 81static int omap_rng_data_read(struct hwrng *rng, u32 *data) 82{ 83 *data = omap_rng_read_reg(RNG_OUT_REG); 84 85 return 4; 86} 87 88static struct hwrng omap_rng_ops = { 89 .name = "omap", 90 .data_present = omap_rng_data_present, 91 .data_read = omap_rng_data_read, 92}; 93 94static int __devinit omap_rng_probe(struct platform_device *pdev) 95{ 96 struct resource *res; 97 int ret; 98 99 /* 100 * A bit ugly, and it will never actually happen but there can 101 * be only one RNG and this catches any bork 102 */ 103 if (rng_dev) 104 return -EBUSY; 105 106 if (cpu_is_omap24xx()) { 107 rng_ick = clk_get(&pdev->dev, "ick"); 108 if (IS_ERR(rng_ick)) { 109 dev_err(&pdev->dev, "Could not get rng_ick\n"); 110 ret = PTR_ERR(rng_ick); 111 return ret; 112 } else 113 clk_enable(rng_ick); 114 } 115 116 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 117 118 if (!res) { 119 ret = -ENOENT; 120 goto err_region; 121 } 122 123 if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 124 ret = -EBUSY; 125 goto err_region; 126 } 127 128 dev_set_drvdata(&pdev->dev, res); 129 rng_base = ioremap(res->start, resource_size(res)); 130 if (!rng_base) { 131 ret = -ENOMEM; 132 goto err_ioremap; 133 } 134 135 ret = hwrng_register(&omap_rng_ops); 136 if (ret) 137 goto err_register; 138 139 dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", 140 omap_rng_read_reg(RNG_REV_REG)); 141 omap_rng_write_reg(RNG_MASK_REG, 0x1); 142 143 rng_dev = pdev; 144 145 return 0; 146 147err_register: 148 iounmap(rng_base); 149 rng_base = NULL; 150err_ioremap: 151 release_mem_region(res->start, resource_size(res)); 152err_region: 153 if (cpu_is_omap24xx()) { 154 clk_disable(rng_ick); 155 clk_put(rng_ick); 156 } 157 return ret; 158} 159 160static int __exit omap_rng_remove(struct platform_device *pdev) 161{ 162 struct resource *res = dev_get_drvdata(&pdev->dev); 163 164 hwrng_unregister(&omap_rng_ops); 165 166 omap_rng_write_reg(RNG_MASK_REG, 0x0); 167 168 iounmap(rng_base); 169 170 if (cpu_is_omap24xx()) { 171 clk_disable(rng_ick); 172 clk_put(rng_ick); 173 } 174 175 release_mem_region(res->start, resource_size(res)); 176 rng_base = NULL; 177 178 return 0; 179} 180 181#ifdef CONFIG_PM 182 183static int omap_rng_suspend(struct platform_device *pdev, pm_message_t message) 184{ 185 omap_rng_write_reg(RNG_MASK_REG, 0x0); 186 return 0; 187} 188 189static int omap_rng_resume(struct platform_device *pdev) 190{ 191 omap_rng_write_reg(RNG_MASK_REG, 0x1); 192 return 0; 193} 194 195#else 196 197#define omap_rng_suspend NULL 198#define omap_rng_resume NULL 199 200#endif 201 202/* work with hotplug and coldplug */ 203MODULE_ALIAS("platform:omap_rng"); 204 205static struct platform_driver omap_rng_driver = { 206 .driver = { 207 .name = "omap_rng", 208 .owner = THIS_MODULE, 209 }, 210 .probe = omap_rng_probe, 211 .remove = __exit_p(omap_rng_remove), 212 .suspend = omap_rng_suspend, 213 .resume = omap_rng_resume 214}; 215 216static int __init omap_rng_init(void) 217{ 218 if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) 219 return -ENODEV; 220 221 return platform_driver_register(&omap_rng_driver); 222} 223 224static void __exit omap_rng_exit(void) 225{ 226 platform_driver_unregister(&omap_rng_driver); 227} 228 229module_init(omap_rng_init); 230module_exit(omap_rng_exit); 231 232MODULE_AUTHOR("Deepak Saxena (and others)"); 233MODULE_LICENSE("GPL");