stm32-rng.c 5.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (c) 2015, Daniel Thompson
 *
 * This file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/hw_random.h>
#include <linux/io.h>
19
#include <linux/iopoll.h>
20 21 22 23 24
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
25
#include <linux/reset.h>
26 27 28 29
#include <linux/slab.h>

#define RNG_CR 0x00
#define RNG_CR_RNGEN BIT(2)
30
#define RNG_CR_CED BIT(5)
31 32 33 34 35 36 37 38 39 40 41 42

#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;
43
	struct reset_control *rst;
44
	bool ced;
45 46 47 48 49 50 51 52 53 54 55 56 57
};

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);
58 59
		/* Manage timeout which is based on timer and take */
		/* care of initial delay time when enabling rng	*/
60
		if (!sr && wait) {
61 62 63 64 65 66 67
			retval = readl_relaxed_poll_timeout_atomic(priv->base
								   + RNG_SR,
								   sr, sr,
								   10, 50000);
			if (retval)
				dev_err((struct device *)priv->rng.priv,
					"%s: timeout %x!\n", __func__, sr);
68 69 70
		}

		/* If error detected or data not ready... */
71 72 73 74
		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);
75
			break;
76
		}
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

		*(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;

101 102 103 104 105
	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);
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

	/* 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);

146 147 148 149 150 151 152
	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);
	}

153 154
	priv->ced = of_property_read_bool(np, "clock-error-detect");

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	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;

	pm_runtime_set_autosuspend_delay(dev, 100);
	pm_runtime_use_autosuspend(dev);
	pm_runtime_enable(dev);

	return devm_hwrng_register(dev, &priv->rng);
}

172 173 174 175 176 177 178
static int stm32_rng_remove(struct platform_device *ofdev)
{
	pm_runtime_disable(&ofdev->dev);

	return 0;
}

179 180 181
#ifdef CONFIG_PM
static int stm32_rng_runtime_suspend(struct device *dev)
{
182
	struct stm32_rng_private *priv = dev_get_drvdata(dev);
183 184 185 186 187 188 189 190

	stm32_rng_cleanup(&priv->rng);

	return 0;
}

static int stm32_rng_runtime_resume(struct device *dev)
{
191
	struct stm32_rng_private *priv = dev_get_drvdata(dev);
192 193 194 195 196

	return stm32_rng_init(&priv->rng);
}
#endif

197 198 199 200 201 202 203
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)
};

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

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,
220
	.remove = stm32_rng_remove,
221 222 223 224 225 226 227
};

module_platform_driver(stm32_rng_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>");
MODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver");