hwrng: cn10k - Add extended trng register support
[platform/kernel/linux-starfive.git] / drivers / char / hw_random / cn10k-rng.c
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                 dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
192                 return -ENOMEM;
193         }
194
195         rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
196                                        "cn10k-rng-%s", dev_name(&pdev->dev));
197         if (!rng->ops.name)
198                 return -ENOMEM;
199
200         rng->ops.read = cn10k_rng_read;
201         rng->ops.priv = (unsigned long)rng;
202
203         rng->extended_trng_regs = cn10k_is_extended_trng_regs_supported(pdev);
204
205         reset_rng_health_state(rng);
206
207         err = devm_hwrng_register(&pdev->dev, &rng->ops);
208         if (err) {
209                 dev_err(&pdev->dev, "Could not register hwrng device.\n");
210                 return err;
211         }
212
213         return 0;
214 }
215
216 static void cn10k_rng_remove(struct pci_dev *pdev)
217 {
218         /* Nothing to do */
219 }
220
221 static const struct pci_device_id cn10k_rng_id_table[] = {
222         { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
223         {0,},
224 };
225
226 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
227
228 static struct pci_driver cn10k_rng_driver = {
229         .name           = "cn10k_rng",
230         .id_table       = cn10k_rng_id_table,
231         .probe          = cn10k_rng_probe,
232         .remove         = cn10k_rng_remove,
233 };
234
235 module_pci_driver(cn10k_rng_driver);
236 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
237 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
238 MODULE_LICENSE("GPL v2");