1 /* 2 * Copyright 2013, Michael Ellerman, IBM Corporation. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 10 #define pr_fmt(fmt) "powernv-rng: " fmt 11 12 #include <linux/kernel.h> 13 #include <linux/of.h> 14 #include <linux/of_address.h> 15 #include <linux/of_platform.h> 16 #include <linux/slab.h> 17 #include <linux/smp.h> 18 #include <asm/archrandom.h> 19 #include <asm/io.h> 20 #include <asm/prom.h> 21 #include <asm/machdep.h> 22 #include <asm/smp.h> 23 24 25 struct powernv_rng { 26 void __iomem *regs; 27 unsigned long mask; 28 }; 29 30 static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); 31 32 33 static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val) 34 { 35 unsigned long parity; 36 37 /* Calculate the parity of the value */ 38 asm ("popcntd %0,%1" : "=r" (parity) : "r" (val)); 39 40 /* xor our value with the previous mask */ 41 val ^= rng->mask; 42 43 /* update the mask based on the parity of this value */ 44 rng->mask = (rng->mask << 1) | (parity & 1); 45 46 return val; 47 } 48 49 int powernv_get_random_long(unsigned long *v) 50 { 51 struct powernv_rng *rng; 52 53 rng = get_cpu_var(powernv_rng); 54 55 *v = rng_whiten(rng, in_be64(rng->regs)); 56 57 put_cpu_var(rng); 58 59 return 1; 60 } 61 EXPORT_SYMBOL_GPL(powernv_get_random_long); 62 63 static __init void rng_init_per_cpu(struct powernv_rng *rng, 64 struct device_node *dn) 65 { 66 int chip_id, cpu; 67 68 chip_id = of_get_ibm_chip_id(dn); 69 if (chip_id == -1) 70 pr_warn("No ibm,chip-id found for %s.\n", dn->full_name); 71 72 for_each_possible_cpu(cpu) { 73 if (per_cpu(powernv_rng, cpu) == NULL || 74 cpu_to_chip_id(cpu) == chip_id) { 75 per_cpu(powernv_rng, cpu) = rng; 76 } 77 } 78 } 79 80 static __init int rng_create(struct device_node *dn) 81 { 82 struct powernv_rng *rng; 83 unsigned long val; 84 85 rng = kzalloc(sizeof(*rng), GFP_KERNEL); 86 if (!rng) 87 return -ENOMEM; 88 89 rng->regs = of_iomap(dn, 0); 90 if (!rng->regs) { 91 kfree(rng); 92 return -ENXIO; 93 } 94 95 val = in_be64(rng->regs); 96 rng->mask = val; 97 98 rng_init_per_cpu(rng, dn); 99 100 pr_info_once("Registering arch random hook.\n"); 101 102 ppc_md.get_random_long = powernv_get_random_long; 103 104 return 0; 105 } 106 107 static __init int rng_init(void) 108 { 109 struct device_node *dn; 110 int rc; 111 112 for_each_compatible_node(dn, NULL, "ibm,power-rng") { 113 rc = rng_create(dn); 114 if (rc) { 115 pr_err("Failed creating rng for %s (%d).\n", 116 dn->full_name, rc); 117 continue; 118 } 119 120 /* Create devices for hwrng driver */ 121 of_platform_device_create(dn, NULL, NULL); 122 } 123 124 return 0; 125 } 126 subsys_initcall(rng_init); 127