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