400 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * exynos-rng.c - Random Number Generator driver for the Exynos4 *5 * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>6 *7 * Loosely based on old driver from drivers/char/hw_random/exynos-rng.c:8 * Copyright (C) 2012 Samsung Electronics9 * Jonghwa Lee <jonghwa3.lee@samsung.com>10 */11 12#include <linux/clk.h>13#include <linux/crypto.h>14#include <linux/err.h>15#include <linux/io.h>16#include <linux/module.h>17#include <linux/mutex.h>18#include <linux/of.h>19#include <linux/platform_device.h>20 21#include <crypto/internal/rng.h>22 23#define EXYNOS_RNG_CONTROL 0x024#define EXYNOS_RNG_STATUS 0x1025 26#define EXYNOS_RNG_SEED_CONF 0x1427#define EXYNOS_RNG_GEN_PRNG BIT(1)28 29#define EXYNOS_RNG_SEED_BASE 0x14030#define EXYNOS_RNG_SEED(n) (EXYNOS_RNG_SEED_BASE + (n * 0x4))31#define EXYNOS_RNG_OUT_BASE 0x16032#define EXYNOS_RNG_OUT(n) (EXYNOS_RNG_OUT_BASE + (n * 0x4))33 34/* EXYNOS_RNG_CONTROL bit fields */35#define EXYNOS_RNG_CONTROL_START 0x1836/* EXYNOS_RNG_STATUS bit fields */37#define EXYNOS_RNG_STATUS_SEED_SETTING_DONE BIT(1)38#define EXYNOS_RNG_STATUS_RNG_DONE BIT(5)39 40/* Five seed and output registers, each 4 bytes */41#define EXYNOS_RNG_SEED_REGS 542#define EXYNOS_RNG_SEED_SIZE (EXYNOS_RNG_SEED_REGS * 4)43 44enum exynos_prng_type {45 EXYNOS_PRNG_UNKNOWN = 0,46 EXYNOS_PRNG_EXYNOS4,47 EXYNOS_PRNG_EXYNOS5,48};49 50/*51 * Driver re-seeds itself with generated random numbers to hinder52 * backtracking of the original seed.53 *54 * Time for next re-seed in ms.55 */56#define EXYNOS_RNG_RESEED_TIME 100057#define EXYNOS_RNG_RESEED_BYTES 6553658 59/*60 * In polling mode, do not wait infinitely for the engine to finish the work.61 */62#define EXYNOS_RNG_WAIT_RETRIES 10063 64/* Context for crypto */65struct exynos_rng_ctx {66 struct exynos_rng_dev *rng;67};68 69/* Device associated memory */70struct exynos_rng_dev {71 struct device *dev;72 enum exynos_prng_type type;73 void __iomem *mem;74 struct clk *clk;75 struct mutex lock;76 /* Generated numbers stored for seeding during resume */77 u8 seed_save[EXYNOS_RNG_SEED_SIZE];78 unsigned int seed_save_len;79 /* Time of last seeding in jiffies */80 unsigned long last_seeding;81 /* Bytes generated since last seeding */82 unsigned long bytes_seeding;83};84 85static struct exynos_rng_dev *exynos_rng_dev;86 87static u32 exynos_rng_readl(struct exynos_rng_dev *rng, u32 offset)88{89 return readl_relaxed(rng->mem + offset);90}91 92static void exynos_rng_writel(struct exynos_rng_dev *rng, u32 val, u32 offset)93{94 writel_relaxed(val, rng->mem + offset);95}96 97static int exynos_rng_set_seed(struct exynos_rng_dev *rng,98 const u8 *seed, unsigned int slen)99{100 u32 val;101 int i;102 103 /* Round seed length because loop iterates over full register size */104 slen = ALIGN_DOWN(slen, 4);105 106 if (slen < EXYNOS_RNG_SEED_SIZE)107 return -EINVAL;108 109 for (i = 0; i < slen ; i += 4) {110 unsigned int seed_reg = (i / 4) % EXYNOS_RNG_SEED_REGS;111 112 val = seed[i] << 24;113 val |= seed[i + 1] << 16;114 val |= seed[i + 2] << 8;115 val |= seed[i + 3] << 0;116 117 exynos_rng_writel(rng, val, EXYNOS_RNG_SEED(seed_reg));118 }119 120 val = exynos_rng_readl(rng, EXYNOS_RNG_STATUS);121 if (!(val & EXYNOS_RNG_STATUS_SEED_SETTING_DONE)) {122 dev_warn(rng->dev, "Seed setting not finished\n");123 return -EIO;124 }125 126 rng->last_seeding = jiffies;127 rng->bytes_seeding = 0;128 129 return 0;130}131 132/*133 * Start the engine and poll for finish. Then read from output registers134 * filling the 'dst' buffer up to 'dlen' bytes or up to size of generated135 * random data (EXYNOS_RNG_SEED_SIZE).136 *137 * On success: return 0 and store number of read bytes under 'read' address.138 * On error: return -ERRNO.139 */140static int exynos_rng_get_random(struct exynos_rng_dev *rng,141 u8 *dst, unsigned int dlen,142 unsigned int *read)143{144 int retry = EXYNOS_RNG_WAIT_RETRIES;145 146 if (rng->type == EXYNOS_PRNG_EXYNOS4) {147 exynos_rng_writel(rng, EXYNOS_RNG_CONTROL_START,148 EXYNOS_RNG_CONTROL);149 } else if (rng->type == EXYNOS_PRNG_EXYNOS5) {150 exynos_rng_writel(rng, EXYNOS_RNG_GEN_PRNG,151 EXYNOS_RNG_SEED_CONF);152 }153 154 while (!(exynos_rng_readl(rng,155 EXYNOS_RNG_STATUS) & EXYNOS_RNG_STATUS_RNG_DONE) && --retry)156 cpu_relax();157 158 if (!retry)159 return -ETIMEDOUT;160 161 /* Clear status bit */162 exynos_rng_writel(rng, EXYNOS_RNG_STATUS_RNG_DONE,163 EXYNOS_RNG_STATUS);164 *read = min_t(size_t, dlen, EXYNOS_RNG_SEED_SIZE);165 memcpy_fromio(dst, rng->mem + EXYNOS_RNG_OUT_BASE, *read);166 rng->bytes_seeding += *read;167 168 return 0;169}170 171/* Re-seed itself from time to time */172static void exynos_rng_reseed(struct exynos_rng_dev *rng)173{174 unsigned long next_seeding = rng->last_seeding + \175 msecs_to_jiffies(EXYNOS_RNG_RESEED_TIME);176 unsigned long now = jiffies;177 unsigned int read = 0;178 u8 seed[EXYNOS_RNG_SEED_SIZE];179 180 if (time_before(now, next_seeding) &&181 rng->bytes_seeding < EXYNOS_RNG_RESEED_BYTES)182 return;183 184 if (exynos_rng_get_random(rng, seed, sizeof(seed), &read))185 return;186 187 exynos_rng_set_seed(rng, seed, read);188 189 /* Let others do some of their job. */190 mutex_unlock(&rng->lock);191 mutex_lock(&rng->lock);192}193 194static int exynos_rng_generate(struct crypto_rng *tfm,195 const u8 *src, unsigned int slen,196 u8 *dst, unsigned int dlen)197{198 struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);199 struct exynos_rng_dev *rng = ctx->rng;200 unsigned int read = 0;201 int ret;202 203 ret = clk_prepare_enable(rng->clk);204 if (ret)205 return ret;206 207 mutex_lock(&rng->lock);208 do {209 ret = exynos_rng_get_random(rng, dst, dlen, &read);210 if (ret)211 break;212 213 dlen -= read;214 dst += read;215 216 exynos_rng_reseed(rng);217 } while (dlen > 0);218 mutex_unlock(&rng->lock);219 220 clk_disable_unprepare(rng->clk);221 222 return ret;223}224 225static int exynos_rng_seed(struct crypto_rng *tfm, const u8 *seed,226 unsigned int slen)227{228 struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);229 struct exynos_rng_dev *rng = ctx->rng;230 int ret;231 232 ret = clk_prepare_enable(rng->clk);233 if (ret)234 return ret;235 236 mutex_lock(&rng->lock);237 ret = exynos_rng_set_seed(ctx->rng, seed, slen);238 mutex_unlock(&rng->lock);239 240 clk_disable_unprepare(rng->clk);241 242 return ret;243}244 245static int exynos_rng_kcapi_init(struct crypto_tfm *tfm)246{247 struct exynos_rng_ctx *ctx = crypto_tfm_ctx(tfm);248 249 ctx->rng = exynos_rng_dev;250 251 return 0;252}253 254static struct rng_alg exynos_rng_alg = {255 .generate = exynos_rng_generate,256 .seed = exynos_rng_seed,257 .seedsize = EXYNOS_RNG_SEED_SIZE,258 .base = {259 .cra_name = "stdrng",260 .cra_driver_name = "exynos_rng",261 .cra_priority = 300,262 .cra_ctxsize = sizeof(struct exynos_rng_ctx),263 .cra_module = THIS_MODULE,264 .cra_init = exynos_rng_kcapi_init,265 }266};267 268static int exynos_rng_probe(struct platform_device *pdev)269{270 struct exynos_rng_dev *rng;271 int ret;272 273 if (exynos_rng_dev)274 return -EEXIST;275 276 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);277 if (!rng)278 return -ENOMEM;279 280 rng->type = (uintptr_t)of_device_get_match_data(&pdev->dev);281 282 mutex_init(&rng->lock);283 284 rng->dev = &pdev->dev;285 rng->clk = devm_clk_get(&pdev->dev, "secss");286 if (IS_ERR(rng->clk)) {287 dev_err(&pdev->dev, "Couldn't get clock.\n");288 return PTR_ERR(rng->clk);289 }290 291 rng->mem = devm_platform_ioremap_resource(pdev, 0);292 if (IS_ERR(rng->mem))293 return PTR_ERR(rng->mem);294 295 platform_set_drvdata(pdev, rng);296 297 exynos_rng_dev = rng;298 299 ret = crypto_register_rng(&exynos_rng_alg);300 if (ret) {301 dev_err(&pdev->dev,302 "Couldn't register rng crypto alg: %d\n", ret);303 exynos_rng_dev = NULL;304 }305 306 return ret;307}308 309static void exynos_rng_remove(struct platform_device *pdev)310{311 crypto_unregister_rng(&exynos_rng_alg);312 313 exynos_rng_dev = NULL;314}315 316static int __maybe_unused exynos_rng_suspend(struct device *dev)317{318 struct exynos_rng_dev *rng = dev_get_drvdata(dev);319 int ret;320 321 /* If we were never seeded then after resume it will be the same */322 if (!rng->last_seeding)323 return 0;324 325 rng->seed_save_len = 0;326 ret = clk_prepare_enable(rng->clk);327 if (ret)328 return ret;329 330 mutex_lock(&rng->lock);331 332 /* Get new random numbers and store them for seeding on resume. */333 exynos_rng_get_random(rng, rng->seed_save, sizeof(rng->seed_save),334 &(rng->seed_save_len));335 336 mutex_unlock(&rng->lock);337 338 dev_dbg(rng->dev, "Stored %u bytes for seeding on system resume\n",339 rng->seed_save_len);340 341 clk_disable_unprepare(rng->clk);342 343 return 0;344}345 346static int __maybe_unused exynos_rng_resume(struct device *dev)347{348 struct exynos_rng_dev *rng = dev_get_drvdata(dev);349 int ret;350 351 /* Never seeded so nothing to do */352 if (!rng->last_seeding)353 return 0;354 355 ret = clk_prepare_enable(rng->clk);356 if (ret)357 return ret;358 359 mutex_lock(&rng->lock);360 361 ret = exynos_rng_set_seed(rng, rng->seed_save, rng->seed_save_len);362 363 mutex_unlock(&rng->lock);364 365 clk_disable_unprepare(rng->clk);366 367 return ret;368}369 370static SIMPLE_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_suspend,371 exynos_rng_resume);372 373static const struct of_device_id exynos_rng_dt_match[] = {374 {375 .compatible = "samsung,exynos4-rng",376 .data = (const void *)EXYNOS_PRNG_EXYNOS4,377 }, {378 .compatible = "samsung,exynos5250-prng",379 .data = (const void *)EXYNOS_PRNG_EXYNOS5,380 },381 { },382};383MODULE_DEVICE_TABLE(of, exynos_rng_dt_match);384 385static struct platform_driver exynos_rng_driver = {386 .driver = {387 .name = "exynos-rng",388 .pm = &exynos_rng_pm_ops,389 .of_match_table = exynos_rng_dt_match,390 },391 .probe = exynos_rng_probe,392 .remove_new = exynos_rng_remove,393};394 395module_platform_driver(exynos_rng_driver);396 397MODULE_DESCRIPTION("Exynos H/W Random Number Generator driver");398MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");399MODULE_LICENSE("GPL v2");400