Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * Copyright (c) 2010-2012 Broadcom. All rights reserved. 4 : : * Copyright (c) 2013 Lubomir Rintel 5 : : */ 6 : : 7 : : #include <linux/hw_random.h> 8 : : #include <linux/io.h> 9 : : #include <linux/kernel.h> 10 : : #include <linux/module.h> 11 : : #include <linux/of_address.h> 12 : : #include <linux/of_platform.h> 13 : : #include <linux/platform_device.h> 14 : : #include <linux/printk.h> 15 : : #include <linux/clk.h> 16 : : 17 : : #define RNG_CTRL 0x0 18 : : #define RNG_STATUS 0x4 19 : : #define RNG_DATA 0x8 20 : : #define RNG_INT_MASK 0x10 21 : : 22 : : /* enable rng */ 23 : : #define RNG_RBGEN 0x1 24 : : 25 : : /* the initial numbers generated are "less random" so will be discarded */ 26 : : #define RNG_WARMUP_COUNT 0x40000 27 : : 28 : : #define RNG_INT_OFF 0x1 29 : : 30 : : struct bcm2835_rng_priv { 31 : : struct hwrng rng; 32 : : void __iomem *base; 33 : : bool mask_interrupts; 34 : : struct clk *clk; 35 : : }; 36 : : 37 : : static inline struct bcm2835_rng_priv *to_rng_priv(struct hwrng *rng) 38 : : { 39 : : return container_of(rng, struct bcm2835_rng_priv, rng); 40 : : } 41 : : 42 : : static inline u32 rng_readl(struct bcm2835_rng_priv *priv, u32 offset) 43 : : { 44 : : /* MIPS chips strapped for BE will automagically configure the 45 : : * peripheral registers for CPU-native byte order. 46 : : */ 47 : : if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 48 : : return __raw_readl(priv->base + offset); 49 : : else 50 : 3 : return readl(priv->base + offset); 51 : : } 52 : : 53 : : static inline void rng_writel(struct bcm2835_rng_priv *priv, u32 val, 54 : : u32 offset) 55 : : { 56 : : if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 57 : : __raw_writel(val, priv->base + offset); 58 : : else 59 : 3 : writel(val, priv->base + offset); 60 : : } 61 : : 62 : 3 : static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max, 63 : : bool wait) 64 : : { 65 : : struct bcm2835_rng_priv *priv = to_rng_priv(rng); 66 : 3 : u32 max_words = max / sizeof(u32); 67 : : u32 num_words, count; 68 : : 69 : 3 : while ((rng_readl(priv, RNG_STATUS) >> 24) == 0) { 70 : 2 : if (!wait) 71 : : return 0; 72 : 2 : cpu_relax(); 73 : : } 74 : : 75 : 3 : num_words = rng_readl(priv, RNG_STATUS) >> 24; 76 : 3 : if (num_words > max_words) 77 : : num_words = max_words; 78 : : 79 : 3 : for (count = 0; count < num_words; count++) 80 : 3 : ((u32 *)buf)[count] = rng_readl(priv, RNG_DATA); 81 : : 82 : 3 : return num_words * sizeof(u32); 83 : : } 84 : : 85 : 3 : static int bcm2835_rng_init(struct hwrng *rng) 86 : : { 87 : : struct bcm2835_rng_priv *priv = to_rng_priv(rng); 88 : : int ret = 0; 89 : : u32 val; 90 : : 91 : 3 : if (!IS_ERR(priv->clk)) { 92 : 0 : ret = clk_prepare_enable(priv->clk); 93 : 0 : if (ret) 94 : : return ret; 95 : : } 96 : : 97 : 3 : if (priv->mask_interrupts) { 98 : : /* mask the interrupt */ 99 : : val = rng_readl(priv, RNG_INT_MASK); 100 : 0 : val |= RNG_INT_OFF; 101 : : rng_writel(priv, val, RNG_INT_MASK); 102 : : } 103 : : 104 : : /* set warm-up count & enable */ 105 : 3 : if (!(rng_readl(priv, RNG_CTRL) & RNG_RBGEN)) { 106 : : rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS); 107 : : rng_writel(priv, RNG_RBGEN, RNG_CTRL); 108 : : } 109 : : 110 : 3 : return ret; 111 : : } 112 : : 113 : 0 : static void bcm2835_rng_cleanup(struct hwrng *rng) 114 : : { 115 : : struct bcm2835_rng_priv *priv = to_rng_priv(rng); 116 : : 117 : : /* disable rng hardware */ 118 : : rng_writel(priv, 0, RNG_CTRL); 119 : : 120 : 0 : if (!IS_ERR(priv->clk)) 121 : : clk_disable_unprepare(priv->clk); 122 : 0 : } 123 : : 124 : : struct bcm2835_rng_of_data { 125 : : bool mask_interrupts; 126 : : }; 127 : : 128 : : static const struct bcm2835_rng_of_data nsp_rng_of_data = { 129 : : .mask_interrupts = true, 130 : : }; 131 : : 132 : : static const struct of_device_id bcm2835_rng_of_match[] = { 133 : : { .compatible = "brcm,bcm2835-rng"}, 134 : : { .compatible = "brcm,bcm-nsp-rng", .data = &nsp_rng_of_data }, 135 : : { .compatible = "brcm,bcm5301x-rng", .data = &nsp_rng_of_data }, 136 : : { .compatible = "brcm,bcm6368-rng"}, 137 : : {}, 138 : : }; 139 : : 140 : 3 : static int bcm2835_rng_probe(struct platform_device *pdev) 141 : : { 142 : : const struct bcm2835_rng_of_data *of_data; 143 : 3 : struct device *dev = &pdev->dev; 144 : 3 : struct device_node *np = dev->of_node; 145 : : const struct of_device_id *rng_id; 146 : : struct bcm2835_rng_priv *priv; 147 : : struct resource *r; 148 : : int err; 149 : : 150 : : priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 151 : 3 : if (!priv) 152 : : return -ENOMEM; 153 : : 154 : : platform_set_drvdata(pdev, priv); 155 : : 156 : 3 : r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 157 : : 158 : : /* map peripheral */ 159 : 3 : priv->base = devm_ioremap_resource(dev, r); 160 : 3 : if (IS_ERR(priv->base)) 161 : 0 : return PTR_ERR(priv->base); 162 : : 163 : : /* Clock is optional on most platforms */ 164 : 3 : priv->clk = devm_clk_get(dev, NULL); 165 : 3 : if (IS_ERR(priv->clk) && PTR_ERR(priv->clk) == -EPROBE_DEFER) 166 : : return -EPROBE_DEFER; 167 : : 168 : 3 : priv->rng.name = pdev->name; 169 : 3 : priv->rng.init = bcm2835_rng_init; 170 : 3 : priv->rng.read = bcm2835_rng_read; 171 : 3 : priv->rng.cleanup = bcm2835_rng_cleanup; 172 : : 173 : 3 : if (dev_of_node(dev)) { 174 : 3 : rng_id = of_match_node(bcm2835_rng_of_match, np); 175 : 3 : if (!rng_id) 176 : : return -EINVAL; 177 : : 178 : : /* Check for rng init function, execute it */ 179 : 3 : of_data = rng_id->data; 180 : 3 : if (of_data) 181 : 0 : priv->mask_interrupts = of_data->mask_interrupts; 182 : : } 183 : : 184 : : /* register driver */ 185 : 3 : err = devm_hwrng_register(dev, &priv->rng); 186 : 3 : if (err) 187 : 0 : dev_err(dev, "hwrng registration failed\n"); 188 : : else 189 : 3 : dev_info(dev, "hwrng registered\n"); 190 : : 191 : 3 : return err; 192 : : } 193 : : 194 : : MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match); 195 : : 196 : : static struct platform_device_id bcm2835_rng_devtype[] = { 197 : : { .name = "bcm2835-rng" }, 198 : : { .name = "bcm63xx-rng" }, 199 : : { /* sentinel */ } 200 : : }; 201 : : MODULE_DEVICE_TABLE(platform, bcm2835_rng_devtype); 202 : : 203 : : static struct platform_driver bcm2835_rng_driver = { 204 : : .driver = { 205 : : .name = "bcm2835-rng", 206 : : .of_match_table = bcm2835_rng_of_match, 207 : : }, 208 : : .probe = bcm2835_rng_probe, 209 : : .id_table = bcm2835_rng_devtype, 210 : : }; 211 : 3 : module_platform_driver(bcm2835_rng_driver); 212 : : 213 : : MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 214 : : MODULE_DESCRIPTION("BCM2835 Random Number Generator (RNG) driver"); 215 : : MODULE_LICENSE("GPL v2");