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 31 struct npcm_rng { 32 void __iomem *base; 33 struct hwrng rng; 34 }; 35 36 static 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 46 static 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 53 static 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 >= sizeof(u32)) { 62 if (wait) { 63 if (readl_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 ((readl(priv->base + NPCM_RNGCS_REG) & 71 NPCM_RNG_DATA_VALID) == 0) 72 break; 73 } 74 75 *(u32 *)buf = readl(priv->base + NPCM_RNGD_REG); 76 retval += sizeof(u32); 77 buf += sizeof(u32); 78 max -= sizeof(u32); 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 87 static 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 128 static 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 140 static 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 149 static 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 157 static 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 164 static const struct of_device_id rng_dt_id[] = { 165 { .compatible = "nuvoton,npcm750-rng", }, 166 {}, 167 }; 168 MODULE_DEVICE_TABLE(of, rng_dt_id); 169 170 static 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 180 module_platform_driver(npcm_rng_driver); 181 182 MODULE_DESCRIPTION("Nuvoton NPCM Random Number Generator Driver"); 183 MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); 184 MODULE_LICENSE("GPL v2"); 185