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 /* Extended TRNG Read and Status Registers */
27 #define RNM_PF_TRNG_DAT		0x1000
28 #define RNM_PF_TRNG_RES		0x1008
29 
30 struct cn10k_rng {
31 	void __iomem *reg_base;
32 	struct hwrng ops;
33 	struct pci_dev *pdev;
34 	/* Octeon CN10K-A A0/A1, CNF10K-A A0/A1 and CNF10K-B A0/B0
35 	 * does not support extended TRNG registers
36 	 */
37 	bool extended_trng_regs;
38 };
39 
40 #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE     0xc2000b0f
41 
42 #define PCI_SUBSYS_DEVID_CN10K_A_RNG	0xB900
43 #define PCI_SUBSYS_DEVID_CNF10K_A_RNG	0xBA00
44 #define PCI_SUBSYS_DEVID_CNF10K_B_RNG	0xBC00
45 
46 static bool cn10k_is_extended_trng_regs_supported(struct pci_dev *pdev)
47 {
48 	/* CN10K-A A0/A1 */
49 	if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_RNG) &&
50 	    (!pdev->revision || (pdev->revision & 0xff) == 0x50 ||
51 	     (pdev->revision & 0xff) == 0x51))
52 		return false;
53 
54 	/* CNF10K-A A0 */
55 	if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_RNG) &&
56 	    (!pdev->revision || (pdev->revision & 0xff) == 0x60 ||
57 	     (pdev->revision & 0xff) == 0x61))
58 		return false;
59 
60 	/* CNF10K-B A0/B0 */
61 	if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_B_RNG) &&
62 	    (!pdev->revision || (pdev->revision & 0xff) == 0x70 ||
63 	     (pdev->revision & 0xff) == 0x74))
64 		return false;
65 
66 	return true;
67 }
68 
69 static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
70 {
71 	struct arm_smccc_res res;
72 
73 	/* Send SMC service call to reset EBG health state */
74 	arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res);
75 	return res.a0;
76 }
77 
78 static int check_rng_health(struct cn10k_rng *rng)
79 {
80 	u64 status;
81 	unsigned long err;
82 
83 	/* Skip checking health */
84 	if (!rng->reg_base)
85 		return -ENODEV;
86 
87 	status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
88 	if (status & BIT_ULL(20)) {
89 		err = reset_rng_health_state(rng);
90 		if (err) {
91 			dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
92 					status);
93 			dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
94 					err);
95 			return -EIO;
96 		}
97 	}
98 	return 0;
99 }
100 
101 /* Returns true when valid data available otherwise return false */
102 static bool cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
103 {
104 	u16 retry_count = 0;
105 	u64 upper, lower;
106 	u64 status;
107 
108 	if (rng->extended_trng_regs) {
109 		do {
110 			*value = readq(rng->reg_base + RNM_PF_TRNG_DAT);
111 			if (*value)
112 				return true;
113 			status = readq(rng->reg_base + RNM_PF_TRNG_RES);
114 			if (!status && (retry_count++ > 0x1000))
115 				return false;
116 		} while (!status);
117 	}
118 
119 	*value = readq(rng->reg_base + RNM_PF_RANDOM);
120 
121 	/* HW can run out of entropy if large amount random data is read in
122 	 * quick succession. Zeros may not be real random data from HW.
123 	 */
124 	if (!*value) {
125 		upper = readq(rng->reg_base + RNM_PF_RANDOM);
126 		lower = readq(rng->reg_base + RNM_PF_RANDOM);
127 		while (!(upper & 0x00000000FFFFFFFFULL))
128 			upper = readq(rng->reg_base + RNM_PF_RANDOM);
129 		while (!(lower & 0xFFFFFFFF00000000ULL))
130 			lower = readq(rng->reg_base + RNM_PF_RANDOM);
131 
132 		*value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
133 	}
134 	return true;
135 }
136 
137 static int cn10k_rng_read(struct hwrng *hwrng, void *data,
138 			  size_t max, bool wait)
139 {
140 	struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
141 	unsigned int size;
142 	u8 *pos = data;
143 	int err = 0;
144 	u64 value;
145 
146 	err = check_rng_health(rng);
147 	if (err)
148 		return err;
149 
150 	size = max;
151 
152 	while (size >= 8) {
153 		if (!cn10k_read_trng(rng, &value))
154 			goto out;
155 
156 		*((u64 *)pos) = value;
157 		size -= 8;
158 		pos += 8;
159 	}
160 
161 	if (size > 0) {
162 		if (!cn10k_read_trng(rng, &value))
163 			goto out;
164 
165 		while (size > 0) {
166 			*pos = (u8)value;
167 			value >>= 8;
168 			size--;
169 			pos++;
170 		}
171 	}
172 
173 out:
174 	return max - size;
175 }
176 
177 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
178 {
179 	struct	cn10k_rng *rng;
180 	int	err;
181 
182 	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
183 	if (!rng)
184 		return -ENOMEM;
185 
186 	rng->pdev = pdev;
187 	pci_set_drvdata(pdev, rng);
188 
189 	rng->reg_base = pcim_iomap(pdev, 0, 0);
190 	if (!rng->reg_base)
191 		return dev_err_probe(&pdev->dev, -ENOMEM, "Error while mapping CSRs, exiting\n");
192 
193 	rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
194 				       "cn10k-rng-%s", dev_name(&pdev->dev));
195 	if (!rng->ops.name)
196 		return -ENOMEM;
197 
198 	rng->ops.read = cn10k_rng_read;
199 	rng->ops.priv = (unsigned long)rng;
200 
201 	rng->extended_trng_regs = cn10k_is_extended_trng_regs_supported(pdev);
202 
203 	reset_rng_health_state(rng);
204 
205 	err = devm_hwrng_register(&pdev->dev, &rng->ops);
206 	if (err)
207 		return dev_err_probe(&pdev->dev, err, "Could not register hwrng device.\n");
208 
209 	return 0;
210 }
211 
212 static const struct pci_device_id cn10k_rng_id_table[] = {
213 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
214 	{0,},
215 };
216 
217 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
218 
219 static struct pci_driver cn10k_rng_driver = {
220 	.name		= "cn10k_rng",
221 	.id_table	= cn10k_rng_id_table,
222 	.probe		= cn10k_rng_probe,
223 };
224 
225 module_pci_driver(cn10k_rng_driver);
226 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
227 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
228 MODULE_LICENSE("GPL v2");
229