diff options
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r-- | drivers/char/hw_random/Kconfig | 2 | ||||
-rw-r--r-- | drivers/char/hw_random/bcm2835-rng.c | 2 | ||||
-rw-r--r-- | drivers/char/hw_random/hisi-rng.c | 2 | ||||
-rw-r--r-- | drivers/char/hw_random/imx-rngc.c | 10 | ||||
-rw-r--r-- | drivers/char/hw_random/jh7110-trng.c | 2 | ||||
-rw-r--r-- | drivers/char/hw_random/ks-sa-rng.c | 26 | ||||
-rw-r--r-- | drivers/char/hw_random/meson-rng.c | 80 | ||||
-rw-r--r-- | drivers/char/hw_random/mpfs-rng.c | 2 | ||||
-rw-r--r-- | drivers/char/hw_random/n2-drv.c | 10 | ||||
-rw-r--r-- | drivers/char/hw_random/nomadik-rng.c | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/octeon-rng.c | 6 | ||||
-rw-r--r-- | drivers/char/hw_random/st-rng.c | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/stm32-rng.c | 512 | ||||
-rw-r--r-- | drivers/char/hw_random/xgene-rng.c | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/xiphera-trng.c | 2 |
15 files changed, 529 insertions, 130 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 8de74dcfa1..442c40efb2 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -37,7 +37,7 @@ config HW_RANDOM_TIMERIOMEM config HW_RANDOM_INTEL tristate "Intel HW Random Number Generator support" - depends on (X86 || IA64 || COMPILE_TEST) && PCI + depends on (X86 || COMPILE_TEST) && PCI default HW_RANDOM help This driver provides kernel-side support for the Random Number diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index 4c08efe7f3..b03e803006 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -149,8 +149,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); - /* map peripheral */ priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) diff --git a/drivers/char/hw_random/hisi-rng.c b/drivers/char/hw_random/hisi-rng.c index 96438f85ca..b6f27566e0 100644 --- a/drivers/char/hw_random/hisi-rng.c +++ b/drivers/char/hw_random/hisi-rng.c @@ -79,8 +79,6 @@ static int hisi_rng_probe(struct platform_device *pdev) if (!rng) return -ENOMEM; - platform_set_drvdata(pdev, rng); - rng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rng->base)) return PTR_ERR(rng->base); diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index e4b385b01b..118a72acb9 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -51,8 +51,8 @@ #define RNGC_ERROR_STATUS_STAT_ERR 0x00000008 -#define RNGC_TIMEOUT 3000 /* 3 sec */ - +#define RNGC_SELFTEST_TIMEOUT 2500 /* us */ +#define RNGC_SEED_TIMEOUT 200 /* ms */ static bool self_test = true; module_param(self_test, bool, 0); @@ -110,7 +110,8 @@ static int imx_rngc_self_test(struct imx_rngc *rngc) cmd = readl(rngc->base + RNGC_COMMAND); writel(cmd | RNGC_CMD_SELF_TEST, rngc->base + RNGC_COMMAND); - ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); + ret = wait_for_completion_timeout(&rngc->rng_op_done, + usecs_to_jiffies(RNGC_SELFTEST_TIMEOUT)); imx_rngc_irq_mask_clear(rngc); if (!ret) return -ETIMEDOUT; @@ -182,7 +183,8 @@ static int imx_rngc_init(struct hwrng *rng) cmd = readl(rngc->base + RNGC_COMMAND); writel(cmd | RNGC_CMD_SEED, rngc->base + RNGC_COMMAND); - ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); + ret = wait_for_completion_timeout(&rngc->rng_op_done, + msecs_to_jiffies(RNGC_SEED_TIMEOUT)); if (!ret) { ret = -ETIMEDOUT; goto err; diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 38474d48a2..b1f94e3c0c 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -300,7 +300,7 @@ static int starfive_trng_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name, (void *)trng); if (ret) - return dev_err_probe(&pdev->dev, irq, + return dev_err_probe(&pdev->dev, ret, "Failed to register interrupt handler\n"); trng->hclk = devm_clk_get(&pdev->dev, "hclk"); diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c index 2f2f21f1b6..dff7b9db70 100644 --- a/drivers/char/hw_random/ks-sa-rng.c +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -81,7 +81,6 @@ struct trng_regs { }; struct ks_sa_rng { - struct device *dev; struct hwrng rng; struct clk *clk; struct regmap *regmap_cfg; @@ -113,8 +112,7 @@ static unsigned int refill_delay_ns(unsigned long clk_rate) static int ks_sa_rng_init(struct hwrng *rng) { u32 value; - struct device *dev = (struct device *)rng->priv; - struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng); unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk); /* Enable RNG module */ @@ -153,8 +151,7 @@ static int ks_sa_rng_init(struct hwrng *rng) static void ks_sa_rng_cleanup(struct hwrng *rng) { - struct device *dev = (struct device *)rng->priv; - struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng); /* Disable RNG */ writel(0, &ks_sa_rng->reg_rng->control); @@ -164,8 +161,7 @@ static void ks_sa_rng_cleanup(struct hwrng *rng) static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) { - struct device *dev = (struct device *)rng->priv; - struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng); /* Read random data */ data[0] = readl(&ks_sa_rng->reg_rng->output_l); @@ -179,8 +175,7 @@ static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) static int ks_sa_rng_data_present(struct hwrng *rng, int wait) { - struct device *dev = (struct device *)rng->priv; - struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng); u64 now = ktime_get_ns(); u32 ready; @@ -217,7 +212,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev) if (!ks_sa_rng) return -ENOMEM; - ks_sa_rng->dev = dev; ks_sa_rng->rng = (struct hwrng) { .name = "ks_sa_hwrng", .init = ks_sa_rng_init, @@ -225,7 +219,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev) .data_present = ks_sa_rng_data_present, .cleanup = ks_sa_rng_cleanup, }; - ks_sa_rng->rng.priv = (unsigned long)dev; ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ks_sa_rng->reg_rng)) @@ -235,21 +228,16 @@ static int ks_sa_rng_probe(struct platform_device *pdev) syscon_regmap_lookup_by_phandle(dev->of_node, "ti,syscon-sa-cfg"); - if (IS_ERR(ks_sa_rng->regmap_cfg)) { - dev_err(dev, "syscon_node_to_regmap failed\n"); - return -EINVAL; - } + if (IS_ERR(ks_sa_rng->regmap_cfg)) + return dev_err_probe(dev, -EINVAL, "syscon_node_to_regmap failed\n"); pm_runtime_enable(dev); ret = pm_runtime_resume_and_get(dev); if (ret < 0) { - dev_err(dev, "Failed to enable SA power-domain\n"); pm_runtime_disable(dev); - return ret; + return dev_err_probe(dev, ret, "Failed to enable SA power-domain\n"); } - platform_set_drvdata(pdev, ks_sa_rng); - return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng); } diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c index a4eb8e35f1..75225eb9fe 100644 --- a/drivers/char/hw_random/meson-rng.c +++ b/drivers/char/hw_random/meson-rng.c @@ -13,12 +13,23 @@ #include <linux/types.h> #include <linux/of.h> #include <linux/clk.h> +#include <linux/iopoll.h> -#define RNG_DATA 0x00 +#define RNG_DATA 0x00 +#define RNG_S4_DATA 0x08 +#define RNG_S4_CFG 0x00 + +#define RUN_BIT BIT(0) +#define SEED_READY_STS_BIT BIT(31) + +struct meson_rng_priv { + int (*read)(struct hwrng *rng, void *buf, size_t max, bool wait); +}; struct meson_rng_data { void __iomem *base; struct hwrng rng; + struct device *dev; }; static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) @@ -31,16 +42,62 @@ static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return sizeof(u32); } +static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) +{ + u32 status = 0; + int ret; + + ret = readl_relaxed_poll_timeout_atomic(cfg_addr, + status, !(status & bit), + 10, 10000); + if (ret) + return -EBUSY; + + return 0; +} + +static int meson_s4_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct meson_rng_data *data = + container_of(rng, struct meson_rng_data, rng); + + void __iomem *cfg_addr = data->base + RNG_S4_CFG; + int err; + + writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); + + err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); + if (err) { + dev_err(data->dev, "Seed isn't ready, try again\n"); + return err; + } + + err = meson_rng_wait_status(cfg_addr, RUN_BIT); + if (err) { + dev_err(data->dev, "Can't get random number, try again\n"); + return err; + } + + *(u32 *)buf = readl_relaxed(data->base + RNG_S4_DATA); + + return sizeof(u32); +} + static int meson_rng_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct meson_rng_data *data; struct clk *core_clk; + const struct meson_rng_priv *priv; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + priv = device_get_match_data(&pdev->dev); + if (!priv) + return -ENODEV; + data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) return PTR_ERR(data->base); @@ -51,13 +108,30 @@ static int meson_rng_probe(struct platform_device *pdev) "Failed to get core clock\n"); data->rng.name = pdev->name; - data->rng.read = meson_rng_read; + data->rng.read = priv->read; + + data->dev = &pdev->dev; return devm_hwrng_register(dev, &data->rng); } +static const struct meson_rng_priv meson_rng_priv = { + .read = meson_rng_read, +}; + +static const struct meson_rng_priv meson_rng_priv_s4 = { + .read = meson_s4_rng_read, +}; + static const struct of_device_id meson_rng_of_match[] = { - { .compatible = "amlogic,meson-rng", }, + { + .compatible = "amlogic,meson-rng", + .data = (void *)&meson_rng_priv, + }, + { + .compatible = "amlogic,meson-s4-rng", + .data = (void *)&meson_rng_priv_s4, + }, {}, }; MODULE_DEVICE_TABLE(of, meson_rng_of_match); diff --git a/drivers/char/hw_random/mpfs-rng.c b/drivers/char/hw_random/mpfs-rng.c index c6972734ae..0994024daa 100644 --- a/drivers/char/hw_random/mpfs-rng.c +++ b/drivers/char/hw_random/mpfs-rng.c @@ -79,8 +79,6 @@ static int mpfs_rng_probe(struct platform_device *pdev) rng_priv->rng.read = mpfs_rng_read; rng_priv->rng.name = pdev->name; - platform_set_drvdata(pdev, rng_priv); - ret = devm_hwrng_register(&pdev->dev, &rng_priv->rng); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to register MPFS hwrng\n"); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 73e4081464..aaae16b984 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -14,7 +14,8 @@ #include <linux/hw_random.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <asm/hypervisor.h> @@ -695,20 +696,15 @@ static void n2rng_driver_version(void) static const struct of_device_id n2rng_match[]; static int n2rng_probe(struct platform_device *op) { - const struct of_device_id *match; int err = -ENOMEM; struct n2rng *np; - match = of_match_device(n2rng_match, &op->dev); - if (!match) - return -EINVAL; - n2rng_driver_version(); np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL); if (!np) goto out; np->op = op; - np->data = (struct n2rng_template *)match->data; + np->data = (struct n2rng_template *)device_get_match_data(&op->dev); INIT_DELAYED_WORK(&np->work, n2rng_work); diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 8c6a40d6ce..a2009fc4ad 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -88,4 +88,5 @@ static struct amba_driver nmk_rng_driver = { module_amba_driver(nmk_rng_driver); +MODULE_DESCRIPTION("ST-Ericsson Nomadik Random Number Generator"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index 8561a09b46..412f544050 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -33,7 +33,7 @@ static int octeon_rng_init(struct hwrng *rng) ctl.u64 = 0; ctl.s.ent_en = 1; /* Enable the entropy source. */ ctl.s.rng_en = 1; /* Enable the RNG hardware. */ - cvmx_write_csr((__force u64)p->control_status, ctl.u64); + cvmx_write_csr((unsigned long)p->control_status, ctl.u64); return 0; } @@ -44,14 +44,14 @@ static void octeon_rng_cleanup(struct hwrng *rng) ctl.u64 = 0; /* Disable everything. */ - cvmx_write_csr((__force u64)p->control_status, ctl.u64); + cvmx_write_csr((unsigned long)p->control_status, ctl.u64); } static int octeon_rng_data_read(struct hwrng *rng, u32 *data) { struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); - *data = cvmx_read64_uint32((__force u64)p->result); + *data = cvmx_read64_uint32((unsigned long)p->result); return sizeof(u32); } diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c index 6e9dfac9fc..23749817d8 100644 --- a/drivers/char/hw_random/st-rng.c +++ b/drivers/char/hw_random/st-rng.c @@ -121,4 +121,5 @@ static struct platform_driver st_rng_driver = { module_platform_driver(st_rng_driver); MODULE_AUTHOR("Pankaj Dev <pankaj.dev@st.com>"); +MODULE_DESCRIPTION("ST Microelectronics HW Random Number Generator"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index efb6a9f9a1..efd6edcd70 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -17,60 +17,234 @@ #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_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; +}; -#define RNG_DR 0x08 +/** + * 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); + struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng); + unsigned int i = 0; + int retval = 0, err = 0; u32 sr; - int retval = 0; 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 rng */ + /* + * Manage timeout which is based on timer and take + * care of initial delay time when enabling the RNG. + */ if (!sr && wait) { - int err; - err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, sr, sr, 10, 50000); - if (err) + 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 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)) + 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); - break; } + /* 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); @@ -82,54 +256,265 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) 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; - 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); + 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) { + clk_disable_unprepare(priv->clk); + 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 void stm32_rng_cleanup(struct hwrng *rng) +static int stm32_rng_remove(struct platform_device *ofdev) { - struct stm32_rng_private *priv = - container_of(rng, struct stm32_rng_private, rng); + 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; - writel_relaxed(0, priv->base + RNG_CR); + 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; - int err; + struct resource *res; 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); + priv->base = devm_platform_get_and_ioremap_resource(ofdev, 0, &res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -145,14 +530,16 @@ static int stm32_rng_probe(struct platform_device *ofdev) } 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); -#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; @@ -164,51 +551,10 @@ static int stm32_rng_probe(struct platform_device *ofdev) 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, + .pm = pm_ptr(&stm32_rng_pm_ops), .of_match_table = stm32_rng_match, }, .probe = stm32_rng_probe, diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index 99f4e86ac3..7382724bf5 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -321,7 +321,6 @@ static int xgene_rng_probe(struct platform_device *pdev) return -ENOMEM; ctx->dev = &pdev->dev; - platform_set_drvdata(pdev, ctx); ctx->csr_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->csr_base)) diff --git a/drivers/char/hw_random/xiphera-trng.c b/drivers/char/hw_random/xiphera-trng.c index 2c586d1fe8..4af64f76c8 100644 --- a/drivers/char/hw_random/xiphera-trng.c +++ b/drivers/char/hw_random/xiphera-trng.c @@ -121,8 +121,6 @@ static int xiphera_trng_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, trng); - return 0; } |