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> 20f3eac426SJason 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 32a4da0d50SMichael Ellerman static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val) 33a4da0d50SMichael Ellerman { 34a4da0d50SMichael Ellerman unsigned long parity; 35a4da0d50SMichael Ellerman 36a4da0d50SMichael Ellerman /* Calculate the parity of the value */ 378667d0d6SAnders Roxell asm (".machine push; \ 388667d0d6SAnders Roxell .machine power7; \ 398667d0d6SAnders Roxell popcntd %0,%1; \ 408667d0d6SAnders Roxell .machine pop;" 418667d0d6SAnders Roxell : "=r" (parity) : "r" (val)); 42a4da0d50SMichael Ellerman 43a4da0d50SMichael Ellerman /* xor our value with the previous mask */ 44a4da0d50SMichael Ellerman val ^= rng->mask; 45a4da0d50SMichael Ellerman 46a4da0d50SMichael Ellerman /* update the mask based on the parity of this value */ 47a4da0d50SMichael Ellerman rng->mask = (rng->mask << 1) | (parity & 1); 48a4da0d50SMichael Ellerman 49a4da0d50SMichael Ellerman return val; 50a4da0d50SMichael Ellerman } 51a4da0d50SMichael Ellerman 523b70464aSOliver O'Halloran static int powernv_get_random_darn(unsigned long *v) 53e66ca3dbSMatt Brown { 54e66ca3dbSMatt Brown unsigned long val; 55e66ca3dbSMatt Brown 56e66ca3dbSMatt Brown /* Using DARN with L=1 - 64-bit conditioned random number */ 57e66ca3dbSMatt Brown asm volatile(PPC_DARN(%0, 1) : "=r"(val)); 58e66ca3dbSMatt Brown 59e66ca3dbSMatt Brown if (val == DARN_ERR) 60e66ca3dbSMatt Brown return 0; 61e66ca3dbSMatt Brown 62e66ca3dbSMatt Brown *v = val; 63e66ca3dbSMatt Brown 64e66ca3dbSMatt Brown return 1; 65e66ca3dbSMatt Brown } 66e66ca3dbSMatt Brown 67e5913db1SNick Child static int __init initialise_darn(void) 68e66ca3dbSMatt Brown { 69e66ca3dbSMatt Brown unsigned long val; 70e66ca3dbSMatt Brown int i; 71e66ca3dbSMatt Brown 72e66ca3dbSMatt Brown if (!cpu_has_feature(CPU_FTR_ARCH_300)) 73e66ca3dbSMatt Brown return -ENODEV; 74e66ca3dbSMatt Brown 75e66ca3dbSMatt Brown for (i = 0; i < 10; i++) { 76e66ca3dbSMatt Brown if (powernv_get_random_darn(&val)) { 77e66ca3dbSMatt Brown ppc_md.get_random_seed = powernv_get_random_darn; 78e66ca3dbSMatt Brown return 0; 79e66ca3dbSMatt Brown } 80e66ca3dbSMatt Brown } 81e66ca3dbSMatt Brown return -EIO; 82e66ca3dbSMatt Brown } 83e66ca3dbSMatt Brown 84a4da0d50SMichael Ellerman int powernv_get_random_long(unsigned long *v) 85a4da0d50SMichael Ellerman { 86a4da0d50SMichael Ellerman struct powernv_rng *rng; 87a4da0d50SMichael Ellerman 88*7ef3d06fSJason A. Donenfeld if (mfmsr() & MSR_DR) { 89a4da0d50SMichael Ellerman rng = get_cpu_var(powernv_rng); 90a4da0d50SMichael Ellerman *v = rng_whiten(rng, in_be64(rng->regs)); 91a4da0d50SMichael Ellerman put_cpu_var(rng); 92*7ef3d06fSJason A. Donenfeld } else { 93*7ef3d06fSJason A. Donenfeld rng = raw_cpu_read(powernv_rng); 94*7ef3d06fSJason A. Donenfeld *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real)); 95*7ef3d06fSJason A. Donenfeld } 96a4da0d50SMichael Ellerman return 1; 97a4da0d50SMichael Ellerman } 98a4da0d50SMichael Ellerman EXPORT_SYMBOL_GPL(powernv_get_random_long); 99a4da0d50SMichael Ellerman 100a4da0d50SMichael Ellerman static __init void rng_init_per_cpu(struct powernv_rng *rng, 101a4da0d50SMichael Ellerman struct device_node *dn) 102a4da0d50SMichael Ellerman { 103a4da0d50SMichael Ellerman int chip_id, cpu; 104a4da0d50SMichael Ellerman 105a4da0d50SMichael Ellerman chip_id = of_get_ibm_chip_id(dn); 106a4da0d50SMichael Ellerman if (chip_id == -1) 107b7c670d6SRob Herring pr_warn("No ibm,chip-id found for %pOF.\n", dn); 108a4da0d50SMichael Ellerman 109a4da0d50SMichael Ellerman for_each_possible_cpu(cpu) { 110a4da0d50SMichael Ellerman if (per_cpu(powernv_rng, cpu) == NULL || 111a4da0d50SMichael Ellerman cpu_to_chip_id(cpu) == chip_id) { 112a4da0d50SMichael Ellerman per_cpu(powernv_rng, cpu) = rng; 113a4da0d50SMichael Ellerman } 114a4da0d50SMichael Ellerman } 115a4da0d50SMichael Ellerman } 116a4da0d50SMichael Ellerman 117a4da0d50SMichael Ellerman static __init int rng_create(struct device_node *dn) 118a4da0d50SMichael Ellerman { 119a4da0d50SMichael Ellerman struct powernv_rng *rng; 120e928e9cbSMichael Ellerman struct resource res; 121a4da0d50SMichael Ellerman unsigned long val; 122a4da0d50SMichael Ellerman 123a4da0d50SMichael Ellerman rng = kzalloc(sizeof(*rng), GFP_KERNEL); 124a4da0d50SMichael Ellerman if (!rng) 125a4da0d50SMichael Ellerman return -ENOMEM; 126a4da0d50SMichael Ellerman 127e928e9cbSMichael Ellerman if (of_address_to_resource(dn, 0, &res)) { 128e928e9cbSMichael Ellerman kfree(rng); 129e928e9cbSMichael Ellerman return -ENXIO; 130e928e9cbSMichael Ellerman } 131e928e9cbSMichael Ellerman 132e928e9cbSMichael Ellerman rng->regs_real = (void __iomem *)res.start; 133e928e9cbSMichael Ellerman 134a4da0d50SMichael Ellerman rng->regs = of_iomap(dn, 0); 135a4da0d50SMichael Ellerman if (!rng->regs) { 136a4da0d50SMichael Ellerman kfree(rng); 137a4da0d50SMichael Ellerman return -ENXIO; 138a4da0d50SMichael Ellerman } 139a4da0d50SMichael Ellerman 140a4da0d50SMichael Ellerman val = in_be64(rng->regs); 141a4da0d50SMichael Ellerman rng->mask = val; 142a4da0d50SMichael Ellerman 143a4da0d50SMichael Ellerman rng_init_per_cpu(rng, dn); 144a4da0d50SMichael Ellerman 14501c9348cSPaul Mackerras ppc_md.get_random_seed = powernv_get_random_long; 146a4da0d50SMichael Ellerman 147a4da0d50SMichael Ellerman return 0; 148a4da0d50SMichael Ellerman } 149a4da0d50SMichael Ellerman 150f3eac426SJason A. Donenfeld static int __init pnv_get_random_long_early(unsigned long *v) 151a4da0d50SMichael Ellerman { 152a4da0d50SMichael Ellerman struct device_node *dn; 153f3eac426SJason A. Donenfeld 154f3eac426SJason A. Donenfeld if (!slab_is_available()) 155f3eac426SJason A. Donenfeld return 0; 156f3eac426SJason A. Donenfeld 157f3eac426SJason A. Donenfeld if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early, 158f3eac426SJason A. Donenfeld NULL) != pnv_get_random_long_early) 159f3eac426SJason A. Donenfeld return 0; 160a4da0d50SMichael Ellerman 16188750282SJason A. Donenfeld for_each_compatible_node(dn, NULL, "ibm,power-rng") 16288750282SJason A. Donenfeld rng_create(dn); 163a4da0d50SMichael Ellerman 164f3eac426SJason A. Donenfeld if (!ppc_md.get_random_seed) 165f3eac426SJason A. Donenfeld return 0; 166f3eac426SJason A. Donenfeld return ppc_md.get_random_seed(v); 167f3eac426SJason A. Donenfeld } 168e66ca3dbSMatt Brown 169f3eac426SJason A. Donenfeld void __init pnv_rng_init(void) 170f3eac426SJason A. Donenfeld { 171f3eac426SJason A. Donenfeld struct device_node *dn; 172f3eac426SJason A. Donenfeld 173f3eac426SJason A. Donenfeld /* Prefer darn over the rest. */ 174f3eac426SJason A. Donenfeld if (!initialise_darn()) 175f3eac426SJason A. Donenfeld return; 176f3eac426SJason A. Donenfeld 177f3eac426SJason A. Donenfeld dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng"); 178f3eac426SJason A. Donenfeld if (dn) 179f3eac426SJason A. Donenfeld ppc_md.get_random_seed = pnv_get_random_long_early; 180f3eac426SJason A. Donenfeld 181f3eac426SJason A. Donenfeld of_node_put(dn); 182f3eac426SJason A. Donenfeld } 183f3eac426SJason A. Donenfeld 184f3eac426SJason A. Donenfeld static int __init pnv_rng_late_init(void) 185f3eac426SJason A. Donenfeld { 18688750282SJason A. Donenfeld struct device_node *dn; 187f3eac426SJason A. Donenfeld unsigned long v; 18888750282SJason A. Donenfeld 189f3eac426SJason A. Donenfeld /* In case it wasn't called during init for some other reason. */ 190f3eac426SJason A. Donenfeld if (ppc_md.get_random_seed == pnv_get_random_long_early) 191f3eac426SJason A. Donenfeld pnv_get_random_long_early(&v); 19288750282SJason A. Donenfeld 19388750282SJason A. Donenfeld if (ppc_md.get_random_seed == powernv_get_random_long) { 19488750282SJason A. Donenfeld for_each_compatible_node(dn, NULL, "ibm,power-rng") 19588750282SJason A. Donenfeld of_platform_device_create(dn, NULL, NULL); 19688750282SJason A. Donenfeld } 19788750282SJason A. Donenfeld 198a4da0d50SMichael Ellerman return 0; 199a4da0d50SMichael Ellerman } 200f3eac426SJason A. Donenfeld machine_subsys_initcall(powernv, pnv_rng_late_init); 201