1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2015
4 * Texas Instruments Incorporated - https://www.ti.com/
5 */
6#define pr_fmt(fmt) "%s: " fmt, __func__
7#include <dm.h>
8#include <errno.h>
9#include <log.h>
10#include <remoteproc.h>
11#include <asm/io.h>
12#include <linux/printk.h>
13
14/**
15 * enum sandbox_state - different device states
16 * @sb_booted:	Entry condition, just booted
17 * @sb_init:	Initialized (basic environment is ready)
18 * @sb_reset:	Held in reset (accessible, but not running)
19 * @sb_loaded:	Loaded with image (but not running)
20 * @sb_running:	Processor is running
21 */
22enum sandbox_state {
23	sb_booted,
24	sb_init,
25	sb_reset,
26	sb_loaded,
27	sb_running
28};
29
30/**
31 * struct sandbox_test_devdata - private data per device
32 * @current_state:	device current state
33 */
34struct sandbox_test_devdata {
35	enum sandbox_state current_state;
36};
37
38/**
39 * sandbox_dev_move_to_state() - statemachine for our dummy device
40 * @dev:	device to switch state
41 * @next_state:	next proposed state
42 *
43 * This tries to follow the following statemachine:
44 *           Entry
45 *            |
46 *            v
47 *         +-------+
48 *     +---+ init  |
49 *     |   |       | <---------------------+
50 *     |   +-------+                       |
51 *     |                                   |
52 *     |                                   |
53 *     |   +--------+                      |
54 * Load|   |  reset |                      |
55 *     |   |        | <----------+         |
56 *     |   +--------+            |         |
57 *     |        |Load            |         |
58 *     |        |                |         |
59 *     |   +----v----+   reset   |         |
60 *     +-> |         |    (opt)  |         |
61 *         |  Loaded +-----------+         |
62 *         |         |                     |
63 *         +----+----+                     |
64 *              | Start                    |
65 *          +---v-----+        (opt)       |
66 *       +->| Running |        Stop        |
67 * Ping  +- |         +--------------------+
68 * (opt)    +---------+
69 *
70 * (is_running does not change state)
71 *
72 * Return: 0 when valid state transition is seen, else returns -EINVAL
73 */
74static int sandbox_dev_move_to_state(struct udevice *dev,
75				     enum sandbox_state next_state)
76{
77	struct sandbox_test_devdata *ddata = dev_get_priv(dev);
78
79	/* No state transition is OK */
80	if (ddata->current_state == next_state)
81		return 0;
82
83	debug("current_state=%d, next_state=%d\n", ddata->current_state,
84	      next_state);
85	switch (ddata->current_state) {
86	case sb_booted:
87		if (next_state == sb_init)
88			goto ok_state;
89		break;
90
91	case sb_init:
92		if (next_state == sb_reset || next_state == sb_loaded)
93			goto ok_state;
94		break;
95
96	case sb_reset:
97		if (next_state == sb_loaded || next_state == sb_init)
98			goto ok_state;
99		break;
100
101	case sb_loaded:
102		if (next_state == sb_reset || next_state == sb_init ||
103		    next_state == sb_running)
104			goto ok_state;
105		break;
106
107	case sb_running:
108		if (next_state == sb_reset || next_state == sb_init)
109			goto ok_state;
110		break;
111	};
112	return -EINVAL;
113
114ok_state:
115	ddata->current_state = next_state;
116	return 0;
117}
118
119/**
120 * sandbox_testproc_probe() - basic probe function
121 * @dev:	test proc device that is being probed.
122 *
123 * Return: 0 if all went ok, else return appropriate error
124 */
125static int sandbox_testproc_probe(struct udevice *dev)
126{
127	struct dm_rproc_uclass_pdata *uc_pdata;
128	struct sandbox_test_devdata *ddata;
129	int ret;
130
131	uc_pdata = dev_get_uclass_plat(dev);
132	ddata = dev_get_priv(dev);
133	if (!ddata) {
134		debug("%s: platform private data missing\n", uc_pdata->name);
135		return -EINVAL;
136	}
137	ret = sandbox_dev_move_to_state(dev, sb_booted);
138	debug("%s: called(%d)\n", uc_pdata->name, ret);
139
140	return ret;
141}
142
143/**
144 * sandbox_testproc_init() - Simple initialization function
145 * @dev:	device to operate upon
146 *
147 * Return: 0 if all went ok, else return appropriate error
148 */
149static int sandbox_testproc_init(struct udevice *dev)
150{
151	struct dm_rproc_uclass_pdata *uc_pdata;
152	int ret;
153
154	uc_pdata = dev_get_uclass_plat(dev);
155
156	ret = sandbox_dev_move_to_state(dev, sb_init);
157
158	debug("%s: called(%d)\n", uc_pdata->name, ret);
159	if (ret)
160		debug("%s init failed\n", uc_pdata->name);
161
162	return ret;
163}
164
165/**
166 * sandbox_testproc_reset() - Reset the remote processor
167 * @dev:	device to operate upon
168 *
169 * Return: 0 if all went ok, else return appropriate error
170 */
171static int sandbox_testproc_reset(struct udevice *dev)
172{
173	struct dm_rproc_uclass_pdata *uc_pdata;
174	int ret;
175
176	uc_pdata = dev_get_uclass_plat(dev);
177
178	ret = sandbox_dev_move_to_state(dev, sb_reset);
179
180	debug("%s: called(%d)\n", uc_pdata->name, ret);
181
182	if (ret)
183		debug("%s reset failed\n", uc_pdata->name);
184	return ret;
185}
186
187/**
188 * sandbox_testproc_load() - (replace: short desc)
189 * @dev:	device to operate upon
190 * @addr:	Address of the binary image to load
191 * @size:	Size (in bytes) of the binary image to load
192 *
193 * Return: 0 if all went ok, else return appropriate error
194 */
195static int sandbox_testproc_load(struct udevice *dev, ulong addr, ulong size)
196{
197	struct dm_rproc_uclass_pdata *uc_pdata;
198	int ret;
199
200	uc_pdata = dev_get_uclass_plat(dev);
201
202	ret = sandbox_dev_move_to_state(dev, sb_loaded);
203
204	debug("%s: called(%d) Loading to %08lX %lu size\n",
205	      uc_pdata->name, ret, addr, size);
206
207	if (ret)
208		debug("%s load failed\n", uc_pdata->name);
209	return ret;
210}
211
212/**
213 * sandbox_testproc_start() - Start the remote processor
214 * @dev:	device to operate upon
215 *
216 * Return: 0 if all went ok, else return appropriate error
217 */
218static int sandbox_testproc_start(struct udevice *dev)
219{
220	struct dm_rproc_uclass_pdata *uc_pdata;
221	int ret;
222
223	uc_pdata = dev_get_uclass_plat(dev);
224
225	ret = sandbox_dev_move_to_state(dev, sb_running);
226
227	debug("%s: called(%d)\n", uc_pdata->name, ret);
228
229	if (ret)
230		debug("%s start failed\n", uc_pdata->name);
231	return ret;
232}
233
234/**
235 * sandbox_testproc_stop() - Stop the remote processor
236 * @dev:	device to operate upon
237 *
238 * Return: 0 if all went ok, else return appropriate error
239 */
240static int sandbox_testproc_stop(struct udevice *dev)
241{
242	struct dm_rproc_uclass_pdata *uc_pdata;
243	int ret;
244
245	uc_pdata = dev_get_uclass_plat(dev);
246
247	ret = sandbox_dev_move_to_state(dev, sb_init);
248
249	debug("%s: called(%d)\n", uc_pdata->name, ret);
250
251	if (ret)
252		debug("%s stop failed\n", uc_pdata->name);
253	return ret;
254}
255
256/**
257 * sandbox_testproc_is_running() - Check if remote processor is running
258 * @dev:	device to operate upon
259 *
260 * Return: 0 if running, 1 if not running
261 */
262static int sandbox_testproc_is_running(struct udevice *dev)
263{
264	struct dm_rproc_uclass_pdata *uc_pdata;
265	struct sandbox_test_devdata *ddata;
266	int ret = 1;
267
268	uc_pdata = dev_get_uclass_plat(dev);
269	ddata = dev_get_priv(dev);
270
271	if (ddata->current_state == sb_running)
272		ret = 0;
273	debug("%s: called(%d)\n", uc_pdata->name, ret);
274
275	return ret;
276}
277
278/**
279 * sandbox_testproc_ping() - Try pinging remote processor
280 * @dev:	device to operate upon
281 *
282 * Return: 0 if running, -EINVAL if not running
283 */
284static int sandbox_testproc_ping(struct udevice *dev)
285{
286	struct dm_rproc_uclass_pdata *uc_pdata;
287	struct sandbox_test_devdata *ddata;
288	int ret;
289
290	uc_pdata = dev_get_uclass_plat(dev);
291	ddata = dev_get_priv(dev);
292
293	if (ddata->current_state == sb_running)
294		ret = 0;
295	else
296		ret = -EINVAL;
297
298	debug("%s: called(%d)\n", uc_pdata->name, ret);
299	if (ret)
300		debug("%s: No response.(Not started?)\n", uc_pdata->name);
301
302	return ret;
303}
304
305#define SANDBOX_RPROC_DEV_TO_PHY_OFFSET	0x1000
306/**
307 * sandbox_testproc_device_to_virt() - Convert device address to virtual address
308 * @dev:	device to operate upon
309 * @da:		device address
310 * @size:	Size of the memory region @da is pointing to
311 * Return: converted virtual address
312 */
313static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da,
314					     ulong size)
315{
316	u64 paddr;
317
318	/* Use a simple offset conversion */
319	paddr = da + SANDBOX_RPROC_DEV_TO_PHY_OFFSET;
320
321	return phys_to_virt(paddr);
322}
323
324static const struct dm_rproc_ops sandbox_testproc_ops = {
325	.init = sandbox_testproc_init,
326	.reset = sandbox_testproc_reset,
327	.load = sandbox_testproc_load,
328	.start = sandbox_testproc_start,
329	.stop = sandbox_testproc_stop,
330	.is_running = sandbox_testproc_is_running,
331	.ping = sandbox_testproc_ping,
332	.device_to_virt = sandbox_testproc_device_to_virt,
333};
334
335static const struct udevice_id sandbox_ids[] = {
336	{.compatible = "sandbox,test-processor"},
337	{}
338};
339
340U_BOOT_DRIVER(sandbox_testproc) = {
341	.name = "sandbox_test_proc",
342	.of_match = sandbox_ids,
343	.id = UCLASS_REMOTEPROC,
344	.ops = &sandbox_testproc_ops,
345	.probe = sandbox_testproc_probe,
346	.priv_auto	= sizeof(struct sandbox_test_devdata),
347};
348
349/* TODO(nm@ti.com): Remove this along with non-DT support */
350static struct dm_rproc_uclass_pdata proc_3_test = {
351	.name = "proc_3_legacy",
352	.mem_type = RPROC_INTERNAL_MEMORY_MAPPED,
353};
354
355U_BOOT_DRVINFO(proc_3_demo) = {
356	.name = "sandbox_test_proc",
357	.plat = &proc_3_test,
358};
359