]>
Commit | Line | Data |
---|---|---|
1ad190bf LV |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Texas Instruments' K3 System Controller Driver | |
4 | * | |
5 | * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ | |
6 | * Lokesh Vutla <lokeshvutla@ti.com> | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <dm.h> | |
f7ae49fc | 11 | #include <log.h> |
1ad190bf LV |
12 | #include <remoteproc.h> |
13 | #include <errno.h> | |
14 | #include <mailbox.h> | |
336d4615 | 15 | #include <dm/device_compat.h> |
1ad190bf LV |
16 | #include <linux/soc/ti/k3-sec-proxy.h> |
17 | ||
18 | #define K3_MSG_R5_TO_M3_M3FW 0x8105 | |
19 | #define K3_MSG_M3_TO_R5_CERT_RESULT 0x8805 | |
20 | #define K3_MSG_M3_TO_R5_BOOT_NOTIFICATION 0x000A | |
21 | ||
22 | #define K3_FLAGS_MSG_CERT_AUTH_PASS 0x555555 | |
23 | #define K3_FLAGS_MSG_CERT_AUTH_FAIL 0xffffff | |
24 | ||
25 | /** | |
26 | * struct k3_sysctrler_msg_hdr - Generic Header for Messages and responses. | |
27 | * @cmd_id: Message ID. One of K3_MSG_* | |
28 | * @host_id: Host ID of the message | |
29 | * @seq_ne: Message identifier indicating a transfer sequence. | |
30 | * @flags: Flags for the message. | |
31 | */ | |
32 | struct k3_sysctrler_msg_hdr { | |
33 | u16 cmd_id; | |
34 | u8 host_id; | |
35 | u8 seq_nr; | |
36 | u32 flags; | |
37 | } __packed; | |
38 | ||
39 | /** | |
40 | * struct k3_sysctrler_load_msg - Message format for Firmware loading | |
41 | * @hdr: Generic message hdr | |
42 | * @buffer_address: Address at which firmware is located. | |
43 | * @buffer_size: Size of the firmware. | |
44 | */ | |
45 | struct k3_sysctrler_load_msg { | |
46 | struct k3_sysctrler_msg_hdr hdr; | |
47 | u32 buffer_address; | |
48 | u32 buffer_size; | |
49 | } __packed; | |
50 | ||
51 | /** | |
52 | * struct k3_sysctrler_boot_notification_msg - Message format for boot | |
53 | * notification | |
54 | * @checksum: Checksum for the entire message | |
55 | * @reserved: Reserved for future use. | |
56 | * @hdr: Generic message hdr | |
57 | */ | |
58 | struct k3_sysctrler_boot_notification_msg { | |
59 | u16 checksum; | |
60 | u16 reserved; | |
61 | struct k3_sysctrler_msg_hdr hdr; | |
62 | } __packed; | |
63 | ||
64 | /** | |
65 | * struct k3_sysctrler_desc - Description of SoC integration. | |
66 | * @host_id: Host identifier representing the compute entity | |
67 | * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) | |
68 | * @max_msg_size: Maximum size of data per message that can be handled. | |
69 | */ | |
70 | struct k3_sysctrler_desc { | |
71 | u8 host_id; | |
72 | int max_rx_timeout_us; | |
73 | int max_msg_size; | |
74 | }; | |
75 | ||
76 | /** | |
77 | * struct k3_sysctrler_privdata - Structure representing System Controller data. | |
78 | * @chan_tx: Transmit mailbox channel | |
79 | * @chan_rx: Receive mailbox channel | |
80 | * @desc: SoC description for this instance | |
81 | * @seq_nr: Counter for number of messages sent. | |
82 | */ | |
83 | struct k3_sysctrler_privdata { | |
84 | struct mbox_chan chan_tx; | |
85 | struct mbox_chan chan_rx; | |
86 | struct k3_sysctrler_desc *desc; | |
87 | u32 seq_nr; | |
88 | }; | |
89 | ||
90 | static inline | |
91 | void k3_sysctrler_load_msg_setup(struct k3_sysctrler_load_msg *fw, | |
92 | struct k3_sysctrler_privdata *priv, | |
93 | ulong addr, ulong size) | |
94 | { | |
95 | fw->hdr.cmd_id = K3_MSG_R5_TO_M3_M3FW; | |
96 | fw->hdr.host_id = priv->desc->host_id; | |
97 | fw->hdr.seq_nr = priv->seq_nr++; | |
98 | fw->hdr.flags = 0x0; | |
99 | fw->buffer_address = addr; | |
100 | fw->buffer_size = size; | |
101 | } | |
102 | ||
d7bd29c9 | 103 | static int k3_sysctrler_load_response(struct udevice *dev, u32 *buf) |
1ad190bf LV |
104 | { |
105 | struct k3_sysctrler_load_msg *fw; | |
106 | ||
107 | fw = (struct k3_sysctrler_load_msg *)buf; | |
108 | ||
109 | /* Check for proper response ID */ | |
110 | if (fw->hdr.cmd_id != K3_MSG_M3_TO_R5_CERT_RESULT) { | |
111 | dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", | |
112 | __func__, K3_MSG_M3_TO_R5_CERT_RESULT, fw->hdr.cmd_id); | |
113 | return -EINVAL; | |
114 | } | |
115 | ||
116 | /* Check for certificate authentication result */ | |
117 | if (fw->hdr.flags == K3_FLAGS_MSG_CERT_AUTH_FAIL) { | |
118 | dev_err(dev, "%s: Firmware certificate authentication failed\n", | |
119 | __func__); | |
120 | return -EINVAL; | |
121 | } else if (fw->hdr.flags != K3_FLAGS_MSG_CERT_AUTH_PASS) { | |
122 | dev_err(dev, "%s: Firmware Load response Invalid %d\n", | |
123 | __func__, fw->hdr.flags); | |
124 | return -EINVAL; | |
125 | } | |
126 | ||
127 | debug("%s: Firmware authentication passed\n", __func__); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
d7bd29c9 SA |
132 | static int k3_sysctrler_boot_notification_response(struct udevice *dev, |
133 | u32 *buf) | |
1ad190bf LV |
134 | { |
135 | struct k3_sysctrler_boot_notification_msg *boot; | |
136 | ||
137 | boot = (struct k3_sysctrler_boot_notification_msg *)buf; | |
138 | ||
139 | /* ToDo: Verify checksum */ | |
140 | ||
141 | /* Check for proper response ID */ | |
142 | if (boot->hdr.cmd_id != K3_MSG_M3_TO_R5_BOOT_NOTIFICATION) { | |
143 | dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", | |
144 | __func__, K3_MSG_M3_TO_R5_BOOT_NOTIFICATION, | |
145 | boot->hdr.cmd_id); | |
146 | return -EINVAL; | |
147 | } | |
148 | ||
149 | debug("%s: Boot notification received\n", __func__); | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | /** | |
155 | * k3_sysctrler_load() - Loadup the K3 remote processor | |
156 | * @dev: corresponding K3 remote processor device | |
157 | * @addr: Address in memory where image binary is stored | |
158 | * @size: Size in bytes of the image binary | |
159 | * | |
160 | * Return: 0 if all goes good, else appropriate error message. | |
161 | */ | |
162 | static int k3_sysctrler_load(struct udevice *dev, ulong addr, ulong size) | |
163 | { | |
164 | struct k3_sysctrler_privdata *priv = dev_get_priv(dev); | |
165 | struct k3_sysctrler_load_msg firmware; | |
166 | struct k3_sec_proxy_msg msg; | |
167 | int ret; | |
168 | ||
169 | debug("%s: Loading binary from 0x%08lX, size 0x%08lX\n", | |
170 | __func__, addr, size); | |
171 | ||
172 | memset(&firmware, 0, sizeof(firmware)); | |
173 | memset(&msg, 0, sizeof(msg)); | |
174 | ||
175 | /* Setup the message */ | |
176 | k3_sysctrler_load_msg_setup(&firmware, priv, addr, size); | |
177 | msg.len = sizeof(firmware); | |
178 | msg.buf = (u32 *)&firmware; | |
179 | ||
180 | /* Send the message */ | |
181 | ret = mbox_send(&priv->chan_tx, &msg); | |
182 | if (ret) { | |
183 | dev_err(dev, "%s: Firmware Loading failed. ret = %d\n", | |
184 | __func__, ret); | |
185 | return ret; | |
186 | } | |
187 | ||
188 | /* Receive the response */ | |
189 | ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); | |
190 | if (ret) { | |
191 | dev_err(dev, "%s: Firmware Load response failed. ret = %d\n", | |
192 | __func__, ret); | |
193 | return ret; | |
194 | } | |
195 | ||
196 | /* Process the response */ | |
d7bd29c9 | 197 | ret = k3_sysctrler_load_response(dev, msg.buf); |
1ad190bf LV |
198 | if (ret) |
199 | return ret; | |
200 | ||
201 | debug("%s: Firmware Loaded successfully on dev %s\n", | |
202 | __func__, dev->name); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | /** | |
208 | * k3_sysctrler_start() - Start the remote processor | |
209 | * Note that while technically the K3 system controller starts up | |
210 | * automatically after its firmware got loaded we still want to | |
211 | * utilize the rproc start operation for other startup-related | |
212 | * tasks. | |
213 | * @dev: device to operate upon | |
214 | * | |
215 | * Return: 0 if all went ok, else return appropriate error | |
216 | */ | |
217 | static int k3_sysctrler_start(struct udevice *dev) | |
218 | { | |
219 | struct k3_sysctrler_privdata *priv = dev_get_priv(dev); | |
220 | struct k3_sec_proxy_msg msg; | |
221 | int ret; | |
222 | ||
223 | debug("%s(dev=%p)\n", __func__, dev); | |
224 | ||
225 | /* Receive the boot notification. Note that it is sent only once. */ | |
226 | ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); | |
227 | if (ret) { | |
228 | dev_err(dev, "%s: Boot Notification response failed. ret = %d\n", | |
229 | __func__, ret); | |
230 | return ret; | |
231 | } | |
232 | ||
233 | /* Process the response */ | |
d7bd29c9 | 234 | ret = k3_sysctrler_boot_notification_response(dev, msg.buf); |
1ad190bf LV |
235 | if (ret) |
236 | return ret; | |
237 | ||
238 | debug("%s: Boot notification received successfully on dev %s\n", | |
239 | __func__, dev->name); | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
244 | static const struct dm_rproc_ops k3_sysctrler_ops = { | |
245 | .load = k3_sysctrler_load, | |
246 | .start = k3_sysctrler_start, | |
247 | }; | |
248 | ||
249 | /** | |
250 | * k3_of_to_priv() - generate private data from device tree | |
251 | * @dev: corresponding k3 remote processor device | |
252 | * @priv: pointer to driver specific private data | |
253 | * | |
254 | * Return: 0 if all goes good, else appropriate error message. | |
255 | */ | |
256 | static int k3_of_to_priv(struct udevice *dev, | |
257 | struct k3_sysctrler_privdata *priv) | |
258 | { | |
259 | int ret; | |
260 | ||
261 | ret = mbox_get_by_name(dev, "tx", &priv->chan_tx); | |
262 | if (ret) { | |
263 | dev_err(dev, "%s: Acquiring Tx channel failed. ret = %d\n", | |
264 | __func__, ret); | |
265 | return ret; | |
266 | } | |
267 | ||
268 | ret = mbox_get_by_name(dev, "rx", &priv->chan_rx); | |
269 | if (ret) { | |
270 | dev_err(dev, "%s: Acquiring Rx channel failed. ret = %d\n", | |
271 | __func__, ret); | |
272 | return ret; | |
273 | } | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | /** | |
279 | * k3_sysctrler_probe() - Basic probe | |
280 | * @dev: corresponding k3 remote processor device | |
281 | * | |
282 | * Return: 0 if all goes good, else appropriate error message. | |
283 | */ | |
284 | static int k3_sysctrler_probe(struct udevice *dev) | |
285 | { | |
286 | struct k3_sysctrler_privdata *priv; | |
287 | int ret; | |
288 | ||
289 | debug("%s(dev=%p)\n", __func__, dev); | |
290 | ||
291 | priv = dev_get_priv(dev); | |
292 | ||
293 | ret = k3_of_to_priv(dev, priv); | |
294 | if (ret) { | |
295 | dev_err(dev, "%s: Probe failed with error %d\n", __func__, ret); | |
296 | return ret; | |
297 | } | |
298 | ||
299 | priv->desc = (void *)dev_get_driver_data(dev); | |
300 | priv->seq_nr = 0; | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = { | |
306 | .host_id = 4, /* HOST_ID_R5_1 */ | |
0d7b6cff | 307 | .max_rx_timeout_us = 800000, |
1ad190bf LV |
308 | .max_msg_size = 60, |
309 | }; | |
310 | ||
311 | static const struct udevice_id k3_sysctrler_ids[] = { | |
312 | { | |
313 | .compatible = "ti,am654-system-controller", | |
314 | .data = (ulong)&k3_sysctrler_am654_desc, | |
315 | }, | |
316 | {} | |
317 | }; | |
318 | ||
319 | U_BOOT_DRIVER(k3_sysctrler) = { | |
320 | .name = "k3_system_controller", | |
321 | .of_match = k3_sysctrler_ids, | |
322 | .id = UCLASS_REMOTEPROC, | |
323 | .ops = &k3_sysctrler_ops, | |
324 | .probe = k3_sysctrler_probe, | |
41575d8e | 325 | .priv_auto = sizeof(struct k3_sysctrler_privdata), |
1ad190bf | 326 | }; |