12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a4da0d50SMichael Ellerman /* 3a4da0d50SMichael Ellerman * Copyright 2013, Michael Ellerman, IBM Corporation. 4a4da0d50SMichael Ellerman */ 5a4da0d50SMichael Ellerman 6a4da0d50SMichael Ellerman #define pr_fmt(fmt) "powernv-rng: " fmt 7a4da0d50SMichael Ellerman 8a4da0d50SMichael Ellerman #include <linux/kernel.h> 9a4da0d50SMichael Ellerman #include <linux/of.h> 101400b420SStephen Rothwell #include <linux/of_address.h> 11a4da0d50SMichael Ellerman #include <linux/of_platform.h> 12a4da0d50SMichael Ellerman #include <linux/slab.h> 131400b420SStephen Rothwell #include <linux/smp.h> 14a4da0d50SMichael Ellerman #include <asm/archrandom.h> 15e66ca3dbSMatt Brown #include <asm/cputable.h> 16a4da0d50SMichael Ellerman #include <asm/io.h> 171400b420SStephen Rothwell #include <asm/prom.h> 18a4da0d50SMichael Ellerman #include <asm/machdep.h> 193eb906c6SMichael Ellerman #include <asm/smp.h> 20*f3eac426SJason A. Donenfeld #include "powernv.h" 21a4da0d50SMichael Ellerman 22e66ca3dbSMatt Brown #define DARN_ERR 0xFFFFFFFFFFFFFFFFul 23a4da0d50SMichael Ellerman 24a4da0d50SMichael Ellerman struct powernv_rng { 25a4da0d50SMichael Ellerman void __iomem *regs; 26e928e9cbSMichael Ellerman void __iomem *regs_real; 27a4da0d50SMichael Ellerman unsigned long mask; 28a4da0d50SMichael Ellerman }; 29a4da0d50SMichael Ellerman 30a4da0d50SMichael Ellerman static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); 31a4da0d50SMichael Ellerman 32e928e9cbSMichael Ellerman int powernv_hwrng_present(void) 33e928e9cbSMichael Ellerman { 34e928e9cbSMichael Ellerman struct powernv_rng *rng; 35e928e9cbSMichael Ellerman 36e928e9cbSMichael Ellerman rng = get_cpu_var(powernv_rng); 37e928e9cbSMichael Ellerman put_cpu_var(rng); 38e928e9cbSMichael Ellerman return rng != NULL; 39e928e9cbSMichael Ellerman } 40e928e9cbSMichael Ellerman 41a4da0d50SMichael Ellerman static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val) 42a4da0d50SMichael Ellerman { 43a4da0d50SMichael Ellerman unsigned long parity; 44a4da0d50SMichael Ellerman 45a4da0d50SMichael Ellerman /* Calculate the parity of the value */ 468667d0d6SAnders Roxell asm (".machine push; \ 478667d0d6SAnders Roxell .machine power7; \ 488667d0d6SAnders Roxell popcntd %0,%1; \ 498667d0d6SAnders Roxell .machine pop;" 508667d0d6SAnders Roxell : "=r" (parity) : "r" (val)); 51a4da0d50SMichael Ellerman 52a4da0d50SMichael Ellerman /* xor our value with the previous mask */ 53a4da0d50SMichael Ellerman val ^= rng->mask; 54a4da0d50SMichael Ellerman 55a4da0d50SMichael Ellerman /* update the mask based on the parity of this value */ 56a4da0d50SMichael Ellerman rng->mask = (rng->mask << 1) | (parity & 1); 57a4da0d50SMichael Ellerman 58a4da0d50SMichael Ellerman return val; 59a4da0d50SMichael Ellerman } 60a4da0d50SMichael Ellerman 61e928e9cbSMichael Ellerman int powernv_get_random_real_mode(unsigned long *v) 62e928e9cbSMichael Ellerman { 63e928e9cbSMichael Ellerman struct powernv_rng *rng; 64e928e9cbSMichael Ellerman 65e928e9cbSMichael Ellerman rng = raw_cpu_read(powernv_rng); 66e928e9cbSMichael Ellerman 67d381d7caSBenjamin Herrenschmidt *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real)); 68e928e9cbSMichael Ellerman 69e928e9cbSMichael Ellerman return 1; 70e928e9cbSMichael Ellerman } 71e928e9cbSMichael Ellerman 723b70464aSOliver O'Halloran static int powernv_get_random_darn(unsigned long *v) 73e66ca3dbSMatt Brown { 74e66ca3dbSMatt Brown unsigned long val; 75e66ca3dbSMatt Brown 76e66ca3dbSMatt Brown /* Using DARN with L=1 - 64-bit conditioned random number */ 77e66ca3dbSMatt Brown asm volatile(PPC_DARN(%0, 1) : "=r"(val)); 78e66ca3dbSMatt Brown 79e66ca3dbSMatt Brown if (val == DARN_ERR) 80e66ca3dbSMatt Brown return 0; 81e66ca3dbSMatt Brown 82e66ca3dbSMatt Brown *v = val; 83e66ca3dbSMatt Brown 84e66ca3dbSMatt Brown return 1; 85e66ca3dbSMatt Brown } 86e66ca3dbSMatt Brown 87e5913db1SNick Child static int __init initialise_darn(void) 88e66ca3dbSMatt Brown { 89e66ca3dbSMatt Brown unsigned long val; 90e66ca3dbSMatt Brown int i; 91e66ca3dbSMatt Brown 92e66ca3dbSMatt Brown if (!cpu_has_feature(CPU_FTR_ARCH_300)) 93e66ca3dbSMatt Brown return -ENODEV; 94e66ca3dbSMatt Brown 95e66ca3dbSMatt Brown for (i = 0; i < 10; i++) { 96e66ca3dbSMatt Brown if (powernv_get_random_darn(&val)) { 97e66ca3dbSMatt Brown ppc_md.get_random_seed = powernv_get_random_darn; 98e66ca3dbSMatt Brown return 0; 99e66ca3dbSMatt Brown } 100e66ca3dbSMatt Brown } 101e66ca3dbSMatt Brown return -EIO; 102e66ca3dbSMatt Brown } 103e66ca3dbSMatt Brown 104a4da0d50SMichael Ellerman int powernv_get_random_long(unsigned long *v) 105a4da0d50SMichael Ellerman { 106a4da0d50SMichael Ellerman struct powernv_rng *rng; 107a4da0d50SMichael Ellerman 108a4da0d50SMichael Ellerman rng = get_cpu_var(powernv_rng); 109a4da0d50SMichael Ellerman 110a4da0d50SMichael Ellerman *v = rng_whiten(rng, in_be64(rng->regs)); 111a4da0d50SMichael Ellerman 112a4da0d50SMichael Ellerman put_cpu_var(rng); 113a4da0d50SMichael Ellerman 114a4da0d50SMichael Ellerman return 1; 115a4da0d50SMichael Ellerman } 116a4da0d50SMichael Ellerman EXPORT_SYMBOL_GPL(powernv_get_random_long); 117a4da0d50SMichael Ellerman 118a4da0d50SMichael Ellerman static __init void rng_init_per_cpu(struct powernv_rng *rng, 119a4da0d50SMichael Ellerman struct device_node *dn) 120a4da0d50SMichael Ellerman { 121a4da0d50SMichael Ellerman int chip_id, cpu; 122a4da0d50SMichael Ellerman 123a4da0d50SMichael Ellerman chip_id = of_get_ibm_chip_id(dn); 124a4da0d50SMichael Ellerman if (chip_id == -1) 125b7c670d6SRob Herring pr_warn("No ibm,chip-id found for %pOF.\n", dn); 126a4da0d50SMichael Ellerman 127a4da0d50SMichael Ellerman for_each_possible_cpu(cpu) { 128a4da0d50SMichael Ellerman if (per_cpu(powernv_rng, cpu) == NULL || 129a4da0d50SMichael Ellerman cpu_to_chip_id(cpu) == chip_id) { 130a4da0d50SMichael Ellerman per_cpu(powernv_rng, cpu) = rng; 131a4da0d50SMichael Ellerman } 132a4da0d50SMichael Ellerman } 133a4da0d50SMichael Ellerman } 134a4da0d50SMichael Ellerman 135a4da0d50SMichael Ellerman static __init int rng_create(struct device_node *dn) 136a4da0d50SMichael Ellerman { 137a4da0d50SMichael Ellerman struct powernv_rng *rng; 138e928e9cbSMichael Ellerman struct resource res; 139a4da0d50SMichael Ellerman unsigned long val; 140a4da0d50SMichael Ellerman 141a4da0d50SMichael Ellerman rng = kzalloc(sizeof(*rng), GFP_KERNEL); 142a4da0d50SMichael Ellerman if (!rng) 143a4da0d50SMichael Ellerman return -ENOMEM; 144a4da0d50SMichael Ellerman 145e928e9cbSMichael Ellerman if (of_address_to_resource(dn, 0, &res)) { 146e928e9cbSMichael Ellerman kfree(rng); 147e928e9cbSMichael Ellerman return -ENXIO; 148e928e9cbSMichael Ellerman } 149e928e9cbSMichael Ellerman 150e928e9cbSMichael Ellerman rng->regs_real = (void __iomem *)res.start; 151e928e9cbSMichael Ellerman 152a4da0d50SMichael Ellerman rng->regs = of_iomap(dn, 0); 153a4da0d50SMichael Ellerman if (!rng->regs) { 154a4da0d50SMichael Ellerman kfree(rng); 155a4da0d50SMichael Ellerman return -ENXIO; 156a4da0d50SMichael Ellerman } 157a4da0d50SMichael Ellerman 158a4da0d50SMichael Ellerman val = in_be64(rng->regs); 159a4da0d50SMichael Ellerman rng->mask = val; 160a4da0d50SMichael Ellerman 161a4da0d50SMichael Ellerman rng_init_per_cpu(rng, dn); 162a4da0d50SMichael Ellerman 16301c9348cSPaul Mackerras ppc_md.get_random_seed = powernv_get_random_long; 164a4da0d50SMichael Ellerman 165a4da0d50SMichael Ellerman return 0; 166a4da0d50SMichael Ellerman } 167a4da0d50SMichael Ellerman 168*f3eac426SJason A. Donenfeld static int __init pnv_get_random_long_early(unsigned long *v) 169a4da0d50SMichael Ellerman { 170a4da0d50SMichael Ellerman struct device_node *dn; 171*f3eac426SJason A. Donenfeld 172*f3eac426SJason A. Donenfeld if (!slab_is_available()) 173*f3eac426SJason A. Donenfeld return 0; 174*f3eac426SJason A. Donenfeld 175*f3eac426SJason A. Donenfeld if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early, 176*f3eac426SJason A. Donenfeld NULL) != pnv_get_random_long_early) 177*f3eac426SJason A. Donenfeld return 0; 178a4da0d50SMichael Ellerman 179a4da0d50SMichael Ellerman for_each_compatible_node(dn, NULL, "ibm,power-rng") { 180*f3eac426SJason A. Donenfeld if (rng_create(dn)) 181a4da0d50SMichael Ellerman continue; 182a4da0d50SMichael Ellerman /* Create devices for hwrng driver */ 183a4da0d50SMichael Ellerman of_platform_device_create(dn, NULL, NULL); 184a4da0d50SMichael Ellerman } 185a4da0d50SMichael Ellerman 186*f3eac426SJason A. Donenfeld if (!ppc_md.get_random_seed) 187*f3eac426SJason A. Donenfeld return 0; 188*f3eac426SJason A. Donenfeld return ppc_md.get_random_seed(v); 189*f3eac426SJason A. Donenfeld } 190e66ca3dbSMatt Brown 191*f3eac426SJason A. Donenfeld void __init pnv_rng_init(void) 192*f3eac426SJason A. Donenfeld { 193*f3eac426SJason A. Donenfeld struct device_node *dn; 194*f3eac426SJason A. Donenfeld 195*f3eac426SJason A. Donenfeld /* Prefer darn over the rest. */ 196*f3eac426SJason A. Donenfeld if (!initialise_darn()) 197*f3eac426SJason A. Donenfeld return; 198*f3eac426SJason A. Donenfeld 199*f3eac426SJason A. Donenfeld dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng"); 200*f3eac426SJason A. Donenfeld if (dn) 201*f3eac426SJason A. Donenfeld ppc_md.get_random_seed = pnv_get_random_long_early; 202*f3eac426SJason A. Donenfeld 203*f3eac426SJason A. Donenfeld of_node_put(dn); 204*f3eac426SJason A. Donenfeld } 205*f3eac426SJason A. Donenfeld 206*f3eac426SJason A. Donenfeld static int __init pnv_rng_late_init(void) 207*f3eac426SJason A. Donenfeld { 208*f3eac426SJason A. Donenfeld unsigned long v; 209*f3eac426SJason A. Donenfeld /* In case it wasn't called during init for some other reason. */ 210*f3eac426SJason A. Donenfeld if (ppc_md.get_random_seed == pnv_get_random_long_early) 211*f3eac426SJason A. Donenfeld pnv_get_random_long_early(&v); 212a4da0d50SMichael Ellerman return 0; 213a4da0d50SMichael Ellerman } 214*f3eac426SJason A. Donenfeld machine_subsys_initcall(powernv, pnv_rng_late_init); 215