mirror of
https://github.com/raspberrypi/linux.git
synced 2026-01-02 15:53:42 +00:00
The stm32_rng_read() function uses `retval` variable as a counter of generated random bytes. However, the same variable is used to store a result of the polling function in case the driver is waiting until the TRNG is ready. The TRNG generates random numbers by 16B. One loop read 4B. So, the function calls the polling every 16B, i.e. every 4th loop. The `retval` counter is reset on poll call and only number of bytes read after the last poll call is returned to the caller. The remaining sampled random bytes (for example 48 out of 64 in case 64 bytes are read) are not used. Use different variable to store the polling function result and do not overwrite `retval` counter. Cc: Oleg Karfich <oleg.karfich@wago.com> Signed-off-by: Tomas Marek <tomas.marek@elrest.cz> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
222 lines
5.0 KiB
C
222 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2015, Daniel Thompson
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/hw_random.h>
|
|
#include <linux/io.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define RNG_CR 0x00
|
|
#define RNG_CR_RNGEN BIT(2)
|
|
#define RNG_CR_CED BIT(5)
|
|
|
|
#define RNG_SR 0x04
|
|
#define RNG_SR_SEIS BIT(6)
|
|
#define RNG_SR_CEIS BIT(5)
|
|
#define RNG_SR_DRDY BIT(0)
|
|
|
|
#define RNG_DR 0x08
|
|
|
|
struct stm32_rng_private {
|
|
struct hwrng rng;
|
|
void __iomem *base;
|
|
struct clk *clk;
|
|
struct reset_control *rst;
|
|
bool ced;
|
|
};
|
|
|
|
static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
|
{
|
|
struct stm32_rng_private *priv =
|
|
container_of(rng, struct stm32_rng_private, rng);
|
|
u32 sr;
|
|
int retval = 0;
|
|
|
|
pm_runtime_get_sync((struct device *) priv->rng.priv);
|
|
|
|
while (max > sizeof(u32)) {
|
|
sr = readl_relaxed(priv->base + RNG_SR);
|
|
/* Manage timeout which is based on timer and take */
|
|
/* care of initial delay time when enabling rng */
|
|
if (!sr && wait) {
|
|
int ret;
|
|
|
|
ret = readl_relaxed_poll_timeout_atomic(priv->base
|
|
+ RNG_SR,
|
|
sr, sr,
|
|
10, 50000);
|
|
if (ret)
|
|
dev_err((struct device *)priv->rng.priv,
|
|
"%s: timeout %x!\n", __func__, sr);
|
|
}
|
|
|
|
/* If error detected or data not ready... */
|
|
if (sr != RNG_SR_DRDY) {
|
|
if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS),
|
|
"bad RNG status - %x\n", sr))
|
|
writel_relaxed(0, priv->base + RNG_SR);
|
|
break;
|
|
}
|
|
|
|
*(u32 *)data = readl_relaxed(priv->base + RNG_DR);
|
|
|
|
retval += sizeof(u32);
|
|
data += sizeof(u32);
|
|
max -= sizeof(u32);
|
|
}
|
|
|
|
pm_runtime_mark_last_busy((struct device *) priv->rng.priv);
|
|
pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv);
|
|
|
|
return retval || !wait ? retval : -EIO;
|
|
}
|
|
|
|
static int stm32_rng_init(struct hwrng *rng)
|
|
{
|
|
struct stm32_rng_private *priv =
|
|
container_of(rng, struct stm32_rng_private, rng);
|
|
int err;
|
|
|
|
err = clk_prepare_enable(priv->clk);
|
|
if (err)
|
|
return err;
|
|
|
|
if (priv->ced)
|
|
writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR);
|
|
else
|
|
writel_relaxed(RNG_CR_RNGEN | RNG_CR_CED,
|
|
priv->base + RNG_CR);
|
|
|
|
/* clear error indicators */
|
|
writel_relaxed(0, priv->base + RNG_SR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_rng_cleanup(struct hwrng *rng)
|
|
{
|
|
struct stm32_rng_private *priv =
|
|
container_of(rng, struct stm32_rng_private, rng);
|
|
|
|
writel_relaxed(0, priv->base + RNG_CR);
|
|
clk_disable_unprepare(priv->clk);
|
|
}
|
|
|
|
static int stm32_rng_probe(struct platform_device *ofdev)
|
|
{
|
|
struct device *dev = &ofdev->dev;
|
|
struct device_node *np = ofdev->dev.of_node;
|
|
struct stm32_rng_private *priv;
|
|
struct resource res;
|
|
int err;
|
|
|
|
priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
err = of_address_to_resource(np, 0, &res);
|
|
if (err)
|
|
return err;
|
|
|
|
priv->base = devm_ioremap_resource(dev, &res);
|
|
if (IS_ERR(priv->base))
|
|
return PTR_ERR(priv->base);
|
|
|
|
priv->clk = devm_clk_get(&ofdev->dev, NULL);
|
|
if (IS_ERR(priv->clk))
|
|
return PTR_ERR(priv->clk);
|
|
|
|
priv->rst = devm_reset_control_get(&ofdev->dev, NULL);
|
|
if (!IS_ERR(priv->rst)) {
|
|
reset_control_assert(priv->rst);
|
|
udelay(2);
|
|
reset_control_deassert(priv->rst);
|
|
}
|
|
|
|
priv->ced = of_property_read_bool(np, "clock-error-detect");
|
|
|
|
dev_set_drvdata(dev, priv);
|
|
|
|
priv->rng.name = dev_driver_string(dev);
|
|
#ifndef CONFIG_PM
|
|
priv->rng.init = stm32_rng_init;
|
|
priv->rng.cleanup = stm32_rng_cleanup;
|
|
#endif
|
|
priv->rng.read = stm32_rng_read;
|
|
priv->rng.priv = (unsigned long) dev;
|
|
priv->rng.quality = 900;
|
|
|
|
pm_runtime_set_autosuspend_delay(dev, 100);
|
|
pm_runtime_use_autosuspend(dev);
|
|
pm_runtime_enable(dev);
|
|
|
|
return devm_hwrng_register(dev, &priv->rng);
|
|
}
|
|
|
|
static int stm32_rng_remove(struct platform_device *ofdev)
|
|
{
|
|
pm_runtime_disable(&ofdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int stm32_rng_runtime_suspend(struct device *dev)
|
|
{
|
|
struct stm32_rng_private *priv = dev_get_drvdata(dev);
|
|
|
|
stm32_rng_cleanup(&priv->rng);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_rng_runtime_resume(struct device *dev)
|
|
{
|
|
struct stm32_rng_private *priv = dev_get_drvdata(dev);
|
|
|
|
return stm32_rng_init(&priv->rng);
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops stm32_rng_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(stm32_rng_runtime_suspend,
|
|
stm32_rng_runtime_resume, NULL)
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
pm_runtime_force_resume)
|
|
};
|
|
|
|
|
|
static const struct of_device_id stm32_rng_match[] = {
|
|
{
|
|
.compatible = "st,stm32-rng",
|
|
},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, stm32_rng_match);
|
|
|
|
static struct platform_driver stm32_rng_driver = {
|
|
.driver = {
|
|
.name = "stm32-rng",
|
|
.pm = &stm32_rng_pm_ops,
|
|
.of_match_table = stm32_rng_match,
|
|
},
|
|
.probe = stm32_rng_probe,
|
|
.remove = stm32_rng_remove,
|
|
};
|
|
|
|
module_platform_driver(stm32_rng_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>");
|
|
MODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver");
|