162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, Daniel Thompson 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/hw_random.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/iopoll.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/reset.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define RNG_CR 0x00 2162306a36Sopenharmony_ci#define RNG_CR_RNGEN BIT(2) 2262306a36Sopenharmony_ci#define RNG_CR_CED BIT(5) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define RNG_SR 0x04 2562306a36Sopenharmony_ci#define RNG_SR_SEIS BIT(6) 2662306a36Sopenharmony_ci#define RNG_SR_CEIS BIT(5) 2762306a36Sopenharmony_ci#define RNG_SR_DRDY BIT(0) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define RNG_DR 0x08 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct stm32_rng_private { 3262306a36Sopenharmony_ci struct hwrng rng; 3362306a36Sopenharmony_ci void __iomem *base; 3462306a36Sopenharmony_ci struct clk *clk; 3562306a36Sopenharmony_ci struct reset_control *rst; 3662306a36Sopenharmony_ci bool ced; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct stm32_rng_private *priv = 4262306a36Sopenharmony_ci container_of(rng, struct stm32_rng_private, rng); 4362306a36Sopenharmony_ci u32 sr; 4462306a36Sopenharmony_ci int retval = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pm_runtime_get_sync((struct device *) priv->rng.priv); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci while (max >= sizeof(u32)) { 4962306a36Sopenharmony_ci sr = readl_relaxed(priv->base + RNG_SR); 5062306a36Sopenharmony_ci /* Manage timeout which is based on timer and take */ 5162306a36Sopenharmony_ci /* care of initial delay time when enabling rng */ 5262306a36Sopenharmony_ci if (!sr && wait) { 5362306a36Sopenharmony_ci int err; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci err = readl_relaxed_poll_timeout_atomic(priv->base 5662306a36Sopenharmony_ci + RNG_SR, 5762306a36Sopenharmony_ci sr, sr, 5862306a36Sopenharmony_ci 10, 50000); 5962306a36Sopenharmony_ci if (err) 6062306a36Sopenharmony_ci dev_err((struct device *)priv->rng.priv, 6162306a36Sopenharmony_ci "%s: timeout %x!\n", __func__, sr); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* If error detected or data not ready... */ 6562306a36Sopenharmony_ci if (sr != RNG_SR_DRDY) { 6662306a36Sopenharmony_ci if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS), 6762306a36Sopenharmony_ci "bad RNG status - %x\n", sr)) 6862306a36Sopenharmony_ci writel_relaxed(0, priv->base + RNG_SR); 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci *(u32 *)data = readl_relaxed(priv->base + RNG_DR); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci retval += sizeof(u32); 7562306a36Sopenharmony_ci data += sizeof(u32); 7662306a36Sopenharmony_ci max -= sizeof(u32); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pm_runtime_mark_last_busy((struct device *) priv->rng.priv); 8062306a36Sopenharmony_ci pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return retval || !wait ? retval : -EIO; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int stm32_rng_init(struct hwrng *rng) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct stm32_rng_private *priv = 8862306a36Sopenharmony_ci container_of(rng, struct stm32_rng_private, rng); 8962306a36Sopenharmony_ci int err; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci err = clk_prepare_enable(priv->clk); 9262306a36Sopenharmony_ci if (err) 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (priv->ced) 9662306a36Sopenharmony_ci writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR); 9762306a36Sopenharmony_ci else 9862306a36Sopenharmony_ci writel_relaxed(RNG_CR_RNGEN | RNG_CR_CED, 9962306a36Sopenharmony_ci priv->base + RNG_CR); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* clear error indicators */ 10262306a36Sopenharmony_ci writel_relaxed(0, priv->base + RNG_SR); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void stm32_rng_cleanup(struct hwrng *rng) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct stm32_rng_private *priv = 11062306a36Sopenharmony_ci container_of(rng, struct stm32_rng_private, rng); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci writel_relaxed(0, priv->base + RNG_CR); 11362306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int stm32_rng_probe(struct platform_device *ofdev) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct device *dev = &ofdev->dev; 11962306a36Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 12062306a36Sopenharmony_ci struct stm32_rng_private *priv; 12162306a36Sopenharmony_ci struct resource res; 12262306a36Sopenharmony_ci int err; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL); 12562306a36Sopenharmony_ci if (!priv) 12662306a36Sopenharmony_ci return -ENOMEM; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci err = of_address_to_resource(np, 0, &res); 12962306a36Sopenharmony_ci if (err) 13062306a36Sopenharmony_ci return err; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci priv->base = devm_ioremap_resource(dev, &res); 13362306a36Sopenharmony_ci if (IS_ERR(priv->base)) 13462306a36Sopenharmony_ci return PTR_ERR(priv->base); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci priv->clk = devm_clk_get(&ofdev->dev, NULL); 13762306a36Sopenharmony_ci if (IS_ERR(priv->clk)) 13862306a36Sopenharmony_ci return PTR_ERR(priv->clk); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci priv->rst = devm_reset_control_get(&ofdev->dev, NULL); 14162306a36Sopenharmony_ci if (!IS_ERR(priv->rst)) { 14262306a36Sopenharmony_ci reset_control_assert(priv->rst); 14362306a36Sopenharmony_ci udelay(2); 14462306a36Sopenharmony_ci reset_control_deassert(priv->rst); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci priv->ced = of_property_read_bool(np, "clock-error-detect"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci dev_set_drvdata(dev, priv); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci priv->rng.name = dev_driver_string(dev); 15262306a36Sopenharmony_ci#ifndef CONFIG_PM 15362306a36Sopenharmony_ci priv->rng.init = stm32_rng_init; 15462306a36Sopenharmony_ci priv->rng.cleanup = stm32_rng_cleanup; 15562306a36Sopenharmony_ci#endif 15662306a36Sopenharmony_ci priv->rng.read = stm32_rng_read; 15762306a36Sopenharmony_ci priv->rng.priv = (unsigned long) dev; 15862306a36Sopenharmony_ci priv->rng.quality = 900; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, 100); 16162306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 16262306a36Sopenharmony_ci pm_runtime_enable(dev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return devm_hwrng_register(dev, &priv->rng); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int stm32_rng_remove(struct platform_device *ofdev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci pm_runtime_disable(&ofdev->dev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#ifdef CONFIG_PM 17562306a36Sopenharmony_cistatic int stm32_rng_runtime_suspend(struct device *dev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct stm32_rng_private *priv = dev_get_drvdata(dev); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci stm32_rng_cleanup(&priv->rng); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int stm32_rng_runtime_resume(struct device *dev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct stm32_rng_private *priv = dev_get_drvdata(dev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return stm32_rng_init(&priv->rng); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci#endif 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct dev_pm_ops stm32_rng_pm_ops = { 19362306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(stm32_rng_runtime_suspend, 19462306a36Sopenharmony_ci stm32_rng_runtime_resume, NULL) 19562306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 19662306a36Sopenharmony_ci pm_runtime_force_resume) 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic const struct of_device_id stm32_rng_match[] = { 20162306a36Sopenharmony_ci { 20262306a36Sopenharmony_ci .compatible = "st,stm32-rng", 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci {}, 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_rng_match); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic struct platform_driver stm32_rng_driver = { 20962306a36Sopenharmony_ci .driver = { 21062306a36Sopenharmony_ci .name = "stm32-rng", 21162306a36Sopenharmony_ci .pm = &stm32_rng_pm_ops, 21262306a36Sopenharmony_ci .of_match_table = stm32_rng_match, 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci .probe = stm32_rng_probe, 21562306a36Sopenharmony_ci .remove = stm32_rng_remove, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cimodule_platform_driver(stm32_rng_driver); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22162306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>"); 22262306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver"); 223