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

npcm-rng.c (4416B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2019 Nuvoton Technology corporation.
      3
      4#include <linux/kernel.h>
      5#include <linux/module.h>
      6#include <linux/io.h>
      7#include <linux/iopoll.h>
      8#include <linux/init.h>
      9#include <linux/random.h>
     10#include <linux/err.h>
     11#include <linux/platform_device.h>
     12#include <linux/hw_random.h>
     13#include <linux/delay.h>
     14#include <linux/of_irq.h>
     15#include <linux/pm_runtime.h>
     16
     17#define NPCM_RNGCS_REG		0x00	/* Control and status register */
     18#define NPCM_RNGD_REG		0x04	/* Data register */
     19#define NPCM_RNGMODE_REG	0x08	/* Mode register */
     20
     21#define NPCM_RNG_CLK_SET_25MHZ	GENMASK(4, 3) /* 20-25 MHz */
     22#define NPCM_RNG_DATA_VALID	BIT(1)
     23#define NPCM_RNG_ENABLE		BIT(0)
     24#define NPCM_RNG_M1ROSEL	BIT(1)
     25
     26#define NPCM_RNG_TIMEOUT_USEC	20000
     27#define NPCM_RNG_POLL_USEC	1000
     28
     29#define to_npcm_rng(p)	container_of(p, struct npcm_rng, rng)
     30
     31struct npcm_rng {
     32	void __iomem *base;
     33	struct hwrng rng;
     34};
     35
     36static int npcm_rng_init(struct hwrng *rng)
     37{
     38	struct npcm_rng *priv = to_npcm_rng(rng);
     39
     40	writel(NPCM_RNG_CLK_SET_25MHZ | NPCM_RNG_ENABLE,
     41	       priv->base + NPCM_RNGCS_REG);
     42
     43	return 0;
     44}
     45
     46static void npcm_rng_cleanup(struct hwrng *rng)
     47{
     48	struct npcm_rng *priv = to_npcm_rng(rng);
     49
     50	writel(NPCM_RNG_CLK_SET_25MHZ, priv->base + NPCM_RNGCS_REG);
     51}
     52
     53static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
     54{
     55	struct npcm_rng *priv = to_npcm_rng(rng);
     56	int retval = 0;
     57	int ready;
     58
     59	pm_runtime_get_sync((struct device *)priv->rng.priv);
     60
     61	while (max) {
     62		if (wait) {
     63			if (readb_poll_timeout(priv->base + NPCM_RNGCS_REG,
     64					       ready,
     65					       ready & NPCM_RNG_DATA_VALID,
     66					       NPCM_RNG_POLL_USEC,
     67					       NPCM_RNG_TIMEOUT_USEC))
     68				break;
     69		} else {
     70			if ((readb(priv->base + NPCM_RNGCS_REG) &
     71			    NPCM_RNG_DATA_VALID) == 0)
     72				break;
     73		}
     74
     75		*(u8 *)buf = readb(priv->base + NPCM_RNGD_REG);
     76		retval++;
     77		buf++;
     78		max--;
     79	}
     80
     81	pm_runtime_mark_last_busy((struct device *)priv->rng.priv);
     82	pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv);
     83
     84	return retval || !wait ? retval : -EIO;
     85}
     86
     87static int npcm_rng_probe(struct platform_device *pdev)
     88{
     89	struct npcm_rng *priv;
     90	int ret;
     91
     92	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
     93	if (!priv)
     94		return -ENOMEM;
     95
     96	priv->base = devm_platform_ioremap_resource(pdev, 0);
     97	if (IS_ERR(priv->base))
     98		return PTR_ERR(priv->base);
     99
    100	dev_set_drvdata(&pdev->dev, priv);
    101	pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
    102	pm_runtime_use_autosuspend(&pdev->dev);
    103	pm_runtime_enable(&pdev->dev);
    104
    105#ifndef CONFIG_PM
    106	priv->rng.init = npcm_rng_init;
    107	priv->rng.cleanup = npcm_rng_cleanup;
    108#endif
    109	priv->rng.name = pdev->name;
    110	priv->rng.read = npcm_rng_read;
    111	priv->rng.priv = (unsigned long)&pdev->dev;
    112	priv->rng.quality = 1000;
    113
    114	writel(NPCM_RNG_M1ROSEL, priv->base + NPCM_RNGMODE_REG);
    115
    116	ret = devm_hwrng_register(&pdev->dev, &priv->rng);
    117	if (ret) {
    118		dev_err(&pdev->dev, "Failed to register rng device: %d\n",
    119			ret);
    120		pm_runtime_disable(&pdev->dev);
    121		pm_runtime_set_suspended(&pdev->dev);
    122		return ret;
    123	}
    124
    125	return 0;
    126}
    127
    128static int npcm_rng_remove(struct platform_device *pdev)
    129{
    130	struct npcm_rng *priv = platform_get_drvdata(pdev);
    131
    132	devm_hwrng_unregister(&pdev->dev, &priv->rng);
    133	pm_runtime_disable(&pdev->dev);
    134	pm_runtime_set_suspended(&pdev->dev);
    135
    136	return 0;
    137}
    138
    139#ifdef CONFIG_PM
    140static int npcm_rng_runtime_suspend(struct device *dev)
    141{
    142	struct npcm_rng *priv = dev_get_drvdata(dev);
    143
    144	npcm_rng_cleanup(&priv->rng);
    145
    146	return 0;
    147}
    148
    149static int npcm_rng_runtime_resume(struct device *dev)
    150{
    151	struct npcm_rng *priv = dev_get_drvdata(dev);
    152
    153	return npcm_rng_init(&priv->rng);
    154}
    155#endif
    156
    157static const struct dev_pm_ops npcm_rng_pm_ops = {
    158	SET_RUNTIME_PM_OPS(npcm_rng_runtime_suspend,
    159			   npcm_rng_runtime_resume, NULL)
    160	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
    161				pm_runtime_force_resume)
    162};
    163
    164static const struct of_device_id rng_dt_id[] __maybe_unused = {
    165	{ .compatible = "nuvoton,npcm750-rng",  },
    166	{},
    167};
    168MODULE_DEVICE_TABLE(of, rng_dt_id);
    169
    170static struct platform_driver npcm_rng_driver = {
    171	.driver = {
    172		.name		= "npcm-rng",
    173		.pm		= &npcm_rng_pm_ops,
    174		.of_match_table = of_match_ptr(rng_dt_id),
    175	},
    176	.probe		= npcm_rng_probe,
    177	.remove		= npcm_rng_remove,
    178};
    179
    180module_platform_driver(npcm_rng_driver);
    181
    182MODULE_DESCRIPTION("Nuvoton NPCM Random Number Generator Driver");
    183MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
    184MODULE_LICENSE("GPL v2");