]> Git Repo - J-linux.git/blob - drivers/char/hw_random/cn10k-rng.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.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 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.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
161 static void cn10k_rng_remove(struct pci_dev *pdev)
162 {
163         /* Nothing to do */
164 }
165
166 static const struct pci_device_id cn10k_rng_id_table[] = {
167         { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
168         {0,},
169 };
170
171 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
172
173 static 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
180 module_pci_driver(cn10k_rng_driver);
181 MODULE_AUTHOR("Sunil Goutham <[email protected]>");
182 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
183 MODULE_LICENSE("GPL v2");
This page took 0.04044 seconds and 4 git commands to generate.