summaryrefslogtreecommitdiff
path: root/drivers/remoteproc/k3_system_controller.c
blob: 88430299c9281ea5703df6421958b5494658e669 (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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// SPDX-License-Identifier: GPL-2.0+
/*
 * Texas Instruments' K3 System Controller Driver
 *
 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
 *	Lokesh Vutla <lokeshvutla@ti.com>
 */

#include <common.h>
#include <dm.h>
#include <remoteproc.h>
#include <errno.h>
#include <mailbox.h>
#include <dm/device_compat.h>
#include <linux/soc/ti/k3-sec-proxy.h>

#define K3_MSG_R5_TO_M3_M3FW			0x8105
#define K3_MSG_M3_TO_R5_CERT_RESULT		0x8805
#define K3_MSG_M3_TO_R5_BOOT_NOTIFICATION	0x000A

#define K3_FLAGS_MSG_CERT_AUTH_PASS		0x555555
#define K3_FLAGS_MSG_CERT_AUTH_FAIL		0xffffff

/**
 * struct k3_sysctrler_msg_hdr - Generic Header for Messages and responses.
 * @cmd_id:	Message ID. One of K3_MSG_*
 * @host_id:	Host ID of the message
 * @seq_ne:	Message identifier indicating a transfer sequence.
 * @flags:	Flags for the message.
 */
struct k3_sysctrler_msg_hdr {
	u16 cmd_id;
	u8 host_id;
	u8 seq_nr;
	u32 flags;
} __packed;

/**
 * struct k3_sysctrler_load_msg - Message format for Firmware loading
 * @hdr:		Generic message hdr
 * @buffer_address:	Address at which firmware is located.
 * @buffer_size:	Size of the firmware.
 */
struct k3_sysctrler_load_msg {
	struct k3_sysctrler_msg_hdr hdr;
	u32 buffer_address;
	u32 buffer_size;
} __packed;

/**
 * struct k3_sysctrler_boot_notification_msg - Message format for boot
 *					       notification
 * @checksum:		Checksum for the entire message
 * @reserved:		Reserved for future use.
 * @hdr:		Generic message hdr
 */
struct k3_sysctrler_boot_notification_msg {
	u16 checksum;
	u16 reserved;
	struct k3_sysctrler_msg_hdr hdr;
} __packed;

/**
 * struct k3_sysctrler_desc - Description of SoC integration.
 * @host_id:	Host identifier representing the compute entity
 * @max_rx_timeout_ms:	Timeout for communication with SoC (in Milliseconds)
 * @max_msg_size: Maximum size of data per message that can be handled.
 */
struct k3_sysctrler_desc {
	u8 host_id;
	int max_rx_timeout_us;
	int max_msg_size;
};

/**
 * struct k3_sysctrler_privdata - Structure representing System Controller data.
 * @chan_tx:	Transmit mailbox channel
 * @chan_rx:	Receive mailbox channel
 * @desc:	SoC description for this instance
 * @seq_nr:	Counter for number of messages sent.
 */
struct k3_sysctrler_privdata {
	struct mbox_chan chan_tx;
	struct mbox_chan chan_rx;
	struct k3_sysctrler_desc *desc;
	u32 seq_nr;
};

static inline
void k3_sysctrler_load_msg_setup(struct k3_sysctrler_load_msg *fw,
				 struct k3_sysctrler_privdata *priv,
				 ulong addr, ulong size)
{
	fw->hdr.cmd_id = K3_MSG_R5_TO_M3_M3FW;
	fw->hdr.host_id = priv->desc->host_id;
	fw->hdr.seq_nr = priv->seq_nr++;
	fw->hdr.flags = 0x0;
	fw->buffer_address = addr;
	fw->buffer_size = size;
}

static int k3_sysctrler_load_response(u32 *buf)
{
	struct k3_sysctrler_load_msg *fw;

	fw = (struct k3_sysctrler_load_msg *)buf;

	/* Check for proper response ID */
	if (fw->hdr.cmd_id != K3_MSG_M3_TO_R5_CERT_RESULT) {
		dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n",
			__func__, K3_MSG_M3_TO_R5_CERT_RESULT, fw->hdr.cmd_id);
		return -EINVAL;
	}

	/* Check for certificate authentication result */
	if (fw->hdr.flags == K3_FLAGS_MSG_CERT_AUTH_FAIL) {
		dev_err(dev, "%s: Firmware certificate authentication failed\n",
			__func__);
		return -EINVAL;
	} else if (fw->hdr.flags != K3_FLAGS_MSG_CERT_AUTH_PASS) {
		dev_err(dev, "%s: Firmware Load response Invalid %d\n",
			__func__, fw->hdr.flags);
		return -EINVAL;
	}

	debug("%s: Firmware authentication passed\n", __func__);

	return 0;
}

static int k3_sysctrler_boot_notification_response(u32 *buf)
{
	struct k3_sysctrler_boot_notification_msg *boot;

	boot = (struct k3_sysctrler_boot_notification_msg *)buf;

	/* ToDo: Verify checksum */

	/* Check for proper response ID */
	if (boot->hdr.cmd_id != K3_MSG_M3_TO_R5_BOOT_NOTIFICATION) {
		dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n",
			__func__, K3_MSG_M3_TO_R5_BOOT_NOTIFICATION,
			boot->hdr.cmd_id);
		return -EINVAL;
	}

	debug("%s: Boot notification received\n", __func__);

	return 0;
}

