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

mtk-rng.c (4499B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for Mediatek Hardware Random Number Generator
      4 *
      5 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
      6 */
      7#define MTK_RNG_DEV KBUILD_MODNAME
      8
      9#include <linux/clk.h>
     10#include <linux/delay.h>
     11#include <linux/err.h>
     12#include <linux/hw_random.h>
     13#include <linux/io.h>
     14#include <linux/iopoll.h>
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/platform_device.h>
     19#include <linux/pm_runtime.h>
     20
     21/* Runtime PM autosuspend timeout: */
     22#define RNG_AUTOSUSPEND_TIMEOUT		100
     23
     24#define USEC_POLL			2
     25#define TIMEOUT_POLL			20
     26
     27#define RNG_CTRL			0x00
     28#define RNG_EN				BIT(0)
     29#define RNG_READY			BIT(31)
     30
     31#define RNG_DATA			0x08
     32
     33#define to_mtk_rng(p)	container_of(p, struct mtk_rng, rng)
     34
     35struct mtk_rng {
     36	void __iomem *base;
     37	struct clk *clk;
     38	struct hwrng rng;
     39};
     40
     41static int mtk_rng_init(struct hwrng *rng)
     42{
     43	struct mtk_rng *priv = to_mtk_rng(rng);
     44	u32 val;
     45	int err;
     46
     47	err = clk_prepare_enable(priv->clk);
     48	if (err)
     49		return err;
     50
     51	val = readl(priv->base + RNG_CTRL);
     52	val |= RNG_EN;
     53	writel(val, priv->base + RNG_CTRL);
     54
     55	return 0;
     56}
     57
     58static void mtk_rng_cleanup(struct hwrng *rng)
     59{
     60	struct mtk_rng *priv = to_mtk_rng(rng);
     61	u32 val;
     62
     63	val = readl(priv->base + RNG_CTRL);
     64	val &= ~RNG_EN;
     65	writel(val, priv->base + RNG_CTRL);
     66
     67	clk_disable_unprepare(priv->clk);
     68}
     69
     70static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait)
     71{
     72	struct mtk_rng *priv = to_mtk_rng(rng);
     73	int ready;
     74
     75	ready = readl(priv->base + RNG_CTRL) & RNG_READY;
     76	if (!ready && wait)
     77		readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready,
     78					  ready & RNG_READY, USEC_POLL,
     79					  TIMEOUT_POLL);
     80	return !!ready;
     81}
     82
     83static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
     84{
     85	struct mtk_rng *priv = to_mtk_rng(rng);
     86	int retval = 0;
     87
     88	pm_runtime_get_sync((struct device *)priv->rng.priv);
     89
     90	while (max >= sizeof(u32)) {
     91		if (!mtk_rng_wait_ready(rng, wait))
     92			break;
     93
     94		*(u32 *)buf = readl(priv->base + RNG_DATA);
     95		retval += sizeof(u32);
     96		buf += sizeof(u32);
     97		max -= sizeof(u32);
     98	}
     99
    100	pm_runtime_mark_last_busy((struct device *)priv->rng.priv);
    101	pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv);
    102
    103	return retval || !wait ? retval : -EIO;
    104}
    105
    106static int mtk_rng_probe(struct platform_device *pdev)
    107{
    108	int ret;
    109	struct mtk_rng *priv;
    110
    111	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    112	if (!priv)
    113		return -ENOMEM;
    114
    115	priv->rng.name = pdev->name;
    116#ifndef CONFIG_PM
    117	priv->rng.init = mtk_rng_init;
    118	priv->rng.cleanup = mtk_rng_cleanup;
    119#endif
    120	priv->rng.read = mtk_rng_read;
    121	priv->rng.priv = (unsigned long)&pdev->dev;
    122	priv->rng.quality = 900;
    123
    124	priv->clk = devm_clk_get(&pdev->dev, "rng");
    125	if (IS_ERR(priv->clk)) {
    126		ret = PTR_ERR(priv->clk);
    127		dev_err(&pdev->dev, "no clock for device: %d\n", ret);
    128		return ret;
    129	}
    130
    131	priv->base = devm_platform_ioremap_resource(pdev, 0);
    132	if (IS_ERR(priv->base))
    133		return PTR_ERR(priv->base);
    134
    135	ret = devm_hwrng_register(&pdev->dev, &priv->rng);
    136	if (ret) {
    137		dev_err(&pdev->dev, "failed to register rng device: %d\n",
    138			ret);
    139		return ret;
    140	}
    141
    142	dev_set_drvdata(&pdev->dev, priv);
    143	pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT);
    144	pm_runtime_use_autosuspend(&pdev->dev);
    145	pm_runtime_enable(&pdev->dev);
    146
    147	dev_info(&pdev->dev, "registered RNG driver\n");
    148
    149	return 0;
    150}
    151
    152#ifdef CONFIG_PM
    153static int mtk_rng_runtime_suspend(struct device *dev)
    154{
    155	struct mtk_rng *priv = dev_get_drvdata(dev);
    156
    157	mtk_rng_cleanup(&priv->rng);
    158
    159	return 0;
    160}
    161
    162static int mtk_rng_runtime_resume(struct device *dev)
    163{
    164	struct mtk_rng *priv = dev_get_drvdata(dev);
    165
    166	return mtk_rng_init(&priv->rng);
    167}
    168
    169static const struct dev_pm_ops mtk_rng_pm_ops = {
    170	SET_RUNTIME_PM_OPS(mtk_rng_runtime_suspend,
    171			   mtk_rng_runtime_resume, NULL)
    172	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
    173				pm_runtime_force_resume)
    174};
    175
    176#define MTK_RNG_PM_OPS (&mtk_rng_pm_ops)
    177#else	/* CONFIG_PM */
    178#define MTK_RNG_PM_OPS NULL
    179#endif	/* CONFIG_PM */
    180
    181static const struct of_device_id mtk_rng_match[] = {
    182	{ .compatible = "mediatek,mt7623-rng" },
    183	{},
    184};
    185MODULE_DEVICE_TABLE(of, mtk_rng_match);
    186
    187static struct platform_driver mtk_rng_driver = {
    188	.probe          = mtk_rng_probe,
    189	.driver = {
    190		.name = MTK_RNG_DEV,
    191		.pm = MTK_RNG_PM_OPS,
    192		.of_match_table = mtk_rng_match,
    193	},
    194};
    195
    196module_platform_driver(mtk_rng_driver);
    197
    198MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
    199MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
    200MODULE_LICENSE("GPL");