Loading...
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
30struct 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
46static 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
69static 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
78static 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 */
102static 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
137static 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
173out:
174 return max - size;
175}
176
177static 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
212static const struct pci_device_id cn10k_rng_id_table[] = {
213 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
214 {0,},
215};
216
217MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
218
219static struct pci_driver cn10k_rng_driver = {
220 .name = "cn10k_rng",
221 .id_table = cn10k_rng_id_table,
222 .probe = cn10k_rng_probe,
223};
224
225module_pci_driver(cn10k_rng_driver);
226MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
227MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
228MODULE_LICENSE("GPL v2");
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
26struct 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
34static 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
43static 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
66static 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
87static 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
124static 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.priv = (unsigned long)rng;
149
150 reset_rng_health_state(rng);
151
152 err = devm_hwrng_register(&pdev->dev, &rng->ops);
153 if (err) {
154 dev_err(&pdev->dev, "Could not register hwrng device.\n");
155 return err;
156 }
157
158 return 0;
159}
160
161static void cn10k_rng_remove(struct pci_dev *pdev)
162{
163 /* Nothing to do */
164}
165
166static const struct pci_device_id cn10k_rng_id_table[] = {
167 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
168 {0,},
169};
170
171MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
172
173static struct pci_driver cn10k_rng_driver = {
174 .name = "cn10k_rng",
175 .id_table = cn10k_rng_id_table,
176 .probe = cn10k_rng_probe,
177 .remove = cn10k_rng_remove,
178};
179
180module_pci_driver(cn10k_rng_driver);
181MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
182MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
183MODULE_LICENSE("GPL v2");