1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2019 Nuvoton Technology corporation.
4 #include <linux/kernel.h>
5 #include <linux/module.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>
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 */
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)
26 #define NPCM_RNG_TIMEOUT_USEC 20000
27 #define NPCM_RNG_POLL_USEC 1000
29 #define to_npcm_rng(p) container_of(p, struct npcm_rng, rng)
36 static int npcm_rng_init(struct hwrng
*rng
)
38 struct npcm_rng
*priv
= to_npcm_rng(rng
);
40 writel(NPCM_RNG_CLK_SET_25MHZ
| NPCM_RNG_ENABLE
,
41 priv
->base
+ NPCM_RNGCS_REG
);
46 static void npcm_rng_cleanup(struct hwrng
*rng
)
48 struct npcm_rng
*priv
= to_npcm_rng(rng
);
50 writel(NPCM_RNG_CLK_SET_25MHZ
, priv
->base
+ NPCM_RNGCS_REG
);
53 static int npcm_rng_read(struct hwrng
*rng
, void *buf
, size_t max
, bool wait
)
55 struct npcm_rng
*priv
= to_npcm_rng(rng
);
59 pm_runtime_get_sync((struct device
*)priv
->rng
.priv
);
61 while (max
>= sizeof(u32
)) {
63 if (readl_poll_timeout(priv
->base
+ NPCM_RNGCS_REG
,
65 ready
& NPCM_RNG_DATA_VALID
,
67 NPCM_RNG_TIMEOUT_USEC
))
70 if ((readl(priv
->base
+ NPCM_RNGCS_REG
) &
71 NPCM_RNG_DATA_VALID
) == 0)
75 *(u32
*)buf
= readl(priv
->base
+ NPCM_RNGD_REG
);
76 retval
+= sizeof(u32
);
81 pm_runtime_mark_last_busy((struct device
*)priv
->rng
.priv
);
82 pm_runtime_put_sync_autosuspend((struct device
*)priv
->rng
.priv
);
84 return retval
|| !wait
? retval
: -EIO
;
87 static int npcm_rng_probe(struct platform_device
*pdev
)
89 struct npcm_rng
*priv
;
92 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
96 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
97 if (IS_ERR(priv
->base
))
98 return PTR_ERR(priv
->base
);
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
);
106 priv
->rng
.init
= npcm_rng_init
;
107 priv
->rng
.cleanup
= npcm_rng_cleanup
;
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;
114 writel(NPCM_RNG_M1ROSEL
, priv
->base
+ NPCM_RNGMODE_REG
);
116 ret
= devm_hwrng_register(&pdev
->dev
, &priv
->rng
);
118 dev_err(&pdev
->dev
, "Failed to register rng device: %d\n",
120 pm_runtime_disable(&pdev
->dev
);
121 pm_runtime_set_suspended(&pdev
->dev
);
128 static int npcm_rng_remove(struct platform_device
*pdev
)
130 struct npcm_rng
*priv
= platform_get_drvdata(pdev
);
132 devm_hwrng_unregister(&pdev
->dev
, &priv
->rng
);
133 pm_runtime_disable(&pdev
->dev
);
134 pm_runtime_set_suspended(&pdev
->dev
);
140 static int npcm_rng_runtime_suspend(struct device
*dev
)
142 struct npcm_rng
*priv
= dev_get_drvdata(dev
);
144 npcm_rng_cleanup(&priv
->rng
);
149 static int npcm_rng_runtime_resume(struct device
*dev
)
151 struct npcm_rng
*priv
= dev_get_drvdata(dev
);
153 return npcm_rng_init(&priv
->rng
);
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
)
164 static const struct of_device_id rng_dt_id
[] = {
165 { .compatible
= "nuvoton,npcm750-rng", },
168 MODULE_DEVICE_TABLE(of
, rng_dt_id
);
170 static struct platform_driver npcm_rng_driver
= {
173 .pm
= &npcm_rng_pm_ops
,
174 .of_match_table
= of_match_ptr(rng_dt_id
),
176 .probe
= npcm_rng_probe
,
177 .remove
= npcm_rng_remove
,
180 module_platform_driver(npcm_rng_driver
);
182 MODULE_DESCRIPTION("Nuvoton NPCM Random Number Generator Driver");
183 MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
184 MODULE_LICENSE("GPL v2");