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");