1 // SPDX-License-Identifier: GPL-2.0
2 /* Marvell CN10K RVU Hardware Random Number Generator.
4 * Copyright (C) 2021 Marvell.
8 #include <linux/hw_random.h>
10 #include <linux/module.h>
11 #include <linux/pci.h>
12 #include <linux/pci_ids.h>
13 #include <linux/delay.h>
15 #include <linux/arm-smccc.h>
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
27 void __iomem *reg_base;
32 #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f
34 static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
36 struct arm_smccc_res res;
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);
43 static int check_rng_health(struct cn10k_rng *rng)
48 /* Skip checking health */
52 status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
53 if (status & BIT_ULL(20)) {
54 err = reset_rng_health_state(rng);
56 dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
58 dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
66 static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
70 *value = readq(rng->reg_base + RNM_PF_RANDOM);
72 /* HW can run out of entropy if large amount random data is read in
73 * quick succession. Zeros may not be real random data from HW.
76 upper = readq(rng->reg_base + RNM_PF_RANDOM);
77 lower = readq(rng->reg_base + RNM_PF_RANDOM);
78 while (!(upper & 0x00000000FFFFFFFFULL))
79 upper = readq(rng->reg_base + RNM_PF_RANDOM);
80 while (!(lower & 0xFFFFFFFF00000000ULL))
81 lower = readq(rng->reg_base + RNM_PF_RANDOM);
83 *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
87 static int cn10k_rng_read(struct hwrng *hwrng, void *data,
88 size_t max, bool wait)
90 struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
96 err = check_rng_health(rng);
103 cn10k_read_trng(rng, &value);
105 *((u64 *)pos) = value;
111 cn10k_read_trng(rng, &value);
124 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
126 struct cn10k_rng *rng;
129 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
134 pci_set_drvdata(pdev, rng);
136 rng->reg_base = pcim_iomap(pdev, 0, 0);
137 if (!rng->reg_base) {
138 dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
142 rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
143 "cn10k-rng-%s", dev_name(&pdev->dev));
147 rng->ops.read = cn10k_rng_read;
148 rng->ops.priv = (unsigned long)rng;
150 reset_rng_health_state(rng);
152 err = devm_hwrng_register(&pdev->dev, &rng->ops);
154 dev_err(&pdev->dev, "Could not register hwrng device.\n");
161 static void cn10k_rng_remove(struct pci_dev *pdev)
166 static const struct pci_device_id cn10k_rng_id_table[] = {
167 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
171 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
173 static struct pci_driver cn10k_rng_driver = {
175 .id_table = cn10k_rng_id_table,
176 .probe = cn10k_rng_probe,
177 .remove = cn10k_rng_remove,
180 module_pci_driver(cn10k_rng_driver);
182 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
183 MODULE_LICENSE("GPL v2");