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 <crypto/internal/rng.h> 7#include <linux/acpi.h> 8#include <linux/clk.h> 9#include <linux/crypto.h> 10#include <linux/hw_random.h> 11#include <linux/io.h> 12#include <linux/iopoll.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/of.h> 16#include <linux/platform_device.h> 17 18/* Device specific register offsets */ 19#define PRNG_DATA_OUT 0x0000 20#define PRNG_STATUS 0x0004 21#define PRNG_LFSR_CFG 0x0100 22#define PRNG_CONFIG 0x0104 23 24/* Device specific register masks and config values */ 25#define PRNG_LFSR_CFG_MASK 0x0000ffff 26#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd 27#define PRNG_CONFIG_HW_ENABLE BIT(1) 28#define PRNG_STATUS_DATA_AVAIL BIT(0) 29 30#define WORD_SZ 4 31 32#define QCOM_TRNG_QUALITY 1024 33 34struct qcom_rng { 35 struct mutex lock; 36 void __iomem *base; 37 struct clk *clk; 38 struct hwrng hwrng; 39 struct qcom_rng_of_data *of_data; 40}; 41 42struct qcom_rng_ctx { 43 struct qcom_rng *rng; 44}; 45 46struct qcom_rng_of_data { 47 bool skip_init; 48 bool hwrng_support; 49}; 50 51static struct qcom_rng *qcom_rng_dev; 52 53static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) 54{ 55 unsigned int currsize = 0; 56 u32 val; 57 int ret; 58 59 /* read random data from hardware */ 60 do { 61 ret = readl_poll_timeout(rng->base + PRNG_STATUS, val, 62 val & PRNG_STATUS_DATA_AVAIL, 63 200, 10000); 64 if (ret) 65 return ret; 66 67 val = readl_relaxed(rng->base + PRNG_DATA_OUT); 68 if (!val) 69 return -EINVAL; 70 71 if ((max - currsize) >= WORD_SZ) { 72 memcpy(data, &val, WORD_SZ); 73 data += WORD_SZ; 74 currsize += WORD_SZ; 75 } else { 76 /* copy only remaining bytes */ 77 memcpy(data, &val, max - currsize); 78 currsize = max; 79 } 80 } while (currsize < max); 81 82 return currsize; 83} 84 85static int qcom_rng_generate(struct crypto_rng *tfm, 86 const u8 *src, unsigned int slen, 87 u8 *dstn, unsigned int dlen) 88{ 89 struct qcom_rng_ctx *ctx = crypto_rng_ctx(tfm); 90 struct qcom_rng *rng = ctx->rng; 91 int ret; 92 93 ret = clk_prepare_enable(rng->clk); 94 if (ret) 95 return ret; 96 97 mutex_lock(&rng->lock); 98 99 ret = qcom_rng_read(rng, dstn, dlen); 100 101 mutex_unlock(&rng->lock); 102 clk_disable_unprepare(rng->clk); 103 104 if (ret >= 0) 105 ret = 0; 106 107 return ret; 108} 109 110static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed, 111 unsigned int slen) 112{ 113 return 0; 114} 115 116static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) 117{ 118 struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 119 120 return qcom_rng_read(qrng, data, max); 121} 122 123static int qcom_rng_enable(struct qcom_rng *rng) 124{ 125 u32 val; 126 int ret; 127 128 ret = clk_prepare_enable(rng->clk); 129 if (ret) 130 return ret; 131 132 /* Enable PRNG only if it is not already enabled */ 133 val = readl_relaxed(rng->base + PRNG_CONFIG); 134 if (val & PRNG_CONFIG_HW_ENABLE) 135 goto already_enabled; 136 137 val = readl_relaxed(rng->base + PRNG_LFSR_CFG); 138 val &= ~PRNG_LFSR_CFG_MASK; 139 val |= PRNG_LFSR_CFG_CLOCKS; 140 writel(val, rng->base + PRNG_LFSR_CFG); 141 142 val = readl_relaxed(rng->base + PRNG_CONFIG); 143 val |= PRNG_CONFIG_HW_ENABLE; 144 writel(val, rng->base + PRNG_CONFIG); 145 146already_enabled: 147 clk_disable_unprepare(rng->clk); 148 149 return 0; 150} 151 152static int qcom_rng_init(struct crypto_tfm *tfm) 153{ 154 struct qcom_rng_ctx *ctx = crypto_tfm_ctx(tfm); 155 156 ctx->rng = qcom_rng_dev; 157 158 if (!ctx->rng->of_data->skip_init) 159 return qcom_rng_enable(ctx->rng); 160 161 return 0; 162} 163 164static struct rng_alg qcom_rng_alg = { 165 .generate = qcom_rng_generate, 166 .seed = qcom_rng_seed, 167 .seedsize = 0, 168 .base = { 169 .cra_name = "stdrng", 170 .cra_driver_name = "qcom-rng", 171 .cra_flags = CRYPTO_ALG_TYPE_RNG, 172 .cra_priority = 300, 173 .cra_ctxsize = sizeof(struct qcom_rng_ctx), 174 .cra_module = THIS_MODULE, 175 .cra_init = qcom_rng_init, 176 } 177}; 178 179static int qcom_rng_probe(struct platform_device *pdev) 180{ 181 struct qcom_rng *rng; 182 int ret; 183 184 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 185 if (!rng) 186 return -ENOMEM; 187 188 platform_set_drvdata(pdev, rng); 189 mutex_init(&rng->lock); 190 191 rng->base = devm_platform_ioremap_resource(pdev, 0); 192 if (IS_ERR(rng->base)) 193 return PTR_ERR(rng->base); 194 195 rng->clk = devm_clk_get_optional(&pdev->dev, "core"); 196 if (IS_ERR(rng->clk)) 197 return PTR_ERR(rng->clk); 198 199 rng->of_data = (struct qcom_rng_of_data *)of_device_get_match_data(&pdev->dev); 200 201 qcom_rng_dev = rng; 202 ret = crypto_register_rng(&qcom_rng_alg); 203 if (ret) { 204 dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret); 205 qcom_rng_dev = NULL; 206 return ret; 207 } 208 209 if (rng->of_data->hwrng_support) { 210 rng->hwrng.name = "qcom_hwrng"; 211 rng->hwrng.read = qcom_hwrng_read; 212 rng->hwrng.quality = QCOM_TRNG_QUALITY; 213 ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); 214 if (ret) { 215 dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret); 216 qcom_rng_dev = NULL; 217 goto fail; 218 } 219 } 220 221 return ret; 222fail: 223 crypto_unregister_rng(&qcom_rng_alg); 224 return ret; 225} 226 227static void qcom_rng_remove(struct platform_device *pdev) 228{ 229 crypto_unregister_rng(&qcom_rng_alg); 230 231 qcom_rng_dev = NULL; 232} 233 234static struct qcom_rng_of_data qcom_prng_of_data = { 235 .skip_init = false, 236 .hwrng_support = false, 237}; 238 239static struct qcom_rng_of_data qcom_prng_ee_of_data = { 240 .skip_init = true, 241 .hwrng_support = false, 242}; 243 244static struct qcom_rng_of_data qcom_trng_of_data = { 245 .skip_init = true, 246 .hwrng_support = true, 247}; 248 249static const struct acpi_device_id __maybe_unused qcom_rng_acpi_match[] = { 250 { .id = "QCOM8160", .driver_data = 1 }, 251 {} 252}; 253MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match); 254 255static const struct of_device_id __maybe_unused qcom_rng_of_match[] = { 256 { .compatible = "qcom,prng", .data = &qcom_prng_of_data }, 257 { .compatible = "qcom,prng-ee", .data = &qcom_prng_ee_of_data }, 258 { .compatible = "qcom,trng", .data = &qcom_trng_of_data }, 259 {} 260}; 261MODULE_DEVICE_TABLE(of, qcom_rng_of_match); 262 263static struct platform_driver qcom_rng_driver = { 264 .probe = qcom_rng_probe, 265 .remove_new = qcom_rng_remove, 266 .driver = { 267 .name = KBUILD_MODNAME, 268 .of_match_table = of_match_ptr(qcom_rng_of_match), 269 .acpi_match_table = ACPI_PTR(qcom_rng_acpi_match), 270 } 271}; 272module_platform_driver(qcom_rng_driver); 273 274MODULE_ALIAS("platform:" KBUILD_MODNAME); 275MODULE_DESCRIPTION("Qualcomm random number generator driver"); 276MODULE_LICENSE("GPL v2"); 277