/**
 * k3_sysctrler_load() - Loadup the K3 remote processor
 * @dev:	corresponding K3 remote processor device
 * @addr:	Address in memory where image binary is stored
 * @size:	Size in bytes of the image binary
 *
 * Return: 0 if all goes good, else appropriate error message.
 */
static int k3_sysctrler_load(struct udevice *dev, ulong addr, ulong size)
{
	struct k3_sysctrler_privdata *priv = dev_get_priv(dev);
	struct k3_sysctrler_load_msg firmware;
	struct k3_sec_proxy_msg msg;
	int ret;

	debug("%s: Loading binary from 0x%08lX, size 0x%08lX\n",
	      __func__, addr, size);

	memset(&firmware, 0, sizeof(firmware));
	memset(&msg, 0, sizeof(msg));

	/* Setup the message */
	k3_sysctrler_load_msg_setup(&firmware, priv, addr, size);
	msg.len = sizeof(firmware);
	msg.buf = (u32 *)&firmware;

	/* Send the message */
	ret = mbox_send(&priv->chan_tx, &msg);
	if (ret) {
		dev_err(dev, "%s: Firmware Loading failed. ret = %d\n",
			__func__, ret);
		return ret;
	}

	/* Receive the response */
	ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us);
	if (ret) {
		dev_err(dev, "%s: Firmware Load response failed. ret = %d\n",
			__func__, ret);
		return ret;
	}

	/* Process the response */
	ret = k3_sysctrler_load_response(msg.buf);
	if (ret)
		return ret;

	debug("%s: Firmware Loaded successfully on dev %s\n",
	      __func__, dev->name);

	return 0;
}

/**
 * k3_sysctrler_start() - Start the remote processor
 *		Note that while technically the K3 system controller starts up
 *		automatically after its firmware got loaded we still want to
 *		utilize the rproc start operation for other startup-related
 *		tasks.
 * @dev:	device to operate upon
 *
 * Return: 0 if all went ok, else return appropriate error
 */
static int k3_sysctrler_start(struct udevice *dev)
{
	struct k3_sysctrler_privdata *priv = dev_get_priv(dev);
	struct k3_sec_proxy_msg msg;
	int ret;

	debug("%s(dev=%p)\n", __func__, dev);

	/* Receive the boot notification. Note that it is sent only once. */
	ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us);
	if (ret) {
		dev_err(dev, "%s: Boot Notification response failed. ret = %d\n",
			__func__, ret);
		return ret;
	}

	/* Process the response */
	ret = k3_sysctrler_boot_notification_response(msg.buf);
	if (ret)
		return ret;

	debug("%s: Boot notification received successfully on dev %s\n",
	      __func__, dev->name);

	return 0;
}

static const struct dm_rproc_ops k3_sysctrler_ops = {
	.load = k3_sysctrler_load,
	.start = k3_sysctrler_start,
};

/**
 * k3_of_to_priv() - generate private data from device tree
 * @dev:	corresponding k3 remote processor device
 * @priv:	pointer to driver specific private data
 *
 * Return: 0 if all goes good, else appropriate error message.
 */
static int k3_of_to_priv(struct udevice *dev,
			 struct k3_sysctrler_privdata *priv)
{
	int ret;

	ret = mbox_get_by_name(dev, "tx", &priv->chan_tx);
	if (ret) {
		dev_err(dev, "%s: Acquiring Tx channel failed. ret = %d\n",
			__func__, ret);
		return ret;
	}

	ret = mbox_get_by_name(dev, "rx", &priv->chan_rx);
	if (ret) {
		dev_err(dev, "%s: Acquiring Rx channel failed. ret = %d\n",
			__func__, ret);
		return ret;
	}

	return 0;
}

/**
 * k3_sysctrler_probe() - Basic probe
 * @dev:	corresponding k3 remote processor device
 *
 * Return: 0 if all goes good, else appropriate error message.
 */
static int k3_sysctrler_probe(struct udevice *dev)
{
	struct k3_sysctrler_privdata *priv;
	int ret;

	debug("%s(dev=%p)\n", __func__, dev);

	priv = dev_get_priv(dev);

	ret = k3_of_to_priv(dev, priv);
	if (ret) {
		dev_err(dev, "%s: Probe failed with error %d\n", __func__, ret);
		return ret;
	}

	priv->desc = (void *)dev_get_driver_data(dev);
	priv->seq_nr = 0;

	return 0;
}

static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = {
	.host_id = 4,				/* HOST_ID_R5_1 */
	.max_rx_timeout_us = 800000,
	.max_msg_size = 60,
};

static const struct udevice_id k3_sysctrler_ids[] = {
	{
		.compatible = "ti,am654-system-controller",
		.data = (ulong)&k3_sysctrler_am654_desc,
	},
	{}
};

U_BOOT_DRIVER(k3_sysctrler) = {
	.name = "k3_system_controller",
	.of_match = k3_sysctrler_ids,
	.id = UCLASS_REMOTEPROC,
	.ops = &k3_sysctrler_ops,
	.probe = k3_sysctrler_probe,
	.priv_auto_alloc_size = sizeof(struct k3_sysctrler_privdata),
};