0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/hw_random.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/pci.h>
0012 #include <linux/pci_ids.h>
0013 #include <linux/delay.h>
0014
0015 #include <linux/arm-smccc.h>
0016
0017
0018 #define RNM_CTL_STATUS 0x000
0019 #define RNM_ENTROPY_STATUS 0x008
0020 #define RNM_CONST 0x030
0021 #define RNM_EBG_ENT 0x048
0022 #define RNM_PF_EBG_HEALTH 0x050
0023 #define RNM_PF_RANDOM 0x400
0024 #define RNM_TRNG_RESULT 0x408
0025
0026 struct cn10k_rng {
0027 void __iomem *reg_base;
0028 struct hwrng ops;
0029 struct pci_dev *pdev;
0030 };
0031
0032 #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f
0033
0034 static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
0035 {
0036 struct arm_smccc_res res;
0037
0038
0039 arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res);
0040 return res.a0;
0041 }
0042
0043 static int check_rng_health(struct cn10k_rng *rng)
0044 {
0045 u64 status;
0046 unsigned long err;
0047
0048
0049 if (!rng->reg_base)
0050 return -ENODEV;
0051
0052 status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
0053 if (status & BIT_ULL(20)) {
0054 err = reset_rng_health_state(rng);
0055 if (err) {
0056 dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
0057 status);
0058 dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
0059 err);
0060 return -EIO;
0061 }
0062 }
0063 return 0;
0064 }
0065
0066 static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
0067 {
0068 u64 upper, lower;
0069
0070 *value = readq(rng->reg_base + RNM_PF_RANDOM);
0071
0072
0073
0074
0075 if (!*value) {
0076 upper = readq(rng->reg_base + RNM_PF_RANDOM);
0077 lower = readq(rng->reg_base + RNM_PF_RANDOM);
0078 while (!(upper & 0x00000000FFFFFFFFULL))
0079 upper = readq(rng->reg_base + RNM_PF_RANDOM);
0080 while (!(lower & 0xFFFFFFFF00000000ULL))
0081 lower = readq(rng->reg_base + RNM_PF_RANDOM);
0082
0083 *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
0084 }
0085 }
0086
0087 static int cn10k_rng_read(struct hwrng *hwrng, void *data,
0088 size_t max, bool wait)
0089 {
0090 struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
0091 unsigned int size;
0092 u8 *pos = data;
0093 int err = 0;
0094 u64 value;
0095
0096 err = check_rng_health(rng);
0097 if (err)
0098 return err;
0099
0100 size = max;
0101
0102 while (size >= 8) {
0103 cn10k_read_trng(rng, &value);
0104
0105 *((u64 *)pos) = value;
0106 size -= 8;
0107 pos += 8;
0108 }
0109
0110 if (size > 0) {
0111 cn10k_read_trng(rng, &value);
0112
0113 while (size > 0) {
0114 *pos = (u8)value;
0115 value >>= 8;
0116 size--;
0117 pos++;
0118 }
0119 }
0120
0121 return max - size;
0122 }
0123
0124 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0125 {
0126 struct cn10k_rng *rng;
0127 int err;
0128
0129 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
0130 if (!rng)
0131 return -ENOMEM;
0132
0133 rng->pdev = pdev;
0134 pci_set_drvdata(pdev, rng);
0135
0136 rng->reg_base = pcim_iomap(pdev, 0, 0);
0137 if (!rng->reg_base) {
0138 dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
0139 return -ENOMEM;
0140 }
0141
0142 rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
0143 "cn10k-rng-%s", dev_name(&pdev->dev));
0144 if (!rng->ops.name)
0145 return -ENOMEM;
0146
0147 rng->ops.read = cn10k_rng_read;
0148 rng->ops.quality = 1000;
0149 rng->ops.priv = (unsigned long)rng;
0150
0151 reset_rng_health_state(rng);
0152
0153 err = devm_hwrng_register(&pdev->dev, &rng->ops);
0154 if (err) {
0155 dev_err(&pdev->dev, "Could not register hwrng device.\n");
0156 return err;
0157 }
0158
0159 return 0;
0160 }
0161
0162 static void cn10k_rng_remove(struct pci_dev *pdev)
0163 {
0164
0165 }
0166
0167 static const struct pci_device_id cn10k_rng_id_table[] = {
0168 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) },
0169 {0,},
0170 };
0171
0172 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
0173
0174 static struct pci_driver cn10k_rng_driver = {
0175 .name = "cn10k_rng",
0176 .id_table = cn10k_rng_id_table,
0177 .probe = cn10k_rng_probe,
0178 .remove = cn10k_rng_remove,
0179 };
0180
0181 module_pci_driver(cn10k_rng_driver);
0182 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
0183 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
0184 MODULE_LICENSE("GPL v2");