138e9791aSSunil Goutham // SPDX-License-Identifier: GPL-2.0
238e9791aSSunil Goutham /* Marvell CN10K RVU Hardware Random Number Generator.
338e9791aSSunil Goutham *
438e9791aSSunil Goutham * Copyright (C) 2021 Marvell.
538e9791aSSunil Goutham *
638e9791aSSunil Goutham */
738e9791aSSunil Goutham
838e9791aSSunil Goutham #include <linux/hw_random.h>
938e9791aSSunil Goutham #include <linux/io.h>
1038e9791aSSunil Goutham #include <linux/module.h>
1138e9791aSSunil Goutham #include <linux/pci.h>
1238e9791aSSunil Goutham #include <linux/pci_ids.h>
1338e9791aSSunil Goutham #include <linux/delay.h>
1438e9791aSSunil Goutham
1538e9791aSSunil Goutham #include <linux/arm-smccc.h>
1638e9791aSSunil Goutham
1738e9791aSSunil Goutham /* CSRs */
1838e9791aSSunil Goutham #define RNM_CTL_STATUS 0x000
1938e9791aSSunil Goutham #define RNM_ENTROPY_STATUS 0x008
2038e9791aSSunil Goutham #define RNM_CONST 0x030
2138e9791aSSunil Goutham #define RNM_EBG_ENT 0x048
2238e9791aSSunil Goutham #define RNM_PF_EBG_HEALTH 0x050
2338e9791aSSunil Goutham #define RNM_PF_RANDOM 0x400
2438e9791aSSunil Goutham #define RNM_TRNG_RESULT 0x408
2538e9791aSSunil Goutham
26506579e8SBharat Bhushan /* Extended TRNG Read and Status Registers */
27506579e8SBharat Bhushan #define RNM_PF_TRNG_DAT 0x1000
28506579e8SBharat Bhushan #define RNM_PF_TRNG_RES 0x1008
29506579e8SBharat Bhushan
3038e9791aSSunil Goutham struct cn10k_rng {
3138e9791aSSunil Goutham void __iomem *reg_base;
3238e9791aSSunil Goutham struct hwrng ops;
3338e9791aSSunil Goutham struct pci_dev *pdev;
34506579e8SBharat Bhushan /* Octeon CN10K-A A0/A1, CNF10K-A A0/A1 and CNF10K-B A0/B0
35506579e8SBharat Bhushan * does not support extended TRNG registers
36506579e8SBharat Bhushan */
37506579e8SBharat Bhushan bool extended_trng_regs;
3838e9791aSSunil Goutham };
3938e9791aSSunil Goutham
4038e9791aSSunil Goutham #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f
4138e9791aSSunil Goutham
42506579e8SBharat Bhushan #define PCI_SUBSYS_DEVID_CN10K_A_RNG 0xB900
43506579e8SBharat Bhushan #define PCI_SUBSYS_DEVID_CNF10K_A_RNG 0xBA00
44506579e8SBharat Bhushan #define PCI_SUBSYS_DEVID_CNF10K_B_RNG 0xBC00
45506579e8SBharat Bhushan
cn10k_is_extended_trng_regs_supported(struct pci_dev * pdev)46506579e8SBharat Bhushan static bool cn10k_is_extended_trng_regs_supported(struct pci_dev *pdev)
47506579e8SBharat Bhushan {
48506579e8SBharat Bhushan /* CN10K-A A0/A1 */
49506579e8SBharat Bhushan if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_RNG) &&
50506579e8SBharat Bhushan (!pdev->revision || (pdev->revision & 0xff) == 0x50 ||
51506579e8SBharat Bhushan (pdev->revision & 0xff) == 0x51))
52506579e8SBharat Bhushan return false;
53506579e8SBharat Bhushan
54506579e8SBharat Bhushan /* CNF10K-A A0 */
55506579e8SBharat Bhushan if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_RNG) &&
56506579e8SBharat Bhushan (!pdev->revision || (pdev->revision & 0xff) == 0x60 ||
57506579e8SBharat Bhushan (pdev->revision & 0xff) == 0x61))
58506579e8SBharat Bhushan return false;
59506579e8SBharat Bhushan
60506579e8SBharat Bhushan /* CNF10K-B A0/B0 */
61506579e8SBharat Bhushan if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_B_RNG) &&
62506579e8SBharat Bhushan (!pdev->revision || (pdev->revision & 0xff) == 0x70 ||
63506579e8SBharat Bhushan (pdev->revision & 0xff) == 0x74))
64506579e8SBharat Bhushan return false;
65506579e8SBharat Bhushan
66506579e8SBharat Bhushan return true;
67506579e8SBharat Bhushan }
68506579e8SBharat Bhushan
reset_rng_health_state(struct cn10k_rng * rng)6932547a6aSVladis Dronov static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
7038e9791aSSunil Goutham {
7138e9791aSSunil Goutham struct arm_smccc_res res;
7238e9791aSSunil Goutham
7338e9791aSSunil Goutham /* Send SMC service call to reset EBG health state */
7438e9791aSSunil Goutham arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res);
7532547a6aSVladis Dronov return res.a0;
7638e9791aSSunil Goutham }
7738e9791aSSunil Goutham
check_rng_health(struct cn10k_rng * rng)7838e9791aSSunil Goutham static int check_rng_health(struct cn10k_rng *rng)
7938e9791aSSunil Goutham {
8038e9791aSSunil Goutham u64 status;
8132547a6aSVladis Dronov unsigned long err;
8238e9791aSSunil Goutham
8338e9791aSSunil Goutham /* Skip checking health */
8438e9791aSSunil Goutham if (!rng->reg_base)
8532547a6aSVladis Dronov return -ENODEV;
8638e9791aSSunil Goutham
8738e9791aSSunil Goutham status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
8838e9791aSSunil Goutham if (status & BIT_ULL(20)) {
8938e9791aSSunil Goutham err = reset_rng_health_state(rng);
9038e9791aSSunil Goutham if (err) {
9138e9791aSSunil Goutham dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
9238e9791aSSunil Goutham status);
9332547a6aSVladis Dronov dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
9432547a6aSVladis Dronov err);
9532547a6aSVladis Dronov return -EIO;
9638e9791aSSunil Goutham }
9738e9791aSSunil Goutham }
9838e9791aSSunil Goutham return 0;
9938e9791aSSunil Goutham }
10038e9791aSSunil Goutham
101506579e8SBharat Bhushan /* Returns true when valid data available otherwise return false */
cn10k_read_trng(struct cn10k_rng * rng,u64 * value)102506579e8SBharat Bhushan static bool cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
10338e9791aSSunil Goutham {
104506579e8SBharat Bhushan u16 retry_count = 0;
10538e9791aSSunil Goutham u64 upper, lower;
106506579e8SBharat Bhushan u64 status;
107506579e8SBharat Bhushan
108506579e8SBharat Bhushan if (rng->extended_trng_regs) {
109506579e8SBharat Bhushan do {
110506579e8SBharat Bhushan *value = readq(rng->reg_base + RNM_PF_TRNG_DAT);
111506579e8SBharat Bhushan if (*value)
112506579e8SBharat Bhushan return true;
113506579e8SBharat Bhushan status = readq(rng->reg_base + RNM_PF_TRNG_RES);
114506579e8SBharat Bhushan if (!status && (retry_count++ > 0x1000))
115506579e8SBharat Bhushan return false;
116506579e8SBharat Bhushan } while (!status);
117506579e8SBharat Bhushan }
11838e9791aSSunil Goutham
11938e9791aSSunil Goutham *value = readq(rng->reg_base + RNM_PF_RANDOM);
12038e9791aSSunil Goutham
12138e9791aSSunil Goutham /* HW can run out of entropy if large amount random data is read in
12238e9791aSSunil Goutham * quick succession. Zeros may not be real random data from HW.
12338e9791aSSunil Goutham */
12438e9791aSSunil Goutham if (!*value) {
12538e9791aSSunil Goutham upper = readq(rng->reg_base + RNM_PF_RANDOM);
12638e9791aSSunil Goutham lower = readq(rng->reg_base + RNM_PF_RANDOM);
12738e9791aSSunil Goutham while (!(upper & 0x00000000FFFFFFFFULL))
12838e9791aSSunil Goutham upper = readq(rng->reg_base + RNM_PF_RANDOM);
12938e9791aSSunil Goutham while (!(lower & 0xFFFFFFFF00000000ULL))
13038e9791aSSunil Goutham lower = readq(rng->reg_base + RNM_PF_RANDOM);
13138e9791aSSunil Goutham
13238e9791aSSunil Goutham *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
13338e9791aSSunil Goutham }
134506579e8SBharat Bhushan return true;
13538e9791aSSunil Goutham }
13638e9791aSSunil Goutham
cn10k_rng_read(struct hwrng * hwrng,void * data,size_t max,bool wait)13738e9791aSSunil Goutham static int cn10k_rng_read(struct hwrng *hwrng, void *data,
13838e9791aSSunil Goutham size_t max, bool wait)
13938e9791aSSunil Goutham {
14038e9791aSSunil Goutham struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
14138e9791aSSunil Goutham unsigned int size;
142753d6770SVladis Dronov u8 *pos = data;
14338e9791aSSunil Goutham int err = 0;
14438e9791aSSunil Goutham u64 value;
14538e9791aSSunil Goutham
14638e9791aSSunil Goutham err = check_rng_health(rng);
14738e9791aSSunil Goutham if (err)
14838e9791aSSunil Goutham return err;
14938e9791aSSunil Goutham
15038e9791aSSunil Goutham size = max;
15138e9791aSSunil Goutham
15238e9791aSSunil Goutham while (size >= 8) {
153506579e8SBharat Bhushan if (!cn10k_read_trng(rng, &value))
154506579e8SBharat Bhushan goto out;
15538e9791aSSunil Goutham
156753d6770SVladis Dronov *((u64 *)pos) = value;
15738e9791aSSunil Goutham size -= 8;
158753d6770SVladis Dronov pos += 8;
15938e9791aSSunil Goutham }
16038e9791aSSunil Goutham
161753d6770SVladis Dronov if (size > 0) {
162506579e8SBharat Bhushan if (!cn10k_read_trng(rng, &value))
163506579e8SBharat Bhushan goto out;
16438e9791aSSunil Goutham
165753d6770SVladis Dronov while (size > 0) {
166753d6770SVladis Dronov *pos = (u8)value;
167753d6770SVladis Dronov value >>= 8;
16838e9791aSSunil Goutham size--;
169753d6770SVladis Dronov pos++;
170753d6770SVladis Dronov }
17138e9791aSSunil Goutham }
17238e9791aSSunil Goutham
173506579e8SBharat Bhushan out:
17438e9791aSSunil Goutham return max - size;
17538e9791aSSunil Goutham }
17638e9791aSSunil Goutham
cn10k_rng_probe(struct pci_dev * pdev,const struct pci_device_id * id)17738e9791aSSunil Goutham static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
17838e9791aSSunil Goutham {
17938e9791aSSunil Goutham struct cn10k_rng *rng;
18038e9791aSSunil Goutham int err;
18138e9791aSSunil Goutham
18238e9791aSSunil Goutham rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
18338e9791aSSunil Goutham if (!rng)
18438e9791aSSunil Goutham return -ENOMEM;
18538e9791aSSunil Goutham
18638e9791aSSunil Goutham rng->pdev = pdev;
18738e9791aSSunil Goutham pci_set_drvdata(pdev, rng);
18838e9791aSSunil Goutham
18938e9791aSSunil Goutham rng->reg_base = pcim_iomap(pdev, 0, 0);
190*81511798SMartin Kaiser if (!rng->reg_base)
191*81511798SMartin Kaiser return dev_err_probe(&pdev->dev, -ENOMEM, "Error while mapping CSRs, exiting\n");
19238e9791aSSunil Goutham
19338e9791aSSunil Goutham rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
19438e9791aSSunil Goutham "cn10k-rng-%s", dev_name(&pdev->dev));
19538e9791aSSunil Goutham if (!rng->ops.name)
19638e9791aSSunil Goutham return -ENOMEM;
19738e9791aSSunil Goutham
19838e9791aSSunil Goutham rng->ops.read = cn10k_rng_read;
19938e9791aSSunil Goutham rng->ops.priv = (unsigned long)rng;
20038e9791aSSunil Goutham
201506579e8SBharat Bhushan rng->extended_trng_regs = cn10k_is_extended_trng_regs_supported(pdev);
202506579e8SBharat Bhushan
20338e9791aSSunil Goutham reset_rng_health_state(rng);
20438e9791aSSunil Goutham
20538e9791aSSunil Goutham err = devm_hwrng_register(&pdev->dev, &rng->ops);
206*81511798SMartin Kaiser if (err)
207*81511798SMartin Kaiser return dev_err_probe(&pdev->dev, err, "Could not register hwrng device.\n");
20838e9791aSSunil Goutham
20938e9791aSSunil Goutham return 0;
21038e9791aSSunil Goutham }
21138e9791aSSunil Goutham
21238e9791aSSunil Goutham static const struct pci_device_id cn10k_rng_id_table[] = {
21338e9791aSSunil Goutham { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
21438e9791aSSunil Goutham {0,},
21538e9791aSSunil Goutham };
21638e9791aSSunil Goutham
21738e9791aSSunil Goutham MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
21838e9791aSSunil Goutham
21938e9791aSSunil Goutham static struct pci_driver cn10k_rng_driver = {
22038e9791aSSunil Goutham .name = "cn10k_rng",
22138e9791aSSunil Goutham .id_table = cn10k_rng_id_table,
22238e9791aSSunil Goutham .probe = cn10k_rng_probe,
22338e9791aSSunil Goutham };
22438e9791aSSunil Goutham
22538e9791aSSunil Goutham module_pci_driver(cn10k_rng_driver);
22638e9791aSSunil Goutham MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
22738e9791aSSunil Goutham MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
22838e9791aSSunil Goutham MODULE_LICENSE("GPL v2");
229