xref: /linux/drivers/char/hw_random/cn10k-rng.c (revision b5bee6ced21ca98389000b7017dd41b0cc37fa50)
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 unsigned long 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 	return res.a0;
41 }
42 
43 static int check_rng_health(struct cn10k_rng *rng)
44 {
45 	u64 status;
46 	unsigned long err;
47 
48 	/* Skip checking health */
49 	if (!rng->reg_base)
50 		return -ENODEV;
51 
52 	status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
53 	if (status & BIT_ULL(20)) {
54 		err = reset_rng_health_state(rng);
55 		if (err) {
56 			dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
57 					status);
58 			dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
59 					err);
60 			return -EIO;
61 		}
62 	}
63 	return 0;
64 }
65 
66 static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
67 {
68 	u64 upper, lower;
69 
70 	*value = readq(rng->reg_base + RNM_PF_RANDOM);
71 
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.
74 	 */
75 	if (!*value) {
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);
82 
83 		*value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
84 	}
85 }
86 
87 static int cn10k_rng_read(struct hwrng *hwrng, void *data,
88 			  size_t max, bool wait)
89 {
90 	struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
91 	unsigned int size;
92 	u8 *pos = data;
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 *)pos) = value;
106 		size -= 8;
107 		pos += 8;
108 	}
109 
110 	if (size > 0) {
111 		cn10k_read_trng(rng, &value);
112 
113 		while (size > 0) {
114 			*pos = (u8)value;
115 			value >>= 8;
116 			size--;
117 			pos++;
118 		}
119 	}
120 
121 	return max - size;
122 }
123 
124 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
125 {
126 	struct	cn10k_rng *rng;
127 	int	err;
128 
129 	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
130 	if (!rng)
131 		return -ENOMEM;
132 
133 	rng->pdev = pdev;
134 	pci_set_drvdata(pdev, rng);
135 
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");
139 		return -ENOMEM;
140 	}
141 
142 	rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
143 				       "cn10k-rng-%s", dev_name(&pdev->dev));
144 	if (!rng->ops.name)
145 		return -ENOMEM;
146 
147 	rng->ops.read    = cn10k_rng_read;
148 	rng->ops.quality = 1000;
149 	rng->ops.priv = (unsigned long)rng;
150 
151 	reset_rng_health_state(rng);
152 
153 	err = devm_hwrng_register(&pdev->dev, &rng->ops);
154 	if (err) {
155 		dev_err(&pdev->dev, "Could not register hwrng device.\n");
156 		return err;
157 	}
158 
159 	return 0;
160 }
161 
162 static void cn10k_rng_remove(struct pci_dev *pdev)
163 {
164 	/* Nothing to do */
165 }
166 
167 static const struct pci_device_id cn10k_rng_id_table[] = {
168 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
169 	{0,},
170 };
171 
172 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
173 
174 static struct pci_driver cn10k_rng_driver = {
175 	.name		= "cn10k_rng",
176 	.id_table	= cn10k_rng_id_table,
177 	.probe		= cn10k_rng_probe,
178 	.remove		= cn10k_rng_remove,
179 };
180 
181 module_pci_driver(cn10k_rng_driver);
182 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
183 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
184 MODULE_LICENSE("GPL v2");
185