cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

exynos-trng.c (5467B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * RNG driver for Exynos TRNGs
      4 *
      5 * Author: Łukasz Stelmach <l.stelmach@samsung.com>
      6 *
      7 * Copyright 2017 (c) Samsung Electronics Software, Inc.
      8 *
      9 * Based on the Exynos PRNG driver drivers/crypto/exynos-rng by
     10 * Krzysztof Kozłowski <krzk@kernel.org>
     11 */
     12
     13#include <linux/clk.h>
     14#include <linux/crypto.h>
     15#include <linux/delay.h>
     16#include <linux/err.h>
     17#include <linux/hw_random.h>
     18#include <linux/io.h>
     19#include <linux/iopoll.h>
     20#include <linux/kernel.h>
     21#include <linux/module.h>
     22#include <linux/mod_devicetable.h>
     23#include <linux/platform_device.h>
     24#include <linux/pm_runtime.h>
     25
     26#define EXYNOS_TRNG_CLKDIV         (0x0)
     27
     28#define EXYNOS_TRNG_CTRL           (0x20)
     29#define EXYNOS_TRNG_CTRL_RNGEN     BIT(31)
     30
     31#define EXYNOS_TRNG_POST_CTRL      (0x30)
     32#define EXYNOS_TRNG_ONLINE_CTRL    (0x40)
     33#define EXYNOS_TRNG_ONLINE_STAT    (0x44)
     34#define EXYNOS_TRNG_ONLINE_MAXCHI2 (0x48)
     35#define EXYNOS_TRNG_FIFO_CTRL      (0x50)
     36#define EXYNOS_TRNG_FIFO_0         (0x80)
     37#define EXYNOS_TRNG_FIFO_1         (0x84)
     38#define EXYNOS_TRNG_FIFO_2         (0x88)
     39#define EXYNOS_TRNG_FIFO_3         (0x8c)
     40#define EXYNOS_TRNG_FIFO_4         (0x90)
     41#define EXYNOS_TRNG_FIFO_5         (0x94)
     42#define EXYNOS_TRNG_FIFO_6         (0x98)
     43#define EXYNOS_TRNG_FIFO_7         (0x9c)
     44#define EXYNOS_TRNG_FIFO_LEN       (8)
     45#define EXYNOS_TRNG_CLOCK_RATE     (500000)
     46
     47
     48struct exynos_trng_dev {
     49	struct device    *dev;
     50	void __iomem     *mem;
     51	struct clk       *clk;
     52	struct hwrng rng;
     53};
     54
     55static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
     56			       bool wait)
     57{
     58	struct exynos_trng_dev *trng;
     59	int val;
     60
     61	max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
     62
     63	trng = (struct exynos_trng_dev *)rng->priv;
     64
     65	writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
     66	val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
     67				 val == 0, 200, 1000000);
     68	if (val < 0)
     69		return val;
     70
     71	memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max);
     72
     73	return max;
     74}
     75
     76static int exynos_trng_init(struct hwrng *rng)
     77{
     78	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
     79	unsigned long sss_rate;
     80	u32 val;
     81
     82	sss_rate = clk_get_rate(trng->clk);
     83
     84	/*
     85	 * For most TRNG circuits the clock frequency of under 500 kHz
     86	 * is safe.
     87	 */
     88	val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
     89	if (val > 0x7fff) {
     90		dev_err(trng->dev, "clock divider too large: %d", val);
     91		return -ERANGE;
     92	}
     93	val = val << 1;
     94	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV);
     95
     96	/* Enable the generator. */
     97	val = EXYNOS_TRNG_CTRL_RNGEN;
     98	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL);
     99
    100	/*
    101	 * Disable post-processing. /dev/hwrng is supposed to deliver
    102	 * unprocessed data.
    103	 */
    104	writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL);
    105
    106	return 0;
    107}
    108
    109static int exynos_trng_probe(struct platform_device *pdev)
    110{
    111	struct exynos_trng_dev *trng;
    112	int ret = -ENOMEM;
    113
    114	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
    115	if (!trng)
    116		return ret;
    117
    118	trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
    119				      GFP_KERNEL);
    120	if (!trng->rng.name)
    121		return ret;
    122
    123	trng->rng.init = exynos_trng_init;
    124	trng->rng.read = exynos_trng_do_read;
    125	trng->rng.priv = (unsigned long) trng;
    126
    127	platform_set_drvdata(pdev, trng);
    128	trng->dev = &pdev->dev;
    129
    130	trng->mem = devm_platform_ioremap_resource(pdev, 0);
    131	if (IS_ERR(trng->mem))
    132		return PTR_ERR(trng->mem);
    133
    134	pm_runtime_enable(&pdev->dev);
    135	ret = pm_runtime_resume_and_get(&pdev->dev);
    136	if (ret < 0) {
    137		dev_err(&pdev->dev, "Could not get runtime PM.\n");
    138		goto err_pm_get;
    139	}
    140
    141	trng->clk = devm_clk_get(&pdev->dev, "secss");
    142	if (IS_ERR(trng->clk)) {
    143		ret = PTR_ERR(trng->clk);
    144		dev_err(&pdev->dev, "Could not get clock.\n");
    145		goto err_clock;
    146	}
    147
    148	ret = clk_prepare_enable(trng->clk);
    149	if (ret) {
    150		dev_err(&pdev->dev, "Could not enable the clk.\n");
    151		goto err_clock;
    152	}
    153
    154	ret = devm_hwrng_register(&pdev->dev, &trng->rng);
    155	if (ret) {
    156		dev_err(&pdev->dev, "Could not register hwrng device.\n");
    157		goto err_register;
    158	}
    159
    160	dev_info(&pdev->dev, "Exynos True Random Number Generator.\n");
    161
    162	return 0;
    163
    164err_register:
    165	clk_disable_unprepare(trng->clk);
    166
    167err_clock:
    168	pm_runtime_put_noidle(&pdev->dev);
    169
    170err_pm_get:
    171	pm_runtime_disable(&pdev->dev);
    172
    173	return ret;
    174}
    175
    176static int exynos_trng_remove(struct platform_device *pdev)
    177{
    178	struct exynos_trng_dev *trng =  platform_get_drvdata(pdev);
    179
    180	clk_disable_unprepare(trng->clk);
    181
    182	pm_runtime_put_sync(&pdev->dev);
    183	pm_runtime_disable(&pdev->dev);
    184
    185	return 0;
    186}
    187
    188static int __maybe_unused exynos_trng_suspend(struct device *dev)
    189{
    190	pm_runtime_put_sync(dev);
    191
    192	return 0;
    193}
    194
    195static int __maybe_unused exynos_trng_resume(struct device *dev)
    196{
    197	int ret;
    198
    199	ret = pm_runtime_resume_and_get(dev);
    200	if (ret < 0) {
    201		dev_err(dev, "Could not get runtime PM.\n");
    202		return ret;
    203	}
    204
    205	return 0;
    206}
    207
    208static SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
    209			 exynos_trng_resume);
    210
    211static const struct of_device_id exynos_trng_dt_match[] = {
    212	{
    213		.compatible = "samsung,exynos5250-trng",
    214	},
    215	{ },
    216};
    217MODULE_DEVICE_TABLE(of, exynos_trng_dt_match);
    218
    219static struct platform_driver exynos_trng_driver = {
    220	.driver = {
    221		.name = "exynos-trng",
    222		.pm = &exynos_trng_pm_ops,
    223		.of_match_table = exynos_trng_dt_match,
    224	},
    225	.probe = exynos_trng_probe,
    226	.remove = exynos_trng_remove,
    227};
    228
    229module_platform_driver(exynos_trng_driver);
    230MODULE_AUTHOR("Łukasz Stelmach");
    231MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips");
    232MODULE_LICENSE("GPL v2");