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 49 static void __iomem *rng_base; 50 static struct clk *rng_ick; 51 static struct platform_device *rng_dev; 52 53 static inline u32 omap_rng_read_reg(int reg) 54 { 55 return __raw_readl(rng_base + reg); 56 } 57 58 static inline void omap_rng_write_reg(int reg, u32 val) 59 { 60 __raw_writel(val, rng_base + reg); 61 } 62 63 static 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 81 static 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 88 static struct hwrng omap_rng_ops = { 89 .name = "omap", 90 .data_present = omap_rng_data_present, 91 .data_read = omap_rng_data_read, 92 }; 93 94 static 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 147 err_register: 148 iounmap(rng_base); 149 rng_base = NULL; 150 err_ioremap: 151 release_mem_region(res->start, resource_size(res)); 152 err_region: 153 if (cpu_is_omap24xx()) { 154 clk_disable(rng_ick); 155 clk_put(rng_ick); 156 } 157 return ret; 158 } 159 160 static 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 183 static 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 189 static 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 */ 203 MODULE_ALIAS("platform:omap_rng"); 204 205 static 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 216 static 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 224 static void __exit omap_rng_exit(void) 225 { 226 platform_driver_unregister(&omap_rng_driver); 227 } 228 229 module_init(omap_rng_init); 230 module_exit(omap_rng_exit); 231 232 MODULE_AUTHOR("Deepak Saxena (and others)"); 233 MODULE_LICENSE("GPL"); 234