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> 20a4da0d50SMichael Ellerman 21e66ca3dbSMatt Brown #define DARN_ERR 0xFFFFFFFFFFFFFFFFul 22a4da0d50SMichael Ellerman 23a4da0d50SMichael Ellerman struct powernv_rng { 24a4da0d50SMichael Ellerman void __iomem *regs; 25e928e9cbSMichael Ellerman void __iomem *regs_real; 26a4da0d50SMichael Ellerman unsigned long mask; 27a4da0d50SMichael Ellerman }; 28a4da0d50SMichael Ellerman 29a4da0d50SMichael Ellerman static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); 30a4da0d50SMichael Ellerman 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 */ 46a4da0d50SMichael Ellerman asm ("popcntd %0,%1" : "=r" (parity) : "r" (val)); 47a4da0d50SMichael Ellerman 48a4da0d50SMichael Ellerman /* xor our value with the previous mask */ 49a4da0d50SMichael Ellerman val ^= rng->mask; 50a4da0d50SMichael Ellerman 51a4da0d50SMichael Ellerman /* update the mask based on the parity of this value */ 52a4da0d50SMichael Ellerman rng->mask = (rng->mask << 1) | (parity & 1); 53a4da0d50SMichael Ellerman 54a4da0d50SMichael Ellerman return val; 55a4da0d50SMichael Ellerman } 56a4da0d50SMichael Ellerman 57e928e9cbSMichael Ellerman int powernv_get_random_real_mode(unsigned long *v) 58e928e9cbSMichael Ellerman { 59e928e9cbSMichael Ellerman struct powernv_rng *rng; 60e928e9cbSMichael Ellerman 61e928e9cbSMichael Ellerman rng = raw_cpu_read(powernv_rng); 62e928e9cbSMichael Ellerman 63d381d7caSBenjamin Herrenschmidt *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real)); 64e928e9cbSMichael Ellerman 65e928e9cbSMichael Ellerman return 1; 66e928e9cbSMichael Ellerman } 67e928e9cbSMichael Ellerman 683b70464aSOliver O'Halloran static int powernv_get_random_darn(unsigned long *v) 69e66ca3dbSMatt Brown { 70e66ca3dbSMatt Brown unsigned long val; 71e66ca3dbSMatt Brown 72e66ca3dbSMatt Brown /* Using DARN with L=1 - 64-bit conditioned random number */ 73e66ca3dbSMatt Brown asm volatile(PPC_DARN(%0, 1) : "=r"(val)); 74e66ca3dbSMatt Brown 75e66ca3dbSMatt Brown if (val == DARN_ERR) 76e66ca3dbSMatt Brown return 0; 77e66ca3dbSMatt Brown 78e66ca3dbSMatt Brown *v = val; 79e66ca3dbSMatt Brown 80e66ca3dbSMatt Brown return 1; 81e66ca3dbSMatt Brown } 82e66ca3dbSMatt Brown 83*e5913db1SNick Child static int __init initialise_darn(void) 84e66ca3dbSMatt Brown { 85e66ca3dbSMatt Brown unsigned long val; 86e66ca3dbSMatt Brown int i; 87e66ca3dbSMatt Brown 88e66ca3dbSMatt Brown if (!cpu_has_feature(CPU_FTR_ARCH_300)) 89e66ca3dbSMatt Brown return -ENODEV; 90e66ca3dbSMatt Brown 91e66ca3dbSMatt Brown for (i = 0; i < 10; i++) { 92e66ca3dbSMatt Brown if (powernv_get_random_darn(&val)) { 93e66ca3dbSMatt Brown ppc_md.get_random_seed = powernv_get_random_darn; 94e66ca3dbSMatt Brown return 0; 95e66ca3dbSMatt Brown } 96e66ca3dbSMatt Brown } 97e66ca3dbSMatt Brown 98e66ca3dbSMatt Brown pr_warn("Unable to use DARN for get_random_seed()\n"); 99e66ca3dbSMatt Brown 100e66ca3dbSMatt Brown return -EIO; 101e66ca3dbSMatt Brown } 102e66ca3dbSMatt Brown 103a4da0d50SMichael Ellerman int powernv_get_random_long(unsigned long *v) 104a4da0d50SMichael Ellerman { 105a4da0d50SMichael Ellerman struct powernv_rng *rng; 106a4da0d50SMichael Ellerman 107a4da0d50SMichael Ellerman rng = get_cpu_var(powernv_rng); 108a4da0d50SMichael Ellerman 109a4da0d50SMichael Ellerman *v = rng_whiten(rng, in_be64(rng->regs)); 110a4da0d50SMichael Ellerman 111a4da0d50SMichael Ellerman put_cpu_var(rng); 112a4da0d50SMichael Ellerman 113a4da0d50SMichael Ellerman return 1; 114a4da0d50SMichael Ellerman } 115a4da0d50SMichael Ellerman EXPORT_SYMBOL_GPL(powernv_get_random_long); 116a4da0d50SMichael Ellerman 117a4da0d50SMichael Ellerman static __init void rng_init_per_cpu(struct powernv_rng *rng, 118a4da0d50SMichael Ellerman struct device_node *dn) 119a4da0d50SMichael Ellerman { 120a4da0d50SMichael Ellerman int chip_id, cpu; 121a4da0d50SMichael Ellerman 122a4da0d50SMichael Ellerman chip_id = of_get_ibm_chip_id(dn); 123a4da0d50SMichael Ellerman if (chip_id == -1) 124b7c670d6SRob Herring pr_warn("No ibm,chip-id found for %pOF.\n", dn); 125a4da0d50SMichael Ellerman 126a4da0d50SMichael Ellerman for_each_possible_cpu(cpu) { 127a4da0d50SMichael Ellerman if (per_cpu(powernv_rng, cpu) == NULL || 128a4da0d50SMichael Ellerman cpu_to_chip_id(cpu) == chip_id) { 129a4da0d50SMichael Ellerman per_cpu(powernv_rng, cpu) = rng; 130a4da0d50SMichael Ellerman } 131a4da0d50SMichael Ellerman } 132a4da0d50SMichael Ellerman } 133a4da0d50SMichael Ellerman 134a4da0d50SMichael Ellerman static __init int rng_create(struct device_node *dn) 135a4da0d50SMichael Ellerman { 136a4da0d50SMichael Ellerman struct powernv_rng *rng; 137e928e9cbSMichael Ellerman struct resource res; 138a4da0d50SMichael Ellerman unsigned long val; 139a4da0d50SMichael Ellerman 140a4da0d50SMichael Ellerman rng = kzalloc(sizeof(*rng), GFP_KERNEL); 141a4da0d50SMichael Ellerman if (!rng) 142a4da0d50SMichael Ellerman return -ENOMEM; 143a4da0d50SMichael Ellerman 144e928e9cbSMichael Ellerman if (of_address_to_resource(dn, 0, &res)) { 145e928e9cbSMichael Ellerman kfree(rng); 146e928e9cbSMichael Ellerman return -ENXIO; 147e928e9cbSMichael Ellerman } 148e928e9cbSMichael Ellerman 149e928e9cbSMichael Ellerman rng->regs_real = (void __iomem *)res.start; 150e928e9cbSMichael Ellerman 151a4da0d50SMichael Ellerman rng->regs = of_iomap(dn, 0); 152a4da0d50SMichael Ellerman if (!rng->regs) { 153a4da0d50SMichael Ellerman kfree(rng); 154a4da0d50SMichael Ellerman return -ENXIO; 155a4da0d50SMichael Ellerman } 156a4da0d50SMichael Ellerman 157a4da0d50SMichael Ellerman val = in_be64(rng->regs); 158a4da0d50SMichael Ellerman rng->mask = val; 159a4da0d50SMichael Ellerman 160a4da0d50SMichael Ellerman rng_init_per_cpu(rng, dn); 161a4da0d50SMichael Ellerman 162a4da0d50SMichael Ellerman pr_info_once("Registering arch random hook.\n"); 163a4da0d50SMichael Ellerman 16401c9348cSPaul Mackerras ppc_md.get_random_seed = powernv_get_random_long; 165a4da0d50SMichael Ellerman 166a4da0d50SMichael Ellerman return 0; 167a4da0d50SMichael Ellerman } 168a4da0d50SMichael Ellerman 169a4da0d50SMichael Ellerman static __init int rng_init(void) 170a4da0d50SMichael Ellerman { 171a4da0d50SMichael Ellerman struct device_node *dn; 172a4da0d50SMichael Ellerman int rc; 173a4da0d50SMichael Ellerman 174a4da0d50SMichael Ellerman for_each_compatible_node(dn, NULL, "ibm,power-rng") { 175a4da0d50SMichael Ellerman rc = rng_create(dn); 176a4da0d50SMichael Ellerman if (rc) { 177b7c670d6SRob Herring pr_err("Failed creating rng for %pOF (%d).\n", 178b7c670d6SRob Herring dn, rc); 179a4da0d50SMichael Ellerman continue; 180a4da0d50SMichael Ellerman } 181a4da0d50SMichael Ellerman 182a4da0d50SMichael Ellerman /* Create devices for hwrng driver */ 183a4da0d50SMichael Ellerman of_platform_device_create(dn, NULL, NULL); 184a4da0d50SMichael Ellerman } 185a4da0d50SMichael Ellerman 186e66ca3dbSMatt Brown initialise_darn(); 187e66ca3dbSMatt Brown 188a4da0d50SMichael Ellerman return 0; 189a4da0d50SMichael Ellerman } 190b14726c5SMichael Ellerman machine_subsys_initcall(powernv, rng_init); 191