brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 3193531 Raw
229 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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		0x00019#define RNM_ENTROPY_STATUS	0x00820#define RNM_CONST		0x03021#define RNM_EBG_ENT		0x04822#define RNM_PF_EBG_HEALTH	0x05023#define RNM_PF_RANDOM		0x40024#define RNM_TRNG_RESULT		0x40825 26/* Extended TRNG Read and Status Registers */27#define RNM_PF_TRNG_DAT		0x100028#define RNM_PF_TRNG_RES		0x100829 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/B035	 * does not support extended TRNG registers36	 */37	bool extended_trng_regs;38};39 40#define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE     0xc2000b0f41 42#define PCI_SUBSYS_DEVID_CN10K_A_RNG	0xB90043#define PCI_SUBSYS_DEVID_CNF10K_A_RNG	0xBA0044#define PCI_SUBSYS_DEVID_CNF10K_B_RNG	0xBC0045 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 in122	 * 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");229