1 // SPDX-License-Identifier: GPL-2.0 2 /* Marvell CN10K RVU Hardware Random Number Generator. 3 * 4 * Copyright (C) 2021 Marvell. 5 * 6 */ 7 8 #include <linux/hw_random.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/pci.h> 12 #include <linux/pci_ids.h> 13 #include <linux/delay.h> 14 15 #include <linux/arm-smccc.h> 16 17 /* CSRs */ 18 #define RNM_CTL_STATUS 0x000 19 #define RNM_ENTROPY_STATUS 0x008 20 #define RNM_CONST 0x030 21 #define RNM_EBG_ENT 0x048 22 #define RNM_PF_EBG_HEALTH 0x050 23 #define RNM_PF_RANDOM 0x400 24 #define RNM_TRNG_RESULT 0x408 25 26 struct cn10k_rng { 27 void __iomem *reg_base; 28 struct hwrng ops; 29 struct pci_dev *pdev; 30 }; 31 32 #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f 33 34 static int reset_rng_health_state(struct cn10k_rng *rng) 35 { 36 struct arm_smccc_res res; 37 38 /* Send SMC service call to reset EBG health state */ 39 arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res); 40 if (res.a0 != 0UL) 41 return -EIO; 42 43 return 0; 44 } 45 46 static int check_rng_health(struct cn10k_rng *rng) 47 { 48 u64 status; 49 int err; 50 51 /* Skip checking health */ 52 if (!rng->reg_base) 53 return 0; 54 55 status = readq(rng->reg_base + RNM_PF_EBG_HEALTH); 56 if (status & BIT_ULL(20)) { 57 err = reset_rng_health_state(rng); 58 if (err) { 59 dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n", 60 status); 61 dev_err(&rng->pdev->dev, "HWRNG: error during reset\n"); 62 } 63 } 64 return 0; 65 } 66 67 static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value) 68 { 69 u64 upper, lower; 70 71 *value = readq(rng->reg_base + RNM_PF_RANDOM); 72 73 /* HW can run out of entropy if large amount random data is read in 74 * quick succession. Zeros may not be real random data from HW. 75 */ 76 if (!*value) { 77 upper = readq(rng->reg_base + RNM_PF_RANDOM); 78 lower = readq(rng->reg_base + RNM_PF_RANDOM); 79 while (!(upper & 0x00000000FFFFFFFFULL)) 80 upper = readq(rng->reg_base + RNM_PF_RANDOM); 81 while (!(lower & 0xFFFFFFFF00000000ULL)) 82 lower = readq(rng->reg_base + RNM_PF_RANDOM); 83 84 *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF); 85 } 86 } 87 88 static int cn10k_rng_read(struct hwrng *hwrng, void *data, 89 size_t max, bool wait) 90 { 91 struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv; 92 unsigned int size; 93 int err = 0; 94 u64 value; 95 96 err = check_rng_health(rng); 97 if (err) 98 return err; 99 100 size = max; 101 102 while (size >= 8) { 103 cn10k_read_trng(rng, &value); 104 105 *((u64 *)data) = (u64)value; 106 size -= 8; 107 data += 8; 108 } 109 110 while (size > 0) { 111 cn10k_read_trng(rng, &value); 112 113 *((u8 *)data) = (u8)value; 114 size--; 115 data++; 116 } 117 118 return max - size; 119 } 120 121 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id) 122 { 123 struct cn10k_rng *rng; 124 int err; 125 126 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 127 if (!rng) 128 return -ENOMEM; 129 130 rng->pdev = pdev; 131 pci_set_drvdata(pdev, rng); 132 133 rng->reg_base = pcim_iomap(pdev, 0, 0); 134 if (!rng->reg_base) { 135 dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n"); 136 return -ENOMEM; 137 } 138 139 rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 140 "cn10k-rng-%s", dev_name(&pdev->dev)); 141 if (!rng->ops.name) 142 return -ENOMEM; 143 144 rng->ops.read = cn10k_rng_read; 145 rng->ops.quality = 1000; 146 rng->ops.priv = (unsigned long)rng; 147 148 reset_rng_health_state(rng); 149 150 err = devm_hwrng_register(&pdev->dev, &rng->ops); 151 if (err) { 152 dev_err(&pdev->dev, "Could not register hwrng device.\n"); 153 return err; 154 } 155 156 return 0; 157 } 158 159 static void cn10k_rng_remove(struct pci_dev *pdev) 160 { 161 /* Nothing to do */ 162 } 163 164 static const struct pci_device_id cn10k_rng_id_table[] = { 165 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */ 166 {0,}, 167 }; 168 169 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table); 170 171 static struct pci_driver cn10k_rng_driver = { 172 .name = "cn10k_rng", 173 .id_table = cn10k_rng_id_table, 174 .probe = cn10k_rng_probe, 175 .remove = cn10k_rng_remove, 176 }; 177 178 module_pci_driver(cn10k_rng_driver); 179 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>"); 180 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver"); 181 MODULE_LICENSE("GPL v2"); 182