1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018-2019, Intel Corporation |
4 | */ |
5 | |
6 | #include <linux/arm-smccc.h> |
7 | #include <linux/bitfield.h> |
8 | #include <linux/completion.h> |
9 | #include <linux/kobject.h> |
10 | #include <linux/module.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/firmware/intel/stratix10-svc-client.h> |
15 | #include <linux/string.h> |
16 | #include <linux/sysfs.h> |
17 | |
18 | #define RSU_STATE_MASK GENMASK_ULL(31, 0) |
19 | #define RSU_VERSION_MASK GENMASK_ULL(63, 32) |
20 | #define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) |
21 | #define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) |
22 | #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) |
23 | #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) |
24 | #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) |
25 | #define RSU_DCMF3_MASK GENMASK_ULL(63, 32) |
26 | #define RSU_DCMF0_STATUS_MASK GENMASK_ULL(15, 0) |
27 | #define RSU_DCMF1_STATUS_MASK GENMASK_ULL(31, 16) |
28 | #define RSU_DCMF2_STATUS_MASK GENMASK_ULL(47, 32) |
29 | #define RSU_DCMF3_STATUS_MASK GENMASK_ULL(63, 48) |
30 | |
31 | #define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS)) |
32 | |
33 | #define INVALID_RETRY_COUNTER 0xFF |
34 | #define INVALID_DCMF_VERSION 0xFF |
35 | #define INVALID_DCMF_STATUS 0xFFFFFFFF |
36 | #define INVALID_SPT_ADDRESS 0x0 |
37 | |
38 | #define RSU_GET_SPT_CMD 0x5A |
39 | #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) |
40 | |
41 | typedef void (*rsu_callback)(struct stratix10_svc_client *client, |
42 | struct stratix10_svc_cb_data *data); |
43 | /** |
44 | * struct stratix10_rsu_priv - rsu data structure |
45 | * @chan: pointer to the allocated service channel |
46 | * @client: active service client |
47 | * @completion: state for callback completion |
48 | * @lock: a mutex to protect callback completion state |
49 | * @status.current_image: address of image currently running in flash |
50 | * @status.fail_image: address of failed image in flash |
51 | * @status.version: the interface version number of RSU firmware |
52 | * @status.state: the state of RSU system |
53 | * @status.error_details: error code |
54 | * @status.error_location: the error offset inside the image that failed |
55 | * @dcmf_version.dcmf0: Quartus dcmf0 version |
56 | * @dcmf_version.dcmf1: Quartus dcmf1 version |
57 | * @dcmf_version.dcmf2: Quartus dcmf2 version |
58 | * @dcmf_version.dcmf3: Quartus dcmf3 version |
59 | * @dcmf_status.dcmf0: dcmf0 status |
60 | * @dcmf_status.dcmf1: dcmf1 status |
61 | * @dcmf_status.dcmf2: dcmf2 status |
62 | * @dcmf_status.dcmf3: dcmf3 status |
63 | * @retry_counter: the current image's retry counter |
64 | * @max_retry: the preset max retry value |
65 | * @spt0_address: address of spt0 |
66 | * @spt1_address: address of spt1 |
67 | * @get_spt_response_buf: response from sdm for get_spt command |
68 | */ |
69 | struct stratix10_rsu_priv { |
70 | struct stratix10_svc_chan *chan; |
71 | struct stratix10_svc_client client; |
72 | struct completion completion; |
73 | struct mutex lock; |
74 | struct { |
75 | unsigned long current_image; |
76 | unsigned long fail_image; |
77 | unsigned int version; |
78 | unsigned int state; |
79 | unsigned int error_details; |
80 | unsigned int error_location; |
81 | } status; |
82 | |
83 | struct { |
84 | unsigned int dcmf0; |
85 | unsigned int dcmf1; |
86 | unsigned int dcmf2; |
87 | unsigned int dcmf3; |
88 | } dcmf_version; |
89 | |
90 | struct { |
91 | unsigned int dcmf0; |
92 | unsigned int dcmf1; |
93 | unsigned int dcmf2; |
94 | unsigned int dcmf3; |
95 | } dcmf_status; |
96 | |
97 | unsigned int retry_counter; |
98 | unsigned int max_retry; |
99 | |
100 | unsigned long spt0_address; |
101 | unsigned long spt1_address; |
102 | |
103 | unsigned int *get_spt_response_buf; |
104 | }; |
105 | |
106 | /** |
107 | * rsu_status_callback() - Status callback from Intel Service Layer |
108 | * @client: pointer to service client |
109 | * @data: pointer to callback data structure |
110 | * |
111 | * Callback from Intel service layer for RSU status request. Status is |
112 | * only updated after a system reboot, so a get updated status call is |
113 | * made during driver probe. |
114 | */ |
115 | static void rsu_status_callback(struct stratix10_svc_client *client, |
116 | struct stratix10_svc_cb_data *data) |
117 | { |
118 | struct stratix10_rsu_priv *priv = client->priv; |
119 | struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1; |
120 | |
121 | if (data->status == BIT(SVC_STATUS_OK)) { |
122 | priv->status.version = FIELD_GET(RSU_VERSION_MASK, |
123 | res->a2); |
124 | priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2); |
125 | priv->status.fail_image = res->a1; |
126 | priv->status.current_image = res->a0; |
127 | priv->status.error_location = |
128 | FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3); |
129 | priv->status.error_details = |
130 | FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3); |
131 | } else { |
132 | dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n" , |
133 | res->a0); |
134 | priv->status.version = 0; |
135 | priv->status.state = 0; |
136 | priv->status.fail_image = 0; |
137 | priv->status.current_image = 0; |
138 | priv->status.error_location = 0; |
139 | priv->status.error_details = 0; |
140 | } |
141 | |
142 | complete(&priv->completion); |
143 | } |
144 | |
145 | /** |
146 | * rsu_command_callback() - Update callback from Intel Service Layer |
147 | * @client: pointer to client |
148 | * @data: pointer to callback data structure |
149 | * |
150 | * Callback from Intel service layer for RSU commands. |
151 | */ |
152 | static void rsu_command_callback(struct stratix10_svc_client *client, |
153 | struct stratix10_svc_cb_data *data) |
154 | { |
155 | struct stratix10_rsu_priv *priv = client->priv; |
156 | |
157 | if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) |
158 | dev_warn(client->dev, "Secure FW doesn't support notify\n" ); |
159 | else if (data->status == BIT(SVC_STATUS_ERROR)) |
160 | dev_err(client->dev, "Failure, returned status is %lu\n" , |
161 | BIT(data->status)); |
162 | |
163 | complete(&priv->completion); |
164 | } |
165 | |
166 | /** |
167 | * rsu_retry_callback() - Callback from Intel service layer for getting |
168 | * the current image's retry counter from the firmware |
169 | * @client: pointer to client |
170 | * @data: pointer to callback data structure |
171 | * |
172 | * Callback from Intel service layer for retry counter, which is used by |
173 | * user to know how many times the images is still allowed to reload |
174 | * itself before giving up and starting RSU fail-over flow. |
175 | */ |
176 | static void rsu_retry_callback(struct stratix10_svc_client *client, |
177 | struct stratix10_svc_cb_data *data) |
178 | { |
179 | struct stratix10_rsu_priv *priv = client->priv; |
180 | unsigned int *counter = (unsigned int *)data->kaddr1; |
181 | |
182 | if (data->status == BIT(SVC_STATUS_OK)) |
183 | priv->retry_counter = *counter; |
184 | else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) |
185 | dev_warn(client->dev, "Secure FW doesn't support retry\n" ); |
186 | else |
187 | dev_err(client->dev, "Failed to get retry counter %lu\n" , |
188 | BIT(data->status)); |
189 | |
190 | complete(&priv->completion); |
191 | } |
192 | |
193 | /** |
194 | * rsu_max_retry_callback() - Callback from Intel service layer for getting |
195 | * the max retry value from the firmware |
196 | * @client: pointer to client |
197 | * @data: pointer to callback data structure |
198 | * |
199 | * Callback from Intel service layer for max retry. |
200 | */ |
201 | static void rsu_max_retry_callback(struct stratix10_svc_client *client, |
202 | struct stratix10_svc_cb_data *data) |
203 | { |
204 | struct stratix10_rsu_priv *priv = client->priv; |
205 | unsigned int *max_retry = (unsigned int *)data->kaddr1; |
206 | |
207 | if (data->status == BIT(SVC_STATUS_OK)) |
208 | priv->max_retry = *max_retry; |
209 | else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) |
210 | dev_warn(client->dev, "Secure FW doesn't support max retry\n" ); |
211 | else |
212 | dev_err(client->dev, "Failed to get max retry %lu\n" , |
213 | BIT(data->status)); |
214 | |
215 | complete(&priv->completion); |
216 | } |
217 | |
218 | /** |
219 | * rsu_dcmf_version_callback() - Callback from Intel service layer for getting |
220 | * the DCMF version |
221 | * @client: pointer to client |
222 | * @data: pointer to callback data structure |
223 | * |
224 | * Callback from Intel service layer for DCMF version number |
225 | */ |
226 | static void rsu_dcmf_version_callback(struct stratix10_svc_client *client, |
227 | struct stratix10_svc_cb_data *data) |
228 | { |
229 | struct stratix10_rsu_priv *priv = client->priv; |
230 | unsigned long long *value1 = (unsigned long long *)data->kaddr1; |
231 | unsigned long long *value2 = (unsigned long long *)data->kaddr2; |
232 | |
233 | if (data->status == BIT(SVC_STATUS_OK)) { |
234 | priv->dcmf_version.dcmf0 = FIELD_GET(RSU_DCMF0_MASK, *value1); |
235 | priv->dcmf_version.dcmf1 = FIELD_GET(RSU_DCMF1_MASK, *value1); |
236 | priv->dcmf_version.dcmf2 = FIELD_GET(RSU_DCMF2_MASK, *value2); |
237 | priv->dcmf_version.dcmf3 = FIELD_GET(RSU_DCMF3_MASK, *value2); |
238 | } else |
239 | dev_err(client->dev, "failed to get DCMF version\n" ); |
240 | |
241 | complete(&priv->completion); |
242 | } |
243 | |
244 | /** |
245 | * rsu_dcmf_status_callback() - Callback from Intel service layer for getting |
246 | * the DCMF status |
247 | * @client: pointer to client |
248 | * @data: pointer to callback data structure |
249 | * |
250 | * Callback from Intel service layer for DCMF status |
251 | */ |
252 | static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, |
253 | struct stratix10_svc_cb_data *data) |
254 | { |
255 | struct stratix10_rsu_priv *priv = client->priv; |
256 | unsigned long long *value = (unsigned long long *)data->kaddr1; |
257 | |
258 | if (data->status == BIT(SVC_STATUS_OK)) { |
259 | priv->dcmf_status.dcmf0 = FIELD_GET(RSU_DCMF0_STATUS_MASK, |
260 | *value); |
261 | priv->dcmf_status.dcmf1 = FIELD_GET(RSU_DCMF1_STATUS_MASK, |
262 | *value); |
263 | priv->dcmf_status.dcmf2 = FIELD_GET(RSU_DCMF2_STATUS_MASK, |
264 | *value); |
265 | priv->dcmf_status.dcmf3 = FIELD_GET(RSU_DCMF3_STATUS_MASK, |
266 | *value); |
267 | } else |
268 | dev_err(client->dev, "failed to get DCMF status\n" ); |
269 | |
270 | complete(&priv->completion); |
271 | } |
272 | |
273 | static void rsu_get_spt_callback(struct stratix10_svc_client *client, |
274 | struct stratix10_svc_cb_data *data) |
275 | { |
276 | struct stratix10_rsu_priv *priv = client->priv; |
277 | unsigned long *mbox_err = (unsigned long *)data->kaddr1; |
278 | unsigned long *resp_len = (unsigned long *)data->kaddr2; |
279 | |
280 | if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) || |
281 | (*resp_len != RSU_GET_SPT_RESP_LEN)) |
282 | goto error; |
283 | |
284 | priv->spt0_address = priv->get_spt_response_buf[0]; |
285 | priv->spt0_address <<= 32; |
286 | priv->spt0_address |= priv->get_spt_response_buf[1]; |
287 | |
288 | priv->spt1_address = priv->get_spt_response_buf[2]; |
289 | priv->spt1_address <<= 32; |
290 | priv->spt1_address |= priv->get_spt_response_buf[3]; |
291 | |
292 | goto complete; |
293 | |
294 | error: |
295 | dev_err(client->dev, "failed to get SPTs\n" ); |
296 | |
297 | complete: |
298 | stratix10_svc_free_memory(chan: priv->chan, kaddr: priv->get_spt_response_buf); |
299 | priv->get_spt_response_buf = NULL; |
300 | complete(&priv->completion); |
301 | } |
302 | |
303 | /** |
304 | * rsu_send_msg() - send a message to Intel service layer |
305 | * @priv: pointer to rsu private data |
306 | * @command: RSU status or update command |
307 | * @arg: the request argument, the bitstream address or notify status |
308 | * @callback: function pointer for the callback (status or update) |
309 | * |
310 | * Start an Intel service layer transaction to perform the SMC call that |
311 | * is necessary to get RSU boot log or set the address of bitstream to |
312 | * boot after reboot. |
313 | * |
314 | * Returns 0 on success or -ETIMEDOUT on error. |
315 | */ |
316 | static int rsu_send_msg(struct stratix10_rsu_priv *priv, |
317 | enum stratix10_svc_command_code command, |
318 | unsigned long arg, |
319 | rsu_callback callback) |
320 | { |
321 | struct stratix10_svc_client_msg msg; |
322 | int ret; |
323 | |
324 | mutex_lock(&priv->lock); |
325 | reinit_completion(x: &priv->completion); |
326 | priv->client.receive_cb = callback; |
327 | |
328 | msg.command = command; |
329 | if (arg) |
330 | msg.arg[0] = arg; |
331 | |
332 | if (command == COMMAND_MBOX_SEND_CMD) { |
333 | msg.arg[1] = 0; |
334 | msg.payload = NULL; |
335 | msg.payload_length = 0; |
336 | msg.payload_output = priv->get_spt_response_buf; |
337 | msg.payload_length_output = RSU_GET_SPT_RESP_LEN; |
338 | } |
339 | |
340 | ret = stratix10_svc_send(chan: priv->chan, msg: &msg); |
341 | if (ret < 0) |
342 | goto status_done; |
343 | |
344 | ret = wait_for_completion_interruptible_timeout(x: &priv->completion, |
345 | RSU_TIMEOUT); |
346 | if (!ret) { |
347 | dev_err(priv->client.dev, |
348 | "timeout waiting for SMC call\n" ); |
349 | ret = -ETIMEDOUT; |
350 | goto status_done; |
351 | } else if (ret < 0) { |
352 | dev_err(priv->client.dev, |
353 | "error %d waiting for SMC call\n" , ret); |
354 | goto status_done; |
355 | } else { |
356 | ret = 0; |
357 | } |
358 | |
359 | status_done: |
360 | stratix10_svc_done(chan: priv->chan); |
361 | mutex_unlock(lock: &priv->lock); |
362 | return ret; |
363 | } |
364 | |
365 | /* |
366 | * This driver exposes some optional features of the Intel Stratix 10 SoC FPGA. |
367 | * The sysfs interfaces exposed here are FPGA Remote System Update (RSU) |
368 | * related. They allow user space software to query the configuration system |
369 | * status and to request optional reboot behavior specific to Intel FPGAs. |
370 | */ |
371 | |
372 | static ssize_t current_image_show(struct device *dev, |
373 | struct device_attribute *attr, char *buf) |
374 | { |
375 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
376 | |
377 | if (!priv) |
378 | return -ENODEV; |
379 | |
380 | return sprintf(buf, fmt: "0x%08lx\n" , priv->status.current_image); |
381 | } |
382 | |
383 | static ssize_t fail_image_show(struct device *dev, |
384 | struct device_attribute *attr, char *buf) |
385 | { |
386 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
387 | |
388 | if (!priv) |
389 | return -ENODEV; |
390 | |
391 | return sprintf(buf, fmt: "0x%08lx\n" , priv->status.fail_image); |
392 | } |
393 | |
394 | static ssize_t version_show(struct device *dev, struct device_attribute *attr, |
395 | char *buf) |
396 | { |
397 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
398 | |
399 | if (!priv) |
400 | return -ENODEV; |
401 | |
402 | return sprintf(buf, fmt: "0x%08x\n" , priv->status.version); |
403 | } |
404 | |
405 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, |
406 | char *buf) |
407 | { |
408 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
409 | |
410 | if (!priv) |
411 | return -ENODEV; |
412 | |
413 | return sprintf(buf, fmt: "0x%08x\n" , priv->status.state); |
414 | } |
415 | |
416 | static ssize_t error_location_show(struct device *dev, |
417 | struct device_attribute *attr, char *buf) |
418 | { |
419 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
420 | |
421 | if (!priv) |
422 | return -ENODEV; |
423 | |
424 | return sprintf(buf, fmt: "0x%08x\n" , priv->status.error_location); |
425 | } |
426 | |
427 | static ssize_t error_details_show(struct device *dev, |
428 | struct device_attribute *attr, char *buf) |
429 | { |
430 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
431 | |
432 | if (!priv) |
433 | return -ENODEV; |
434 | |
435 | return sprintf(buf, fmt: "0x%08x\n" , priv->status.error_details); |
436 | } |
437 | |
438 | static ssize_t retry_counter_show(struct device *dev, |
439 | struct device_attribute *attr, char *buf) |
440 | { |
441 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
442 | |
443 | if (!priv) |
444 | return -ENODEV; |
445 | |
446 | return sprintf(buf, fmt: "0x%08x\n" , priv->retry_counter); |
447 | } |
448 | |
449 | static ssize_t max_retry_show(struct device *dev, |
450 | struct device_attribute *attr, char *buf) |
451 | { |
452 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
453 | |
454 | if (!priv) |
455 | return -ENODEV; |
456 | |
457 | return scnprintf(buf, size: sizeof(priv->max_retry), |
458 | fmt: "0x%08x\n" , priv->max_retry); |
459 | } |
460 | |
461 | static ssize_t dcmf0_show(struct device *dev, |
462 | struct device_attribute *attr, char *buf) |
463 | { |
464 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
465 | |
466 | if (!priv) |
467 | return -ENODEV; |
468 | |
469 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_version.dcmf0); |
470 | } |
471 | |
472 | static ssize_t dcmf1_show(struct device *dev, |
473 | struct device_attribute *attr, char *buf) |
474 | { |
475 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
476 | |
477 | if (!priv) |
478 | return -ENODEV; |
479 | |
480 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_version.dcmf1); |
481 | } |
482 | |
483 | static ssize_t dcmf2_show(struct device *dev, |
484 | struct device_attribute *attr, char *buf) |
485 | { |
486 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
487 | |
488 | if (!priv) |
489 | return -ENODEV; |
490 | |
491 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_version.dcmf2); |
492 | } |
493 | |
494 | static ssize_t dcmf3_show(struct device *dev, |
495 | struct device_attribute *attr, char *buf) |
496 | { |
497 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
498 | |
499 | if (!priv) |
500 | return -ENODEV; |
501 | |
502 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_version.dcmf3); |
503 | } |
504 | |
505 | static ssize_t dcmf0_status_show(struct device *dev, |
506 | struct device_attribute *attr, char *buf) |
507 | { |
508 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
509 | |
510 | if (!priv) |
511 | return -ENODEV; |
512 | |
513 | if (priv->dcmf_status.dcmf0 == INVALID_DCMF_STATUS) |
514 | return -EIO; |
515 | |
516 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_status.dcmf0); |
517 | } |
518 | |
519 | static ssize_t dcmf1_status_show(struct device *dev, |
520 | struct device_attribute *attr, char *buf) |
521 | { |
522 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
523 | |
524 | if (!priv) |
525 | return -ENODEV; |
526 | |
527 | if (priv->dcmf_status.dcmf1 == INVALID_DCMF_STATUS) |
528 | return -EIO; |
529 | |
530 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_status.dcmf1); |
531 | } |
532 | |
533 | static ssize_t dcmf2_status_show(struct device *dev, |
534 | struct device_attribute *attr, char *buf) |
535 | { |
536 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
537 | |
538 | if (!priv) |
539 | return -ENODEV; |
540 | |
541 | if (priv->dcmf_status.dcmf2 == INVALID_DCMF_STATUS) |
542 | return -EIO; |
543 | |
544 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_status.dcmf2); |
545 | } |
546 | |
547 | static ssize_t dcmf3_status_show(struct device *dev, |
548 | struct device_attribute *attr, char *buf) |
549 | { |
550 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
551 | |
552 | if (!priv) |
553 | return -ENODEV; |
554 | |
555 | if (priv->dcmf_status.dcmf3 == INVALID_DCMF_STATUS) |
556 | return -EIO; |
557 | |
558 | return sprintf(buf, fmt: "0x%08x\n" , priv->dcmf_status.dcmf3); |
559 | } |
560 | static ssize_t reboot_image_store(struct device *dev, |
561 | struct device_attribute *attr, |
562 | const char *buf, size_t count) |
563 | { |
564 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
565 | unsigned long address; |
566 | int ret; |
567 | |
568 | if (!priv) |
569 | return -ENODEV; |
570 | |
571 | ret = kstrtoul(s: buf, base: 0, res: &address); |
572 | if (ret) |
573 | return ret; |
574 | |
575 | ret = rsu_send_msg(priv, command: COMMAND_RSU_UPDATE, |
576 | arg: address, callback: rsu_command_callback); |
577 | if (ret) { |
578 | dev_err(dev, "Error, RSU update returned %i\n" , ret); |
579 | return ret; |
580 | } |
581 | |
582 | return count; |
583 | } |
584 | |
585 | static ssize_t notify_store(struct device *dev, |
586 | struct device_attribute *attr, |
587 | const char *buf, size_t count) |
588 | { |
589 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
590 | unsigned long status; |
591 | int ret; |
592 | |
593 | if (!priv) |
594 | return -ENODEV; |
595 | |
596 | ret = kstrtoul(s: buf, base: 0, res: &status); |
597 | if (ret) |
598 | return ret; |
599 | |
600 | ret = rsu_send_msg(priv, command: COMMAND_RSU_NOTIFY, |
601 | arg: status, callback: rsu_command_callback); |
602 | if (ret) { |
603 | dev_err(dev, "Error, RSU notify returned %i\n" , ret); |
604 | return ret; |
605 | } |
606 | |
607 | /* to get the updated state */ |
608 | ret = rsu_send_msg(priv, command: COMMAND_RSU_STATUS, |
609 | arg: 0, callback: rsu_status_callback); |
610 | if (ret) { |
611 | dev_err(dev, "Error, getting RSU status %i\n" , ret); |
612 | return ret; |
613 | } |
614 | |
615 | ret = rsu_send_msg(priv, command: COMMAND_RSU_RETRY, arg: 0, callback: rsu_retry_callback); |
616 | if (ret) { |
617 | dev_err(dev, "Error, getting RSU retry %i\n" , ret); |
618 | return ret; |
619 | } |
620 | |
621 | return count; |
622 | } |
623 | |
624 | static ssize_t spt0_address_show(struct device *dev, |
625 | struct device_attribute *attr, char *buf) |
626 | { |
627 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
628 | |
629 | if (!priv) |
630 | return -ENODEV; |
631 | |
632 | if (priv->spt0_address == INVALID_SPT_ADDRESS) |
633 | return -EIO; |
634 | |
635 | return scnprintf(buf, PAGE_SIZE, fmt: "0x%08lx\n" , priv->spt0_address); |
636 | } |
637 | |
638 | static ssize_t spt1_address_show(struct device *dev, |
639 | struct device_attribute *attr, char *buf) |
640 | { |
641 | struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); |
642 | |
643 | if (!priv) |
644 | return -ENODEV; |
645 | |
646 | if (priv->spt1_address == INVALID_SPT_ADDRESS) |
647 | return -EIO; |
648 | |
649 | return scnprintf(buf, PAGE_SIZE, fmt: "0x%08lx\n" , priv->spt1_address); |
650 | } |
651 | |
652 | static DEVICE_ATTR_RO(current_image); |
653 | static DEVICE_ATTR_RO(fail_image); |
654 | static DEVICE_ATTR_RO(state); |
655 | static DEVICE_ATTR_RO(version); |
656 | static DEVICE_ATTR_RO(error_location); |
657 | static DEVICE_ATTR_RO(error_details); |
658 | static DEVICE_ATTR_RO(retry_counter); |
659 | static DEVICE_ATTR_RO(max_retry); |
660 | static DEVICE_ATTR_RO(dcmf0); |
661 | static DEVICE_ATTR_RO(dcmf1); |
662 | static DEVICE_ATTR_RO(dcmf2); |
663 | static DEVICE_ATTR_RO(dcmf3); |
664 | static DEVICE_ATTR_RO(dcmf0_status); |
665 | static DEVICE_ATTR_RO(dcmf1_status); |
666 | static DEVICE_ATTR_RO(dcmf2_status); |
667 | static DEVICE_ATTR_RO(dcmf3_status); |
668 | static DEVICE_ATTR_WO(reboot_image); |
669 | static DEVICE_ATTR_WO(notify); |
670 | static DEVICE_ATTR_RO(spt0_address); |
671 | static DEVICE_ATTR_RO(spt1_address); |
672 | |
673 | static struct attribute *rsu_attrs[] = { |
674 | &dev_attr_current_image.attr, |
675 | &dev_attr_fail_image.attr, |
676 | &dev_attr_state.attr, |
677 | &dev_attr_version.attr, |
678 | &dev_attr_error_location.attr, |
679 | &dev_attr_error_details.attr, |
680 | &dev_attr_retry_counter.attr, |
681 | &dev_attr_max_retry.attr, |
682 | &dev_attr_dcmf0.attr, |
683 | &dev_attr_dcmf1.attr, |
684 | &dev_attr_dcmf2.attr, |
685 | &dev_attr_dcmf3.attr, |
686 | &dev_attr_dcmf0_status.attr, |
687 | &dev_attr_dcmf1_status.attr, |
688 | &dev_attr_dcmf2_status.attr, |
689 | &dev_attr_dcmf3_status.attr, |
690 | &dev_attr_reboot_image.attr, |
691 | &dev_attr_notify.attr, |
692 | &dev_attr_spt0_address.attr, |
693 | &dev_attr_spt1_address.attr, |
694 | NULL |
695 | }; |
696 | |
697 | ATTRIBUTE_GROUPS(rsu); |
698 | |
699 | static int stratix10_rsu_probe(struct platform_device *pdev) |
700 | { |
701 | struct device *dev = &pdev->dev; |
702 | struct stratix10_rsu_priv *priv; |
703 | int ret; |
704 | |
705 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
706 | if (!priv) |
707 | return -ENOMEM; |
708 | |
709 | priv->client.dev = dev; |
710 | priv->client.receive_cb = NULL; |
711 | priv->client.priv = priv; |
712 | priv->status.current_image = 0; |
713 | priv->status.fail_image = 0; |
714 | priv->status.error_location = 0; |
715 | priv->status.error_details = 0; |
716 | priv->status.version = 0; |
717 | priv->status.state = 0; |
718 | priv->retry_counter = INVALID_RETRY_COUNTER; |
719 | priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION; |
720 | priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; |
721 | priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; |
722 | priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION; |
723 | priv->dcmf_status.dcmf0 = INVALID_DCMF_STATUS; |
724 | priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; |
725 | priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; |
726 | priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; |
727 | priv->max_retry = INVALID_RETRY_COUNTER; |
728 | priv->spt0_address = INVALID_SPT_ADDRESS; |
729 | priv->spt1_address = INVALID_SPT_ADDRESS; |
730 | |
731 | mutex_init(&priv->lock); |
732 | priv->chan = stratix10_svc_request_channel_byname(client: &priv->client, |
733 | SVC_CLIENT_RSU); |
734 | if (IS_ERR(ptr: priv->chan)) { |
735 | dev_err(dev, "couldn't get service channel %s\n" , |
736 | SVC_CLIENT_RSU); |
737 | return PTR_ERR(ptr: priv->chan); |
738 | } |
739 | |
740 | init_completion(x: &priv->completion); |
741 | platform_set_drvdata(pdev, data: priv); |
742 | |
743 | /* get the initial state from firmware */ |
744 | ret = rsu_send_msg(priv, command: COMMAND_RSU_STATUS, |
745 | arg: 0, callback: rsu_status_callback); |
746 | if (ret) { |
747 | dev_err(dev, "Error, getting RSU status %i\n" , ret); |
748 | stratix10_svc_free_channel(chan: priv->chan); |
749 | } |
750 | |
751 | /* get DCMF version from firmware */ |
752 | ret = rsu_send_msg(priv, command: COMMAND_RSU_DCMF_VERSION, |
753 | arg: 0, callback: rsu_dcmf_version_callback); |
754 | if (ret) { |
755 | dev_err(dev, "Error, getting DCMF version %i\n" , ret); |
756 | stratix10_svc_free_channel(chan: priv->chan); |
757 | } |
758 | |
759 | ret = rsu_send_msg(priv, command: COMMAND_RSU_DCMF_STATUS, |
760 | arg: 0, callback: rsu_dcmf_status_callback); |
761 | if (ret) { |
762 | dev_err(dev, "Error, getting DCMF status %i\n" , ret); |
763 | stratix10_svc_free_channel(chan: priv->chan); |
764 | } |
765 | |
766 | ret = rsu_send_msg(priv, command: COMMAND_RSU_RETRY, arg: 0, callback: rsu_retry_callback); |
767 | if (ret) { |
768 | dev_err(dev, "Error, getting RSU retry %i\n" , ret); |
769 | stratix10_svc_free_channel(chan: priv->chan); |
770 | } |
771 | |
772 | ret = rsu_send_msg(priv, command: COMMAND_RSU_MAX_RETRY, arg: 0, |
773 | callback: rsu_max_retry_callback); |
774 | if (ret) { |
775 | dev_err(dev, "Error, getting RSU max retry %i\n" , ret); |
776 | stratix10_svc_free_channel(chan: priv->chan); |
777 | } |
778 | |
779 | priv->get_spt_response_buf = |
780 | stratix10_svc_allocate_memory(chan: priv->chan, RSU_GET_SPT_RESP_LEN); |
781 | |
782 | if (IS_ERR(ptr: priv->get_spt_response_buf)) { |
783 | dev_err(dev, "failed to allocate get spt buffer\n" ); |
784 | } else { |
785 | ret = rsu_send_msg(priv, command: COMMAND_MBOX_SEND_CMD, |
786 | RSU_GET_SPT_CMD, callback: rsu_get_spt_callback); |
787 | if (ret) { |
788 | dev_err(dev, "Error, getting SPT table %i\n" , ret); |
789 | stratix10_svc_free_channel(chan: priv->chan); |
790 | } |
791 | } |
792 | |
793 | return ret; |
794 | } |
795 | |
796 | static void stratix10_rsu_remove(struct platform_device *pdev) |
797 | { |
798 | struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); |
799 | |
800 | stratix10_svc_free_channel(chan: priv->chan); |
801 | } |
802 | |
803 | static struct platform_driver stratix10_rsu_driver = { |
804 | .probe = stratix10_rsu_probe, |
805 | .remove_new = stratix10_rsu_remove, |
806 | .driver = { |
807 | .name = "stratix10-rsu" , |
808 | .dev_groups = rsu_groups, |
809 | }, |
810 | }; |
811 | |
812 | module_platform_driver(stratix10_rsu_driver); |
813 | |
814 | MODULE_LICENSE("GPL v2" ); |
815 | MODULE_DESCRIPTION("Intel Remote System Update Driver" ); |
816 | MODULE_AUTHOR("Richard Gong <richard.gong@intel.com>" ); |
817 | |