Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Gatien Chevallier | 1192 | 48.57% | 7 | 31.82% |
Daniel R Thompson | 1055 | 42.99% | 2 | 9.09% |
Lionel Debieve | 174 | 7.09% | 6 | 27.27% |
Maxime Coquelin | 17 | 0.69% | 1 | 4.55% |
Tomas Marek | 7 | 0.29% | 2 | 9.09% |
Joshua Henderson | 3 | 0.12% | 1 | 4.55% |
Julia Lawall | 3 | 0.12% | 1 | 4.55% |
Thomas Gleixner | 2 | 0.08% | 1 | 4.55% |
Rob Herring | 1 | 0.04% | 1 | 4.55% |
Total | 2454 | 22 |
// 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.h> #include <linux/of_address.h> #include <linux/platform_device.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_CR_CONFIG1 GENMASK(11, 8) #define RNG_CR_NISTC BIT(12) #define RNG_CR_CONFIG2 GENMASK(15, 13) #define RNG_CR_CLKDIV_SHIFT 16 #define RNG_CR_CLKDIV GENMASK(19, 16) #define RNG_CR_CONFIG3 GENMASK(25, 20) #define RNG_CR_CONDRST BIT(30) #define RNG_CR_CONFLOCK BIT(31) #define RNG_CR_ENTROPY_SRC_MASK (RNG_CR_CONFIG1 | RNG_CR_NISTC | RNG_CR_CONFIG2 | RNG_CR_CONFIG3) #define RNG_CR_CONFIG_MASK (RNG_CR_ENTROPY_SRC_MASK | RNG_CR_CED | RNG_CR_CLKDIV) #define RNG_SR 0x04 #define RNG_SR_DRDY BIT(0) #define RNG_SR_CECS BIT(1) #define RNG_SR_SECS BIT(2) #define RNG_SR_CEIS BIT(5) #define RNG_SR_SEIS BIT(6) #define RNG_DR 0x08 #define RNG_NSCR 0x0C #define RNG_NSCR_MASK GENMASK(17, 0) #define RNG_HTCR 0x10 #define RNG_NB_RECOVER_TRIES 3 struct stm32_rng_data { uint max_clock_rate; u32 cr; u32 nscr; u32 htcr; bool has_cond_reset; }; /** * struct stm32_rng_config - RNG configuration data * * @cr: RNG configuration. 0 means default hardware RNG configuration * @nscr: Noise sources control configuration. * @htcr: Health tests configuration. */ struct stm32_rng_config { u32 cr; u32 nscr; u32 htcr; }; struct stm32_rng_private { struct hwrng rng; void __iomem *base; struct clk *clk; struct reset_control *rst; struct stm32_rng_config pm_conf; const struct stm32_rng_data *data; bool ced; bool lock_conf; }; /* * Extracts from the STM32 RNG specification when RNG supports CONDRST. * * When a noise source (or seed) error occurs, the RNG stops generating * random numbers and sets to “1” both SEIS and SECS bits to indicate * that a seed error occurred. (...) * * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield * description for details). This step is needed only if SECS is set. * Indeed, when SEIS is set and SECS is cleared it means RNG performed * the reset automatically (auto-reset). * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST * to be cleared in the RNG_CR register, then confirm that SEIS is * cleared in the RNG_SR register. Otherwise just clear SEIS bit in * the RNG_SR register. * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be * cleared by RNG. The random number generation is now back to normal. */ static int stm32_rng_conceal_seed_error_cond_reset(struct stm32_rng_private *priv) { struct device *dev = (struct device *)priv->rng.priv; u32 sr = readl_relaxed(priv->base + RNG_SR); u32 cr = readl_relaxed(priv->base + RNG_CR); int err; if (sr & RNG_SR_SECS) { /* Conceal by resetting the subsystem (step 1.) */ writel_relaxed(cr | RNG_CR_CONDRST, priv->base + RNG_CR); writel_relaxed(cr & ~RNG_CR_CONDRST, priv->base + RNG_CR); } else { /* RNG auto-reset (step 2.) */ writel_relaxed(sr & ~RNG_SR_SEIS, priv->base + RNG_SR); goto end; } err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, cr, !(cr & RNG_CR_CONDRST), 10, 100000); if (err) { dev_err(dev, "%s: timeout %x\n", __func__, sr); return err; } /* Check SEIS is cleared (step 2.) */ if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS) return -EINVAL; err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, sr, !(sr & RNG_SR_SECS), 10, 100000); if (err) { dev_err(dev, "%s: timeout %x\n", __func__, sr); return err; } end: return 0; } /* * Extracts from the STM32 RNG specification, when CONDRST is not supported * * When a noise source (or seed) error occurs, the RNG stops generating * random numbers and sets to “1” both SEIS and SECS bits to indicate * that a seed error occurred. (...) * * The following sequence shall be used to fully recover from a seed * error after the RNG initialization: * 1. Clear the SEIS bit by writing it to “0”. * 2. Read out 12 words from the RNG_DR register, and discard each of * them in order to clean the pipeline. * 3. Confirm that SEIS is still cleared. Random number generation is * back to normal. */ static int stm32_rng_conceal_seed_error_sw_reset(struct stm32_rng_private *priv) { unsigned int i = 0; u32 sr = readl_relaxed(priv->base + RNG_SR); writel_relaxed(sr & ~RNG_SR_SEIS, priv->base + RNG_SR); for (i = 12; i != 0; i--) (void)readl_relaxed(priv->base + RNG_DR); if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS) return -EINVAL; return 0; } static int stm32_rng_conceal_seed_error(struct hwrng *rng) { struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng); dev_dbg((struct device *)priv->rng.priv, "Concealing seed error\n"); if (priv->data->has_cond_reset) return stm32_rng_conceal_seed_error_cond_reset(priv); else return stm32_rng_conceal_seed_error_sw_reset(priv); }; 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); unsigned int i = 0; int retval = 0, err = 0; u32 sr; pm_runtime_get_sync((struct device *) priv->rng.priv); if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS) stm32_rng_conceal_seed_error(rng); 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 the RNG. */ if (!sr && wait) { err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, sr, sr, 10, 50000); if (err) { dev_err((struct device *)priv->rng.priv, "%s: timeout %x!\n", __func__, sr); break; } } else if (!sr) { /* The FIFO is being filled up */ break; } if (sr != RNG_SR_DRDY) { if (sr & RNG_SR_SEIS) { err = stm32_rng_conceal_seed_error(rng); i++; if (err && i > RNG_NB_RECOVER_TRIES) { dev_err((struct device *)priv->rng.priv, "Couldn't recover from seed error\n"); return -ENOTRECOVERABLE; } continue; } if (WARN_ONCE((sr & RNG_SR_CEIS), "RNG clock too slow - %x\n", sr)) writel_relaxed(0, priv->base + RNG_SR); } /* Late seed error case: DR being 0 is an error status */ *(u32 *)data = readl_relaxed(priv->base + RNG_DR); if (!*(u32 *)data) { err = stm32_rng_conceal_seed_error(rng); i++; if (err && i > RNG_NB_RECOVER_TRIES) { dev_err((struct device *)priv->rng.priv, "Couldn't recover from seed error"); return -ENOTRECOVERABLE; } continue; } i = 0; 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 uint stm32_rng_clock_freq_restrain(struct hwrng *rng) { struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng); unsigned long clock_rate = 0; uint clock_div = 0; clock_rate = clk_get_rate(priv->clk); /* * Get the exponent to apply on the CLKDIV field in RNG_CR register * No need to handle the case when clock-div > 0xF as it is physically * impossible */ while ((clock_rate >> clock_div) > priv->data->max_clock_rate) clock_div++; pr_debug("RNG clk rate : %lu\n", clk_get_rate(priv->clk) >> clock_div); return clock_div; } static int stm32_rng_init(struct hwrng *rng) { struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng); int err; u32 reg; err = clk_prepare_enable(priv->clk); if (err) return err; /* clear error indicators */ writel_relaxed(0, priv->base + RNG_SR); reg = readl_relaxed(priv->base + RNG_CR); /* * Keep default RNG configuration if none was specified. * 0 is an invalid value as it disables all entropy sources. */ if (priv->data->has_cond_reset && priv->data->cr) { uint clock_div = stm32_rng_clock_freq_restrain(rng); reg &= ~RNG_CR_CONFIG_MASK; reg |= RNG_CR_CONDRST | (priv->data->cr & RNG_CR_ENTROPY_SRC_MASK) | (clock_div << RNG_CR_CLKDIV_SHIFT); if (priv->ced) reg &= ~RNG_CR_CED; else reg |= RNG_CR_CED; writel_relaxed(reg, priv->base + RNG_CR); /* Health tests and noise control registers */ writel_relaxed(priv->data->htcr, priv->base + RNG_HTCR); writel_relaxed(priv->data->nscr & RNG_NSCR_MASK, priv->base + RNG_NSCR); reg &= ~RNG_CR_CONDRST; reg |= RNG_CR_RNGEN; if (priv->lock_conf) reg |= RNG_CR_CONFLOCK; writel_relaxed(reg, priv->base + RNG_CR); err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, reg, (!(reg & RNG_CR_CONDRST)), 10, 50000); if (err) { dev_err((struct device *)priv->rng.priv, "%s: timeout %x!\n", __func__, reg); return -EINVAL; } } else { /* Handle all RNG versions by checking if conditional reset should be set */ if (priv->data->has_cond_reset) reg |= RNG_CR_CONDRST; if (priv->ced) reg &= ~RNG_CR_CED; else reg |= RNG_CR_CED; writel_relaxed(reg, priv->base + RNG_CR); if (priv->data->has_cond_reset) reg &= ~RNG_CR_CONDRST; reg |= RNG_CR_RNGEN; writel_relaxed(reg, priv->base + RNG_CR); } err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, reg, reg & RNG_SR_DRDY, 10, 100000); if (err | (reg & ~RNG_SR_DRDY)) { clk_disable_unprepare(priv->clk); dev_err((struct device *)priv->rng.priv, "%s: timeout:%x SR: %x!\n", __func__, err, reg); return -EINVAL; } return 0; } static int stm32_rng_remove(struct platform_device *ofdev) { pm_runtime_disable(&ofdev->dev); return 0; } static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev) { struct stm32_rng_private *priv = dev_get_drvdata(dev); u32 reg; reg = readl_relaxed(priv->base + RNG_CR); reg &= ~RNG_CR_RNGEN; writel_relaxed(reg, priv->base + RNG_CR); clk_disable_unprepare(priv->clk); return 0; } static int __maybe_unused stm32_rng_suspend(struct device *dev) { struct stm32_rng_private *priv = dev_get_drvdata(dev); if (priv->data->has_cond_reset) { priv->pm_conf.nscr = readl_relaxed(priv->base + RNG_NSCR); priv->pm_conf.htcr = readl_relaxed(priv->base + RNG_HTCR); } /* Do not save that RNG is enabled as it will be handled at resume */ priv->pm_conf.cr = readl_relaxed(priv->base + RNG_CR) & ~RNG_CR_RNGEN; writel_relaxed(priv->pm_conf.cr, priv->base + RNG_CR); clk_disable_unprepare(priv->clk); return 0; } static int __maybe_unused stm32_rng_runtime_resume(struct device *dev) { struct stm32_rng_private *priv = dev_get_drvdata(dev); int err; u32 reg; err = clk_prepare_enable(priv->clk); if (err) return err; /* Clean error indications */ writel_relaxed(0, priv->base + RNG_SR); reg = readl_relaxed(priv->base + RNG_CR); reg |= RNG_CR_RNGEN; writel_relaxed(reg, priv->base + RNG_CR); return 0; } static int __maybe_unused stm32_rng_resume(struct device *dev) { struct stm32_rng_private *priv = dev_get_drvdata(dev); int err; u32 reg; err = clk_prepare_enable(priv->clk); if (err) return err; /* Clean error indications */ writel_relaxed(0, priv->base + RNG_SR); if (priv->data->has_cond_reset) { /* * Correct configuration in bits [29:4] must be set in the same * access that set RNG_CR_CONDRST bit. Else config setting is * not taken into account. CONFIGLOCK bit must also be unset but * it is not handled at the moment. */ writel_relaxed(priv->pm_conf.cr | RNG_CR_CONDRST, priv->base + RNG_CR); writel_relaxed(priv->pm_conf.nscr, priv->base + RNG_NSCR); writel_relaxed(priv->pm_conf.htcr, priv->base + RNG_HTCR); reg = readl_relaxed(priv->base + RNG_CR); reg |= RNG_CR_RNGEN; reg &= ~RNG_CR_CONDRST; writel_relaxed(reg, priv->base + RNG_CR); err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, reg, reg & ~RNG_CR_CONDRST, 10, 100000); if (err) { clk_disable_unprepare(priv->clk); dev_err((struct device *)priv->rng.priv, "%s: timeout:%x CR: %x!\n", __func__, err, reg); return -EINVAL; } } else { reg = priv->pm_conf.cr; reg |= RNG_CR_RNGEN; writel_relaxed(reg, priv->base + RNG_CR); } return 0; } static const struct dev_pm_ops __maybe_unused stm32_rng_pm_ops = { SET_RUNTIME_PM_OPS(stm32_rng_runtime_suspend, stm32_rng_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(stm32_rng_suspend, stm32_rng_resume) }; static const struct stm32_rng_data stm32mp13_rng_data = { .has_cond_reset = true, .max_clock_rate = 48000000, .cr = 0x00F00D00, .nscr = 0x2B5BB, .htcr = 0x969D, }; static const struct stm32_rng_data stm32_rng_data = { .has_cond_reset = false, .max_clock_rate = 3000000, }; static const struct of_device_id stm32_rng_match[] = { { .compatible = "st,stm32mp13-rng", .data = &stm32mp13_rng_data, }, { .compatible = "st,stm32-rng", .data = &stm32_rng_data, }, {}, }; MODULE_DEVICE_TABLE(of, stm32_rng_match); 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; priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->base = devm_platform_get_and_ioremap_resource(ofdev, 0, &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"); priv->lock_conf = of_property_read_bool(np, "st,rng-lock-conf"); priv->data = of_device_get_match_data(dev); if (!priv->data) return -ENODEV; dev_set_drvdata(dev, priv); priv->rng.name = dev_driver_string(dev); priv->rng.init = stm32_rng_init; 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 struct platform_driver stm32_rng_driver = { .driver = { .name = "stm32-rng", .pm = pm_ptr(&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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1