]>
Commit | Line | Data |
---|---|---|
1ad190bf LV |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Texas Instruments' K3 System Controller Driver | |
4 | * | |
a94a4071 | 5 | * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/ |
1ad190bf LV |
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 | |
473bd5ae | 80 | * @chan_boot_notify: Boot notification channel |
1ad190bf LV |
81 | * @desc: SoC description for this instance |
82 | * @seq_nr: Counter for number of messages sent. | |
473bd5ae | 83 | * @has_boot_notify: Has separate boot notification channel |
1ad190bf LV |
84 | */ |
85 | struct k3_sysctrler_privdata { | |
86 | struct mbox_chan chan_tx; | |
87 | struct mbox_chan chan_rx; | |
473bd5ae | 88 | struct mbox_chan chan_boot_notify; |
1ad190bf LV |
89 | struct k3_sysctrler_desc *desc; |
90 | u32 seq_nr; | |
473bd5ae | 91 | bool has_boot_notify; |
1ad190bf LV |
92 | }; |
93 | ||
94 | static inline | |
95 | void k3_sysctrler_load_msg_setup(struct k3_sysctrler_load_msg *fw, | |
96 | struct k3_sysctrler_privdata *priv, | |
97 | ulong addr, ulong size) | |
98 | { | |
99 | fw->hdr.cmd_id = K3_MSG_R5_TO_M3_M3FW; | |
100 | fw->hdr.host_id = priv->desc->host_id; | |
101 | fw->hdr.seq_nr = priv->seq_nr++; | |
102 | fw->hdr.flags = 0x0; | |
103 | fw->buffer_address = addr; | |
104 | fw->buffer_size = size; | |
105 | } | |
106 | ||
d7bd29c9 | 107 | static int k3_sysctrler_load_response(struct udevice *dev, u32 *buf) |
1ad190bf LV |
108 | { |
109 | struct k3_sysctrler_load_msg *fw; | |
110 | ||
111 | fw = (struct k3_sysctrler_load_msg *)buf; | |
112 | ||
113 | /* Check for proper response ID */ | |
114 | if (fw->hdr.cmd_id != K3_MSG_M3_TO_R5_CERT_RESULT) { | |
115 | dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", | |
116 | __func__, K3_MSG_M3_TO_R5_CERT_RESULT, fw->hdr.cmd_id); | |
117 | return -EINVAL; | |
118 | } | |
119 | ||
120 | /* Check for certificate authentication result */ | |
121 | if (fw->hdr.flags == K3_FLAGS_MSG_CERT_AUTH_FAIL) { | |
122 | dev_err(dev, "%s: Firmware certificate authentication failed\n", | |
123 | __func__); | |
124 | return -EINVAL; | |
125 | } else if (fw->hdr.flags != K3_FLAGS_MSG_CERT_AUTH_PASS) { | |
126 | dev_err(dev, "%s: Firmware Load response Invalid %d\n", | |
127 | __func__, fw->hdr.flags); | |
128 | return -EINVAL; | |
129 | } | |
130 | ||
131 | debug("%s: Firmware authentication passed\n", __func__); | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
d7bd29c9 SA |
136 | static int k3_sysctrler_boot_notification_response(struct udevice *dev, |
137 | u32 *buf) | |
1ad190bf LV |
138 | { |
139 | struct k3_sysctrler_boot_notification_msg *boot; | |
140 | ||
141 | boot = (struct k3_sysctrler_boot_notification_msg *)buf; | |
142 | ||
143 | /* ToDo: Verify checksum */ | |
144 | ||
145 | /* Check for proper response ID */ | |
146 | if (boot->hdr.cmd_id != K3_MSG_M3_TO_R5_BOOT_NOTIFICATION) { | |
147 | dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", | |
148 | __func__, K3_MSG_M3_TO_R5_BOOT_NOTIFICATION, | |
149 | boot->hdr.cmd_id); | |
150 | return -EINVAL; | |
151 | } | |
152 | ||
153 | debug("%s: Boot notification received\n", __func__); | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | /** | |
159 | * k3_sysctrler_load() - Loadup the K3 remote processor | |
160 | * @dev: corresponding K3 remote processor device | |
161 | * @addr: Address in memory where image binary is stored | |
162 | * @size: Size in bytes of the image binary | |
163 | * | |
164 | * Return: 0 if all goes good, else appropriate error message. | |
165 | */ | |
166 | static int k3_sysctrler_load(struct udevice *dev, ulong addr, ulong size) | |
167 | { | |
168 | struct k3_sysctrler_privdata *priv = dev_get_priv(dev); | |
169 | struct k3_sysctrler_load_msg firmware; | |
170 | struct k3_sec_proxy_msg msg; | |
171 | int ret; | |
172 | ||
173 | debug("%s: Loading binary from 0x%08lX, size 0x%08lX\n", | |
174 | __func__, addr, size); | |
175 | ||
176 | memset(&firmware, 0, sizeof(firmware)); | |
177 | memset(&msg, 0, sizeof(msg)); | |
178 | ||
179 | /* Setup the message */ | |
180 | k3_sysctrler_load_msg_setup(&firmware, priv, addr, size); | |
181 | msg.len = sizeof(firmware); | |
182 | msg.buf = (u32 *)&firmware; | |
183 | ||
184 | /* Send the message */ | |
185 | ret = mbox_send(&priv->chan_tx, &msg); | |
186 | if (ret) { | |
187 | dev_err(dev, "%s: Firmware Loading failed. ret = %d\n", | |
188 | __func__, ret); | |
189 | return ret; | |
190 | } | |
191 | ||
192 | /* Receive the response */ | |
193 | ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); | |
194 | if (ret) { | |
195 | dev_err(dev, "%s: Firmware Load response failed. ret = %d\n", | |
196 | __func__, ret); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | /* Process the response */ | |
d7bd29c9 | 201 | ret = k3_sysctrler_load_response(dev, msg.buf); |
1ad190bf LV |
202 | if (ret) |
203 | return ret; | |
204 | ||
205 | debug("%s: Firmware Loaded successfully on dev %s\n", | |
206 | __func__, dev->name); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | /** | |
212 | * k3_sysctrler_start() - Start the remote processor | |
213 | * Note that while technically the K3 system controller starts up | |
214 | * automatically after its firmware got loaded we still want to | |
215 | * utilize the rproc start operation for other startup-related | |
216 | * tasks. | |
217 | * @dev: device to operate upon | |
218 | * | |
219 | * Return: 0 if all went ok, else return appropriate error | |
220 | */ | |
221 | static int k3_sysctrler_start(struct udevice *dev) | |
222 | { | |
223 | struct k3_sysctrler_privdata *priv = dev_get_priv(dev); | |
224 | struct k3_sec_proxy_msg msg; | |
225 | int ret; | |
226 | ||
227 | debug("%s(dev=%p)\n", __func__, dev); | |
228 | ||
229 | /* Receive the boot notification. Note that it is sent only once. */ | |
473bd5ae NM |
230 | ret = mbox_recv(priv->has_boot_notify ? &priv->chan_boot_notify : |
231 | &priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); | |
1ad190bf LV |
232 | if (ret) { |
233 | dev_err(dev, "%s: Boot Notification response failed. ret = %d\n", | |
234 | __func__, ret); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | /* Process the response */ | |
d7bd29c9 | 239 | ret = k3_sysctrler_boot_notification_response(dev, msg.buf); |
1ad190bf LV |
240 | if (ret) |
241 | return ret; | |
242 | ||
243 | debug("%s: Boot notification received successfully on dev %s\n", | |
244 | __func__, dev->name); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | static const struct dm_rproc_ops k3_sysctrler_ops = { | |
250 | .load = k3_sysctrler_load, | |
251 | .start = k3_sysctrler_start, | |
252 | }; | |
253 | ||
254 | /** | |
255 | * k3_of_to_priv() - generate private data from device tree | |
256 | * @dev: corresponding k3 remote processor device | |
257 | * @priv: pointer to driver specific private data | |
258 | * | |
259 | * Return: 0 if all goes good, else appropriate error message. | |
260 | */ | |
261 | static int k3_of_to_priv(struct udevice *dev, | |
262 | struct k3_sysctrler_privdata *priv) | |
263 | { | |
264 | int ret; | |
265 | ||
266 | ret = mbox_get_by_name(dev, "tx", &priv->chan_tx); | |
267 | if (ret) { | |
268 | dev_err(dev, "%s: Acquiring Tx channel failed. ret = %d\n", | |
269 | __func__, ret); | |
270 | return ret; | |
271 | } | |
272 | ||
273 | ret = mbox_get_by_name(dev, "rx", &priv->chan_rx); | |
274 | if (ret) { | |
275 | dev_err(dev, "%s: Acquiring Rx channel failed. ret = %d\n", | |
276 | __func__, ret); | |
277 | return ret; | |
278 | } | |
279 | ||
473bd5ae NM |
280 | /* Some SoCs may have a optional channel for boot notification. */ |
281 | priv->has_boot_notify = 1; | |
282 | ret = mbox_get_by_name(dev, "boot_notify", &priv->chan_boot_notify); | |
283 | if (ret == -ENODATA) { | |
284 | dev_dbg(dev, "%s: Acquiring optional Boot_notify failed. ret = %d. Using Rx\n", | |
285 | __func__, ret); | |
286 | priv->has_boot_notify = 0; | |
287 | } else if (ret) { | |
288 | dev_err(dev, "%s: Acquiring boot_notify channel failed. ret = %d\n", | |
289 | __func__, ret); | |
290 | return ret; | |
291 | } | |
292 | ||
1ad190bf LV |
293 | return 0; |
294 | } | |
295 | ||
296 | /** | |
297 | * k3_sysctrler_probe() - Basic probe | |
298 | * @dev: corresponding k3 remote processor device | |
299 | * | |
300 | * Return: 0 if all goes good, else appropriate error message. | |
301 | */ | |
302 | static int k3_sysctrler_probe(struct udevice *dev) | |
303 | { | |
304 | struct k3_sysctrler_privdata *priv; | |
305 | int ret; | |
306 | ||
307 | debug("%s(dev=%p)\n", __func__, dev); | |
308 | ||
309 | priv = dev_get_priv(dev); | |
310 | ||
311 | ret = k3_of_to_priv(dev, priv); | |
312 | if (ret) { | |
313 | dev_err(dev, "%s: Probe failed with error %d\n", __func__, ret); | |
314 | return ret; | |
315 | } | |
316 | ||
317 | priv->desc = (void *)dev_get_driver_data(dev); | |
318 | priv->seq_nr = 0; | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = { | |
324 | .host_id = 4, /* HOST_ID_R5_1 */ | |
0d7b6cff | 325 | .max_rx_timeout_us = 800000, |
1ad190bf LV |
326 | .max_msg_size = 60, |
327 | }; | |
328 | ||
329 | static const struct udevice_id k3_sysctrler_ids[] = { | |
330 | { | |
331 | .compatible = "ti,am654-system-controller", | |
332 | .data = (ulong)&k3_sysctrler_am654_desc, | |
333 | }, | |
334 | {} | |
335 | }; | |
336 | ||
337 | U_BOOT_DRIVER(k3_sysctrler) = { | |
338 | .name = "k3_system_controller", | |
339 | .of_match = k3_sysctrler_ids, | |
340 | .id = UCLASS_REMOTEPROC, | |
341 | .ops = &k3_sysctrler_ops, | |
342 | .probe = k3_sysctrler_probe, | |
41575d8e | 343 | .priv_auto = sizeof(struct k3_sysctrler_privdata), |
1ad190bf | 344 | }; |