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 rng_base = devm_request_and_ioremap(&pdev->dev, res); 119 if (!rng_base) { 120 ret = -ENOMEM; 121 goto err_ioremap; 122 } 123 dev_set_drvdata(&pdev->dev, res); 124 125 ret = hwrng_register(&omap_rng_ops); 126 if (ret) 127 goto err_register; 128 129 dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", 130 omap_rng_read_reg(RNG_REV_REG)); 131 omap_rng_write_reg(RNG_MASK_REG, 0x1); 132 133 rng_dev = pdev; 134 135 return 0; 136 137 err_register: 138 rng_base = NULL; 139 err_ioremap: 140 if (cpu_is_omap24xx()) { 141 clk_disable(rng_ick); 142 clk_put(rng_ick); 143 } 144 return ret; 145 } 146 147 static int __exit omap_rng_remove(struct platform_device *pdev) 148 { 149 hwrng_unregister(&omap_rng_ops); 150 151 omap_rng_write_reg(RNG_MASK_REG, 0x0); 152 153 if (cpu_is_omap24xx()) { 154 clk_disable(rng_ick); 155 clk_put(rng_ick); 156 } 157 158 rng_base = NULL; 159 160 return 0; 161 } 162 163 #ifdef CONFIG_PM_SLEEP 164 165 static int omap_rng_suspend(struct device *dev) 166 { 167 omap_rng_write_reg(RNG_MASK_REG, 0x0); 168 return 0; 169 } 170 171 static int omap_rng_resume(struct device *dev) 172 { 173 omap_rng_write_reg(RNG_MASK_REG, 0x1); 174 return 0; 175 } 176 177 static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); 178 #define OMAP_RNG_PM (&omap_rng_pm) 179 180 #else 181 182 #define OMAP_RNG_PM NULL 183 184 #endif 185 186 /* work with hotplug and coldplug */ 187 MODULE_ALIAS("platform:omap_rng"); 188 189 static struct platform_driver omap_rng_driver = { 190 .driver = { 191 .name = "omap_rng", 192 .owner = THIS_MODULE, 193 .pm = OMAP_RNG_PM, 194 }, 195 .probe = omap_rng_probe, 196 .remove = __exit_p(omap_rng_remove), 197 }; 198 199 static int __init omap_rng_init(void) 200 { 201 if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) 202 return -ENODEV; 203 204 return platform_driver_register(&omap_rng_driver); 205 } 206 207 static void __exit omap_rng_exit(void) 208 { 209 platform_driver_unregister(&omap_rng_driver); 210 } 211 212 module_init(omap_rng_init); 213 module_exit(omap_rng_exit); 214 215 MODULE_AUTHOR("Deepak Saxena (and others)"); 216 MODULE_LICENSE("GPL"); 217