1/* 2 * Broadcom BCM63xx Random Number Generator support 3 * 4 * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org> 5 * Copyright (C) 2009, Broadcom Corporation 6 * 7 */ 8#include <linux/module.h> 9#include <linux/slab.h> 10#include <linux/io.h> 11#include <linux/err.h> 12#include <linux/clk.h> 13#include <linux/platform_device.h> 14#include <linux/hw_random.h> 15 16#define RNG_CTRL 0x00 17#define RNG_EN (1 << 0) 18 19#define RNG_STAT 0x04 20#define RNG_AVAIL_MASK (0xff000000) 21 22#define RNG_DATA 0x08 23#define RNG_THRES 0x0c 24#define RNG_MASK 0x10 25 26struct bcm63xx_rng_priv { 27 struct hwrng rng; 28 struct clk *clk; 29 void __iomem *regs; 30}; 31 32#define to_rng_priv(rng) container_of(rng, struct bcm63xx_rng_priv, rng) 33 34static int bcm63xx_rng_init(struct hwrng *rng) 35{ 36 struct bcm63xx_rng_priv *priv = to_rng_priv(rng); 37 u32 val; 38 int error; 39 40 error = clk_prepare_enable(priv->clk); 41 if (error) 42 return error; 43 44 val = __raw_readl(priv->regs + RNG_CTRL); 45 val |= RNG_EN; 46 __raw_writel(val, priv->regs + RNG_CTRL); 47 48 return 0; 49} 50 51static void bcm63xx_rng_cleanup(struct hwrng *rng) 52{ 53 struct bcm63xx_rng_priv *priv = to_rng_priv(rng); 54 u32 val; 55 56 val = __raw_readl(priv->regs + RNG_CTRL); 57 val &= ~RNG_EN; 58 __raw_writel(val, priv->regs + RNG_CTRL); 59 60 clk_disable_unprepare(priv->clk); 61} 62 63static int bcm63xx_rng_data_present(struct hwrng *rng, int wait) 64{ 65 struct bcm63xx_rng_priv *priv = to_rng_priv(rng); 66 67 return __raw_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; 68} 69 70static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data) 71{ 72 struct bcm63xx_rng_priv *priv = to_rng_priv(rng); 73 74 *data = __raw_readl(priv->regs + RNG_DATA); 75 76 return 4; 77} 78 79static int bcm63xx_rng_probe(struct platform_device *pdev) 80{ 81 struct resource *r; 82 struct clk *clk; 83 int ret; 84 struct bcm63xx_rng_priv *priv; 85 struct hwrng *rng; 86 87 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 88 if (!r) { 89 dev_err(&pdev->dev, "no iomem resource\n"); 90 return -ENXIO; 91 } 92 93 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 94 if (!priv) 95 return -ENOMEM; 96 97 priv->rng.name = pdev->name; 98 priv->rng.init = bcm63xx_rng_init; 99 priv->rng.cleanup = bcm63xx_rng_cleanup; 100 priv->rng.data_present = bcm63xx_rng_data_present; 101 priv->rng.data_read = bcm63xx_rng_data_read; 102 103 priv->clk = devm_clk_get(&pdev->dev, "ipsec"); 104 if (IS_ERR(priv->clk)) { 105 ret = PTR_ERR(priv->clk); 106 dev_err(&pdev->dev, "no clock for device: %d\n", ret); 107 return ret; 108 } 109 110 if (!devm_request_mem_region(&pdev->dev, r->start, 111 resource_size(r), pdev->name)) { 112 dev_err(&pdev->dev, "request mem failed"); 113 return -EBUSY; 114 } 115 116 priv->regs = devm_ioremap_nocache(&pdev->dev, r->start, 117 resource_size(r)); 118 if (!priv->regs) { 119 dev_err(&pdev->dev, "ioremap failed"); 120 return -ENOMEM; 121 } 122 123 ret = devm_hwrng_register(&pdev->dev, &priv->rng); 124 if (ret) { 125 dev_err(&pdev->dev, "failed to register rng device: %d\n", 126 ret); 127 return ret; 128 } 129 130 dev_info(&pdev->dev, "registered RNG driver\n"); 131 132 return 0; 133} 134 135static struct platform_driver bcm63xx_rng_driver = { 136 .probe = bcm63xx_rng_probe, 137 .driver = { 138 .name = "bcm63xx-rng", 139 }, 140}; 141 142module_platform_driver(bcm63xx_rng_driver); 143 144MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 145MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver"); 146MODULE_LICENSE("GPL"); 147