1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2017-18 Linaro Limited
3//
4// Based on msm-rng.c and downstream driver
5
6#include <crypto/internal/rng.h>
7#include <linux/acpi.h>
8#include <linux/clk.h>
9#include <linux/crypto.h>
10#include <linux/io.h>
11#include <linux/iopoll.h>
12#include <linux/module.h>
13#include <linux/of.h>
14#include <linux/platform_device.h>
15
16/* Device specific register offsets */
17#define PRNG_DATA_OUT		0x0000
18#define PRNG_STATUS		0x0004
19#define PRNG_LFSR_CFG		0x0100
20#define PRNG_CONFIG		0x0104
21
22/* Device specific register masks and config values */
23#define PRNG_LFSR_CFG_MASK	0x0000ffff
24#define PRNG_LFSR_CFG_CLOCKS	0x0000dddd
25#define PRNG_CONFIG_HW_ENABLE	BIT(1)
26#define PRNG_STATUS_DATA_AVAIL	BIT(0)
27
28#define WORD_SZ			4
29
30struct qcom_rng {
31	struct mutex lock;
32	void __iomem *base;
33	struct clk *clk;
34	unsigned int skip_init;
35};
36
37struct qcom_rng_ctx {
38	struct qcom_rng *rng;
39};
40
41static struct qcom_rng *qcom_rng_dev;
42
43static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
44{
45	unsigned int currsize = 0;
46	u32 val;
47	int ret;
48
49	/* read random data from hardware */
50	do {
51		ret = readl_poll_timeout(rng->base + PRNG_STATUS, val,
52					 val & PRNG_STATUS_DATA_AVAIL,
53					 200, 10000);
54		if (ret)
55			return ret;
56
57		val = readl_relaxed(rng->base + PRNG_DATA_OUT);
58		if (!val)
59			return -EINVAL;
60
61		if ((max - currsize) >= WORD_SZ) {
62			memcpy(data, &val, WORD_SZ);
63			data += WORD_SZ;
64			currsize += WORD_SZ;
65		} else {
66			/* copy only remaining bytes */
67			memcpy(data, &val, max - currsize);
68			break;
69		}
70	} while (currsize < max);
71
72	return 0;
73}
74
75static int qcom_rng_generate(struct crypto_rng *tfm,
76			     const u8 *src, unsigned int slen,
77			     u8 *dstn, unsigned int dlen)
78{
79	struct qcom_rng_ctx *ctx = crypto_rng_ctx(tfm);
80	struct qcom_rng *rng = ctx->rng;
81	int ret;
82
83	ret = clk_prepare_enable(rng->clk);
84	if (ret)
85		return ret;
86
87	mutex_lock(&rng->lock);
88
89	ret = qcom_rng_read(rng, dstn, dlen);
90
91	mutex_unlock(&rng->lock);
92	clk_disable_unprepare(rng->clk);
93
94	return ret;
95}
96
97static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
98			 unsigned int slen)
99{
100	return 0;
101}
102
103static int qcom_rng_enable(struct qcom_rng *rng)
104{
105	u32 val;
106	int ret;
107
108	ret = clk_prepare_enable(rng->clk);
109	if (ret)
110		return ret;
111
112	/* Enable PRNG only if it is not already enabled */
113	val = readl_relaxed(rng->base + PRNG_CONFIG);
114	if (val & PRNG_CONFIG_HW_ENABLE)
115		goto already_enabled;
116
117	val = readl_relaxed(rng->base + PRNG_LFSR_CFG);
118	val &= ~PRNG_LFSR_CFG_MASK;
119	val |= PRNG_LFSR_CFG_CLOCKS;
120	writel(val, rng->base + PRNG_LFSR_CFG);
121
122	val = readl_relaxed(rng->base + PRNG_CONFIG);
123	val |= PRNG_CONFIG_HW_ENABLE;
124	writel(val, rng->base + PRNG_CONFIG);
125
126already_enabled:
127	clk_disable_unprepare(rng->clk);
128
129	return 0;
130}
131
132static int qcom_rng_init(struct crypto_tfm *tfm)
133{
134	struct qcom_rng_ctx *ctx = crypto_tfm_ctx(tfm);
135
136	ctx->rng = qcom_rng_dev;
137
138	if (!ctx->rng->skip_init)
139		return qcom_rng_enable(ctx->rng);
140
141	return 0;
142}
143
144static struct rng_alg qcom_rng_alg = {
145	.generate	= qcom_rng_generate,
146	.seed		= qcom_rng_seed,
147	.seedsize	= 0,
148	.base		= {
149		.cra_name		= "stdrng",
150		.cra_driver_name	= "qcom-rng",
151		.cra_flags		= CRYPTO_ALG_TYPE_RNG,
152		.cra_priority		= 300,
153		.cra_ctxsize		= sizeof(struct qcom_rng_ctx),
154		.cra_module		= THIS_MODULE,
155		.cra_init		= qcom_rng_init,
156	}
157};
158
159static int qcom_rng_probe(struct platform_device *pdev)
160{
161	struct qcom_rng *rng;
162	int ret;
163
164	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
165	if (!rng)
166		return -ENOMEM;
167
168	platform_set_drvdata(pdev, rng);
169	mutex_init(&rng->lock);
170
171	rng->base = devm_platform_ioremap_resource(pdev, 0);
172	if (IS_ERR(rng->base))
173		return PTR_ERR(rng->base);
174
175	/* ACPI systems have clk already on, so skip clk_get */
176	if (!has_acpi_companion(&pdev->dev)) {
177		rng->clk = devm_clk_get(&pdev->dev, "core");
178		if (IS_ERR(rng->clk))
179			return PTR_ERR(rng->clk);
180	}
181
182
183	rng->skip_init = (unsigned long)device_get_match_data(&pdev->dev);
184
185	qcom_rng_dev = rng;
186	ret = crypto_register_rng(&qcom_rng_alg);
187	if (ret) {
188		dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret);
189		qcom_rng_dev = NULL;
190	}
191
192	return ret;
193}
194
195static int qcom_rng_remove(struct platform_device *pdev)
196{
197	crypto_unregister_rng(&qcom_rng_alg);
198
199	qcom_rng_dev = NULL;
200
201	return 0;
202}
203
204#if IS_ENABLED(CONFIG_ACPI)
205static const struct acpi_device_id qcom_rng_acpi_match[] = {
206	{ .id = "QCOM8160", .driver_data = 1 },
207	{}
208};
209MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match);
210#endif
211
212static const struct of_device_id qcom_rng_of_match[] = {
213	{ .compatible = "qcom,prng", .data = (void *)0},
214	{ .compatible = "qcom,prng-ee", .data = (void *)1},
215	{}
216};
217MODULE_DEVICE_TABLE(of, qcom_rng_of_match);
218
219static struct platform_driver qcom_rng_driver = {
220	.probe = qcom_rng_probe,
221	.remove =  qcom_rng_remove,
222	.driver = {
223		.name = KBUILD_MODNAME,
224		.of_match_table = of_match_ptr(qcom_rng_of_match),
225		.acpi_match_table = ACPI_PTR(qcom_rng_acpi_match),
226	}
227};
228module_platform_driver(qcom_rng_driver);
229
230MODULE_ALIAS("platform:" KBUILD_MODNAME);
231MODULE_DESCRIPTION("Qualcomm random number generator driver");
232MODULE_LICENSE("GPL v2");
233