1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2017-18 Linaro Limited 3 // 4 // Based on msm-rng.c and downstream driver 5 6 #include <linux/acpi.h> 7 #include <linux/clk.h> 8 #include <linux/hw_random.h> 9 #include <linux/io.h> 10 #include <linux/iopoll.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 16 /* Device specific register offsets */ 17 #define PRNG_DATA_OUT 0x0000 18 #define PRNG_STATUS 0x0004 19 #define PRNG_LFSR_CFG 0x0100 20 #define PRNG_CONFIG 0x0104 21 22 /* Device specific register masks and config values */ 23 #define PRNG_LFSR_CFG_MASK 0x0000ffff 24 #define PRNG_LFSR_CFG_CLOCKS 0x0000dddd 25 #define PRNG_CONFIG_HW_ENABLE BIT(1) 26 #define PRNG_STATUS_DATA_AVAIL BIT(0) 27 28 #define WORD_SZ 4 29 30 #define QCOM_TRNG_QUALITY 1024 31 32 struct qcom_rng { 33 void __iomem *base; 34 struct clk *clk; 35 struct hwrng hwrng; 36 }; 37 38 struct qcom_rng_match_data { 39 bool hwrng_support; 40 }; 41 42 static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) 43 { 44 unsigned int currsize = 0; 45 u32 val; 46 int ret; 47 48 /* read random data from hardware */ 49 do { 50 ret = readl_poll_timeout(rng->base + PRNG_STATUS, val, 51 val & PRNG_STATUS_DATA_AVAIL, 52 200, 10000); 53 if (ret) 54 return ret; 55 56 val = readl_relaxed(rng->base + PRNG_DATA_OUT); 57 58 if ((max - currsize) >= WORD_SZ) { 59 memcpy(data, &val, WORD_SZ); 60 data += WORD_SZ; 61 currsize += WORD_SZ; 62 } else { 63 /* copy only remaining bytes */ 64 memcpy(data, &val, max - currsize); 65 currsize = max; 66 } 67 } while (currsize < max); 68 69 return currsize; 70 } 71 72 static int qcom_hwrng_init(struct hwrng *hwrng) 73 { 74 struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 75 76 return clk_prepare_enable(qrng->clk); 77 } 78 79 static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) 80 { 81 struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 82 83 return qcom_rng_read(qrng, data, max); 84 } 85 86 static void qcom_hwrng_cleanup(struct hwrng *hwrng) 87 { 88 struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 89 90 clk_disable_unprepare(qrng->clk); 91 } 92 93 static int qcom_rng_probe(struct platform_device *pdev) 94 { 95 const struct qcom_rng_match_data *match_data; 96 struct qcom_rng *rng; 97 int ret; 98 99 match_data = device_get_match_data(&pdev->dev); 100 if (match_data == NULL || !match_data->hwrng_support) { 101 dev_info(&pdev->dev, "TRNG support not detected\n"); 102 /* 103 * In this case the driver does nothing except the dev_info(), 104 * but bind the device anyway to avoid effects on GCC state. 105 */ 106 return 0; 107 } 108 109 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 110 if (!rng) 111 return -ENOMEM; 112 113 rng->base = devm_platform_ioremap_resource(pdev, 0); 114 if (IS_ERR(rng->base)) 115 return PTR_ERR(rng->base); 116 117 rng->clk = devm_clk_get_optional(&pdev->dev, "core"); 118 if (IS_ERR(rng->clk)) 119 return PTR_ERR(rng->clk); 120 121 rng->hwrng.name = "qcom_hwrng"; 122 rng->hwrng.init = qcom_hwrng_init; 123 rng->hwrng.read = qcom_hwrng_read; 124 rng->hwrng.cleanup = qcom_hwrng_cleanup; 125 rng->hwrng.quality = QCOM_TRNG_QUALITY; 126 ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); 127 if (ret) 128 dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret); 129 return ret; 130 } 131 132 static struct qcom_rng_match_data qcom_prng_match_data = { 133 .hwrng_support = false, 134 }; 135 136 static struct qcom_rng_match_data qcom_prng_ee_match_data = { 137 .hwrng_support = false, 138 }; 139 140 static struct qcom_rng_match_data qcom_trng_match_data = { 141 .hwrng_support = true, 142 }; 143 144 static const struct acpi_device_id __maybe_unused qcom_rng_acpi_match[] = { 145 { .id = "QCOM8160", .driver_data = (kernel_ulong_t)&qcom_prng_ee_match_data }, 146 {} 147 }; 148 MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match); 149 150 static const struct of_device_id __maybe_unused qcom_rng_of_match[] = { 151 { .compatible = "qcom,prng", .data = &qcom_prng_match_data }, 152 { .compatible = "qcom,prng-ee", .data = &qcom_prng_ee_match_data }, 153 { .compatible = "qcom,trng", .data = &qcom_trng_match_data }, 154 {} 155 }; 156 MODULE_DEVICE_TABLE(of, qcom_rng_of_match); 157 158 static struct platform_driver qcom_rng_driver = { 159 .probe = qcom_rng_probe, 160 .driver = { 161 .name = KBUILD_MODNAME, 162 .of_match_table = qcom_rng_of_match, 163 .acpi_match_table = ACPI_PTR(qcom_rng_acpi_match), 164 } 165 }; 166 module_platform_driver(qcom_rng_driver); 167 168 MODULE_ALIAS("platform:" KBUILD_MODNAME); 169 MODULE_DESCRIPTION("Qualcomm random number generator driver"); 170 MODULE_LICENSE("GPL v2"); 171