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>
12 #include <linux/platform_device.h>
13 #include <linux/hw_random.h>
14 #include <linux/delay.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_62_5MHZ BIT(2) /* 60-80 MHz */
22 #define NPCM_RNG_CLK_SET_25MHZ GENMASK(4, 3) /* 20-25 MHz */
23 #define NPCM_RNG_DATA_VALID BIT(1)
24 #define NPCM_RNG_ENABLE BIT(0)
25 #define NPCM_RNG_M1ROSEL BIT(1)
27 #define NPCM_RNG_TIMEOUT_USEC 20000
28 #define NPCM_RNG_POLL_USEC 1000
30 #define to_npcm_rng(p) container_of(p, struct npcm_rng, rng)
38 static int npcm_rng_init(struct hwrng
*rng
)
40 struct npcm_rng
*priv
= to_npcm_rng(rng
);
42 writel(priv
->clkp
| NPCM_RNG_ENABLE
, priv
->base
+ NPCM_RNGCS_REG
);
47 static void npcm_rng_cleanup(struct hwrng
*rng
)
49 struct npcm_rng
*priv
= to_npcm_rng(rng
);
51 writel(priv
->clkp
, priv
->base
+ NPCM_RNGCS_REG
);
54 static int npcm_rng_read(struct hwrng
*rng
, void *buf
, size_t max
, bool wait
)
56 struct npcm_rng
*priv
= to_npcm_rng(rng
);
60 pm_runtime_get_sync((struct device
*)priv
->rng
.priv
);
64 if (readb_poll_timeout(priv
->base
+ NPCM_RNGCS_REG
,
66 ready
& NPCM_RNG_DATA_VALID
,
68 NPCM_RNG_TIMEOUT_USEC
))
71 if ((readb(priv
->base
+ NPCM_RNGCS_REG
) &
72 NPCM_RNG_DATA_VALID
) == 0)
76 *(u8
*)buf
= readb(priv
->base
+ NPCM_RNGD_REG
);
82 pm_runtime_mark_last_busy((struct device
*)priv
->rng
.priv
);
83 pm_runtime_put_sync_autosuspend((struct device
*)priv
->rng
.priv
);
85 return retval
|| !wait
? retval
: -EIO
;
88 static int npcm_rng_probe(struct platform_device
*pdev
)
90 struct npcm_rng
*priv
;
93 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
97 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
98 if (IS_ERR(priv
->base
))
99 return PTR_ERR(priv
->base
);
101 dev_set_drvdata(&pdev
->dev
, priv
);
102 pm_runtime_set_autosuspend_delay(&pdev
->dev
, 100);
103 pm_runtime_use_autosuspend(&pdev
->dev
);
104 pm_runtime_enable(&pdev
->dev
);
107 priv
->rng
.init
= npcm_rng_init
;
108 priv
->rng
.cleanup
= npcm_rng_cleanup
;
110 priv
->rng
.name
= pdev
->name
;
111 priv
->rng
.read
= npcm_rng_read
;
112 priv
->rng
.priv
= (unsigned long)&pdev
->dev
;
113 priv
->clkp
= (u32
)(uintptr_t)of_device_get_match_data(&pdev
->dev
);
115 writel(NPCM_RNG_M1ROSEL
, priv
->base
+ NPCM_RNGMODE_REG
);
117 ret
= devm_hwrng_register(&pdev
->dev
, &priv
->rng
);
119 dev_err(&pdev
->dev
, "Failed to register rng device: %d\n",
121 pm_runtime_disable(&pdev
->dev
);
122 pm_runtime_set_suspended(&pdev
->dev
);
129 static void npcm_rng_remove(struct platform_device
*pdev
)
131 struct npcm_rng
*priv
= platform_get_drvdata(pdev
);
133 devm_hwrng_unregister(&pdev
->dev
, &priv
->rng
);
134 pm_runtime_disable(&pdev
->dev
);
135 pm_runtime_set_suspended(&pdev
->dev
);
139 static int npcm_rng_runtime_suspend(struct device
*dev
)
141 struct npcm_rng
*priv
= dev_get_drvdata(dev
);
143 npcm_rng_cleanup(&priv
->rng
);
148 static int npcm_rng_runtime_resume(struct device
*dev
)
150 struct npcm_rng
*priv
= dev_get_drvdata(dev
);
152 return npcm_rng_init(&priv
->rng
);
156 static const struct dev_pm_ops npcm_rng_pm_ops
= {
157 SET_RUNTIME_PM_OPS(npcm_rng_runtime_suspend
,
158 npcm_rng_runtime_resume
, NULL
)
159 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
160 pm_runtime_force_resume
)
163 static const struct of_device_id rng_dt_id
[] __maybe_unused
= {
164 { .compatible
= "nuvoton,npcm750-rng",
165 .data
= (void *)NPCM_RNG_CLK_SET_25MHZ
},
166 { .compatible
= "nuvoton,npcm845-rng",
167 .data
= (void *)NPCM_RNG_CLK_SET_62_5MHZ
},
170 MODULE_DEVICE_TABLE(of
, rng_dt_id
);
172 static struct platform_driver npcm_rng_driver
= {
175 .pm
= &npcm_rng_pm_ops
,
176 .of_match_table
= of_match_ptr(rng_dt_id
),
178 .probe
= npcm_rng_probe
,
179 .remove
= npcm_rng_remove
,
182 module_platform_driver(npcm_rng_driver
);
184 MODULE_DESCRIPTION("Nuvoton NPCM Random Number Generator Driver");
185 MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
186 MODULE_LICENSE("GPL v2");