Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Hardware Random Number Generator support.
0004  * Cavium Thunder, Marvell OcteonTx/Tx2 processor families.
0005  *
0006  * Copyright (C) 2016 Cavium, Inc.
0007  */
0008 
0009 #include <linux/hw_random.h>
0010 #include <linux/io.h>
0011 #include <linux/module.h>
0012 #include <linux/pci.h>
0013 #include <linux/pci_ids.h>
0014 
0015 #include <asm/arch_timer.h>
0016 
0017 /* PCI device IDs */
0018 #define PCI_DEVID_CAVIUM_RNG_PF     0xA018
0019 #define PCI_DEVID_CAVIUM_RNG_VF     0xA033
0020 
0021 #define HEALTH_STATUS_REG       0x38
0022 
0023 /* RST device info */
0024 #define PCI_DEVICE_ID_RST_OTX2      0xA085
0025 #define RST_BOOT_REG            0x1600ULL
0026 #define CLOCK_BASE_RATE         50000000ULL
0027 #define MSEC_TO_NSEC(x)         (x * 1000000)
0028 
0029 struct cavium_rng {
0030     struct hwrng ops;
0031     void __iomem *result;
0032     void __iomem *pf_regbase;
0033     struct pci_dev *pdev;
0034     u64  clock_rate;
0035     u64  prev_error;
0036     u64  prev_time;
0037 };
0038 
0039 static inline bool is_octeontx(struct pci_dev *pdev)
0040 {
0041     if (midr_is_cpu_model_range(read_cpuid_id(), MIDR_THUNDERX_83XX,
0042                     MIDR_CPU_VAR_REV(0, 0),
0043                     MIDR_CPU_VAR_REV(3, 0)) ||
0044         midr_is_cpu_model_range(read_cpuid_id(), MIDR_THUNDERX_81XX,
0045                     MIDR_CPU_VAR_REV(0, 0),
0046                     MIDR_CPU_VAR_REV(3, 0)) ||
0047         midr_is_cpu_model_range(read_cpuid_id(), MIDR_THUNDERX,
0048                     MIDR_CPU_VAR_REV(0, 0),
0049                     MIDR_CPU_VAR_REV(3, 0)))
0050         return true;
0051 
0052     return false;
0053 }
0054 
0055 static u64 rng_get_coprocessor_clkrate(void)
0056 {
0057     u64 ret = CLOCK_BASE_RATE * 16; /* Assume 800Mhz as default */
0058     struct pci_dev *pdev;
0059     void __iomem *base;
0060 
0061     pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
0062                   PCI_DEVICE_ID_RST_OTX2, NULL);
0063     if (!pdev)
0064         goto error;
0065 
0066     base = pci_ioremap_bar(pdev, 0);
0067     if (!base)
0068         goto error_put_pdev;
0069 
0070     /* RST: PNR_MUL * 50Mhz gives clockrate */
0071     ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT_REG) >> 33) & 0x3F);
0072 
0073     iounmap(base);
0074 
0075 error_put_pdev:
0076     pci_dev_put(pdev);
0077 
0078 error:
0079     return ret;
0080 }
0081 
0082 static int check_rng_health(struct cavium_rng *rng)
0083 {
0084     u64 cur_err, cur_time;
0085     u64 status, cycles;
0086     u64 time_elapsed;
0087 
0088 
0089     /* Skip checking health for OcteonTx */
0090     if (!rng->pf_regbase)
0091         return 0;
0092 
0093     status = readq(rng->pf_regbase + HEALTH_STATUS_REG);
0094     if (status & BIT_ULL(0)) {
0095         dev_err(&rng->pdev->dev, "HWRNG: Startup health test failed\n");
0096         return -EIO;
0097     }
0098 
0099     cycles = status >> 1;
0100     if (!cycles)
0101         return 0;
0102 
0103     cur_time = arch_timer_read_counter();
0104 
0105     /* RNM_HEALTH_STATUS[CYCLES_SINCE_HEALTH_FAILURE]
0106      * Number of coprocessor cycles times 2 since the last failure.
0107      * This field doesn't get cleared/updated until another failure.
0108      */
0109     cycles = cycles / 2;
0110     cur_err = (cycles * 1000000000) / rng->clock_rate; /* In nanosec */
0111 
0112     /* Ignore errors that happenned a long time ago, these
0113      * are most likely false positive errors.
0114      */
0115     if (cur_err > MSEC_TO_NSEC(10)) {
0116         rng->prev_error = 0;
0117         rng->prev_time = 0;
0118         return 0;
0119     }
0120 
0121     if (rng->prev_error) {
0122         /* Calculate time elapsed since last error
0123          * '1' tick of CNTVCT is 10ns, since it runs at 100Mhz.
0124          */
0125         time_elapsed = (cur_time - rng->prev_time) * 10;
0126         time_elapsed += rng->prev_error;
0127 
0128         /* Check if current error is a new one or the old one itself.
0129          * If error is a new one then consider there is a persistent
0130          * issue with entropy, declare hardware failure.
0131          */
0132         if (cur_err < time_elapsed) {
0133             dev_err(&rng->pdev->dev, "HWRNG failure detected\n");
0134             rng->prev_error = cur_err;
0135             rng->prev_time = cur_time;
0136             return -EIO;
0137         }
0138     }
0139 
0140     rng->prev_error = cur_err;
0141     rng->prev_time = cur_time;
0142     return 0;
0143 }
0144 
0145 /* Read data from the RNG unit */
0146 static int cavium_rng_read(struct hwrng *rng, void *dat, size_t max, bool wait)
0147 {
0148     struct cavium_rng *p = container_of(rng, struct cavium_rng, ops);
0149     unsigned int size = max;
0150     int err = 0;
0151 
0152     err = check_rng_health(p);
0153     if (err)
0154         return err;
0155 
0156     while (size >= 8) {
0157         *((u64 *)dat) = readq(p->result);
0158         size -= 8;
0159         dat += 8;
0160     }
0161     while (size > 0) {
0162         *((u8 *)dat) = readb(p->result);
0163         size--;
0164         dat++;
0165     }
0166     return max;
0167 }
0168 
0169 static int cavium_map_pf_regs(struct cavium_rng *rng)
0170 {
0171     struct pci_dev *pdev;
0172 
0173     /* Health status is not supported on 83xx, skip mapping PF CSRs */
0174     if (is_octeontx(rng->pdev)) {
0175         rng->pf_regbase = NULL;
0176         return 0;
0177     }
0178 
0179     pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
0180                   PCI_DEVID_CAVIUM_RNG_PF, NULL);
0181     if (!pdev) {
0182         pr_err("Cannot find RNG PF device\n");
0183         return -EIO;
0184     }
0185 
0186     rng->pf_regbase = ioremap(pci_resource_start(pdev, 0),
0187                   pci_resource_len(pdev, 0));
0188     if (!rng->pf_regbase) {
0189         dev_err(&pdev->dev, "Failed to map PF CSR region\n");
0190         pci_dev_put(pdev);
0191         return -ENOMEM;
0192     }
0193 
0194     pci_dev_put(pdev);
0195 
0196     /* Get co-processor clock rate */
0197     rng->clock_rate = rng_get_coprocessor_clkrate();
0198 
0199     return 0;
0200 }
0201 
0202 /* Map Cavium RNG to an HWRNG object */
0203 static int cavium_rng_probe_vf(struct   pci_dev     *pdev,
0204              const struct   pci_device_id   *id)
0205 {
0206     struct  cavium_rng *rng;
0207     int ret;
0208 
0209     rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
0210     if (!rng)
0211         return -ENOMEM;
0212 
0213     rng->pdev = pdev;
0214 
0215     /* Map the RNG result */
0216     rng->result = pcim_iomap(pdev, 0, 0);
0217     if (!rng->result) {
0218         dev_err(&pdev->dev, "Error iomap failed retrieving result.\n");
0219         return -ENOMEM;
0220     }
0221 
0222     rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
0223                        "cavium-rng-%s", dev_name(&pdev->dev));
0224     if (!rng->ops.name)
0225         return -ENOMEM;
0226 
0227     rng->ops.read    = cavium_rng_read;
0228     rng->ops.quality = 1000;
0229 
0230     pci_set_drvdata(pdev, rng);
0231 
0232     /* Health status is available only at PF, hence map PF registers. */
0233     ret = cavium_map_pf_regs(rng);
0234     if (ret)
0235         return ret;
0236 
0237     ret = devm_hwrng_register(&pdev->dev, &rng->ops);
0238     if (ret) {
0239         dev_err(&pdev->dev, "Error registering device as HWRNG.\n");
0240         return ret;
0241     }
0242 
0243     return 0;
0244 }
0245 
0246 /* Remove the VF */
0247 static void cavium_rng_remove_vf(struct pci_dev *pdev)
0248 {
0249     struct cavium_rng *rng;
0250 
0251     rng = pci_get_drvdata(pdev);
0252     iounmap(rng->pf_regbase);
0253 }
0254 
0255 static const struct pci_device_id cavium_rng_vf_id_table[] = {
0256     { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CAVIUM_RNG_VF) },
0257     { 0, }
0258 };
0259 MODULE_DEVICE_TABLE(pci, cavium_rng_vf_id_table);
0260 
0261 static struct pci_driver cavium_rng_vf_driver = {
0262     .name       = "cavium_rng_vf",
0263     .id_table   = cavium_rng_vf_id_table,
0264     .probe      = cavium_rng_probe_vf,
0265     .remove     = cavium_rng_remove_vf,
0266 };
0267 module_pci_driver(cavium_rng_vf_driver);
0268 
0269 MODULE_AUTHOR("Omer Khaliq <okhaliq@caviumnetworks.com>");
0270 MODULE_LICENSE("GPL v2");