aboutsummaryrefslogtreecommitdiff
path: root/drivers/rng/meson-rng.c
blob: fd2988e91b5a9ca2d0f846b557e76379bd410ca3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
101
102
103
104
105
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
 *
 * Driver for Amlogic hardware random number generator
 */

#include <common.h>
#include <clk.h>
#include <dm.h>
#include <rng.h>
#include <asm/io.h>
#include <linux/iopoll.h>

#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 {
	u32 (*read)(fdt_addr_t base);
};

struct meson_rng_plat {
	fdt_addr_t base;
	struct clk clk;
	struct meson_rng_priv *priv;
};

/**
 * meson_rng_read() - fill buffer with random bytes
 *
 * @buffer:	buffer to receive data
 * @size:	size of buffer
 *
 * Return:	0
 */
static int meson_rng_read(struct udevice *dev, void *data, size_t len)
{
	struct meson_rng_plat *pdata = dev_get_plat(dev);
	struct meson_rng_priv *priv = pdata->priv;
	char *buffer = (char *)data;

	while (len) {
		u32 rand = priv->read(pdata->base);
		size_t step;

		if (len >= 4)
			step = 4;
		else
			step = len;
		memcpy(buffer, &rand, step);
		buffer += step;
		len -= step;
	}
	return 0;
}

static int meson_rng_wait_status(void __iomem *cfg_addr, int bit)
{
	u32 status = 0;
	int ret;

	ret = readl_relaxed_poll_timeout(cfg_addr,
					 status, !(status & bit),
					 10000);
	if (ret)
		return -EBUSY;

	return 0;
}

static u32 meson_common_rng_read(fdt_addr_t base)
{
	return readl(base);
}

static u32 meson_s4_rng_read(fdt_addr_t base)
{
	void __iomem *cfg_addr = (void *)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) {
		pr_err("Seed isn't ready, try again\n");
		return err;
	}

	err = meson_rng_wait_status(cfg_addr, RUN_BIT);
	if (err) {
		pr_err("Can't get random number, try again\n");
		return err;
	}

	return readl_relaxed(base + RNG_S4_DATA);
}

/**
 * meson_rng_probe() - probe rng device
 *
 * @dev:	device
 * Return:	0 if ok
 */
static int meson_rng_probe(struct udevice *dev)
{
	struct meson_rng_plat *pdata = dev_get_plat(dev);
	int err;

	err = clk_enable(&pdata->clk);
	if (err)
		return err;

	pdata->priv = (struct meson_rng_priv *)dev_get_driver_data(dev);

	return 0;
}

/**
 * meson_rng_remove() - deinitialize rng device
 *
 * @dev:	device
 * Return:	0 if ok
 */
static int meson_rng_remove(struct udevice *dev)
{
	struct meson_rng_plat *pdata = dev_get_plat(dev);

	return clk_disable(&pdata->clk);
}

/**
 * meson_rng_of_to_plat() - transfer device tree data to plaform data
 *
 * @dev:	device
 * Return:	0 if ok
 */
static int meson_rng_of_to_plat(struct udevice *dev)
{
	struct meson_rng_plat *pdata = dev_get_plat(dev);
	int err;

	pdata->base = dev_read_addr(dev);
	if (!pdata->base)
		return -ENODEV;

	/* Get optional "core" clock */
	err = clk_get_by_name_optional(dev, "core", &pdata->clk);
	if (err)
		return err;

	return 0;
}

static const struct dm_rng_ops meson_rng_ops = {
	.read = meson_rng_read,
};

static const struct meson_rng_priv meson_rng_priv = {
	.read = meson_common_rng_read,
};

static const struct meson_rng_priv meson_rng_priv_s4 = {
	.read = meson_s4_rng_read,
};

static const struct udevice_id meson_rng_match[] = {
	{
		.compatible = "amlogic,meson-rng",
		.data = (ulong)&meson_rng_priv,
	},
	{
		.compatible = "amlogic,meson-s4-rng",
		.data = (ulong)&meson_rng_priv_s4,
	},
	{},
};

U_BOOT_DRIVER(meson_rng) = {
	.name = "meson-rng",
	.id = UCLASS_RNG,
	.of_match = meson_rng_match,
	.ops = &meson_rng_ops,
	.probe = meson_rng_probe,
	.remove = meson_rng_remove,
	.plat_auto	= sizeof(struct meson_rng_plat),
	.of_to_plat = meson_rng_of_to_plat,
};