summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Gong <richard.gong@intel.com>2019-09-03 09:18:19 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-09-04 07:31:28 -0400
commit4526ebbc77732bcae965ee374cf3e8d86436b2ad (patch)
treed71877ce3f08d7df0ae12d62b578a70d8d31b687
parentb5dc75c915cdaebab9b9875022e45638d6b14a7e (diff)
firmware: add Intel Stratix10 remote system update driver
The Intel Remote System Update (RSU) driver exposes interfaces access through the Intel Service Layer to user space via sysfs interface. The RSU interfaces report and control some of the optional RSU features on Intel Stratix 10 SoC. The RSU feature provides a way for customers to update the boot configuration of a Intel Stratix 10 SoC device with significantly reduced risk of corrupting the bitstream storage and bricking the system. Signed-off-by: Richard Gong <richard.gong@intel.com> Reviewed-by: Alan Tull <atull@kernel.org> Link: https://lore.kernel.org/r/1567516701-26026-3-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/firmware/Kconfig18
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/stratix10-rsu.c451
3 files changed, 470 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index ba8d3d0ef32c..c853a17c3f31 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -216,6 +216,24 @@ config INTEL_STRATIX10_SERVICE
216 216
217 Say Y here if you want Stratix10 service layer support. 217 Say Y here if you want Stratix10 service layer support.
218 218
219config INTEL_STRATIX10_RSU
220 tristate "Intel Stratix10 Remote System Update"
221 depends on INTEL_STRATIX10_SERVICE
222 help
223 The Intel Remote System Update (RSU) driver exposes interfaces
224 access through the Intel Service Layer to user space via sysfs
225 device attribute nodes. The RSU interfaces report/control some of
226 the optional RSU features of the Stratix 10 SoC FPGA.
227
228 The RSU provides a way for customers to update the boot
229 configuration of a Stratix 10 SoC device with significantly reduced
230 risk of corrupting the bitstream storage and bricking the system.
231
232 Enable RSU support if you are using an Intel SoC FPGA with the RSU
233 feature enabled and you want Linux user space control.
234
235 Say Y here if you want Intel RSU support.
236
219config QCOM_SCM 237config QCOM_SCM
220 bool 238 bool
221 depends on ARM || ARM64 239 depends on ARM || ARM64
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 3fa0b34eb72f..d04d5fc4f915 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_EDD) += edd.o
11obj-$(CONFIG_EFI_PCDP) += pcdp.o 11obj-$(CONFIG_EFI_PCDP) += pcdp.o
12obj-$(CONFIG_DMIID) += dmi-id.o 12obj-$(CONFIG_DMIID) += dmi-id.o
13obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o 13obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o
14obj-$(CONFIG_INTEL_STRATIX10_RSU) += stratix10-rsu.o
14obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o 15obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
15obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o 16obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
16obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o 17obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c
new file mode 100644
index 000000000000..bb008c019920
--- /dev/null
+++ b/drivers/firmware/stratix10-rsu.c
@@ -0,0 +1,451 @@
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/of_platform.h>
14#include <linux/platform_device.h>
15#include <linux/firmware/intel/stratix10-svc-client.h>
16#include <linux/string.h>
17#include <linux/sysfs.h>
18
19#define RSU_STATE_MASK GENMASK_ULL(31, 0)
20#define RSU_VERSION_MASK GENMASK_ULL(63, 32)
21#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0)
22#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32)
23#define RSU_FW_VERSION_MASK GENMASK_ULL(15, 0)
24
25#define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS))
26
27#define INVALID_RETRY_COUNTER 0xFFFFFFFF
28
29typedef void (*rsu_callback)(struct stratix10_svc_client *client,
30 struct stratix10_svc_cb_data *data);
31/**
32 * struct stratix10_rsu_priv - rsu data structure
33 * @chan: pointer to the allocated service channel
34 * @client: active service client
35 * @completion: state for callback completion
36 * @lock: a mutex to protect callback completion state
37 * @status.current_image: address of image currently running in flash
38 * @status.fail_image: address of failed image in flash
39 * @status.version: the version number of RSU firmware
40 * @status.state: the state of RSU system
41 * @status.error_details: error code
42 * @status.error_location: the error offset inside the image that failed
43 * @retry_counter: the current image's retry counter
44 */
45struct stratix10_rsu_priv {
46 struct stratix10_svc_chan *chan;
47 struct stratix10_svc_client client;
48 struct completion completion;
49 struct mutex lock;
50 struct {
51 unsigned long current_image;
52 unsigned long fail_image;
53 unsigned int version;
54 unsigned int state;
55 unsigned int error_details;
56 unsigned int error_location;
57 } status;
58 unsigned int retry_counter;
59};
60
61/**
62 * rsu_status_callback() - Status callback from Intel Service Layer
63 * @client: pointer to service client
64 * @data: pointer to callback data structure
65 *
66 * Callback from Intel service layer for RSU status request. Status is
67 * only updated after a system reboot, so a get updated status call is
68 * made during driver probe.
69 */
70static void rsu_status_callback(struct stratix10_svc_client *client,
71 struct stratix10_svc_cb_data *data)
72{
73 struct stratix10_rsu_priv *priv = client->priv;
74 struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
75
76 if (data->status == BIT(SVC_STATUS_RSU_OK)) {
77 priv->status.version = FIELD_GET(RSU_VERSION_MASK,
78 res->a2);
79 priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2);
80 priv->status.fail_image = res->a1;
81 priv->status.current_image = res->a0;
82 priv->status.error_location =
83 FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3);
84 priv->status.error_details =
85 FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3);
86 } else {
87 dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n",
88 res->a0);
89 priv->status.version = 0;
90 priv->status.state = 0;
91 priv->status.fail_image = 0;
92 priv->status.current_image = 0;
93 priv->status.error_location = 0;
94 priv->status.error_details = 0;
95 }
96
97 complete(&priv->completion);
98}
99
100/**
101 * rsu_command_callback() - Update callback from Intel Service Layer
102 * @client: pointer to client
103 * @data: pointer to callback data structure
104 *
105 * Callback from Intel service layer for RSU commands.
106 */
107static void rsu_command_callback(struct stratix10_svc_client *client,
108 struct stratix10_svc_cb_data *data)
109{
110 struct stratix10_rsu_priv *priv = client->priv;
111
112 if (data->status != BIT(SVC_STATUS_RSU_OK))
113 dev_err(client->dev, "RSU returned status is %i\n",
114 data->status);
115 complete(&priv->completion);
116}
117
118/**
119 * rsu_retry_callback() - Callback from Intel service layer for getting
120 * the current image's retry counter from firmware
121 * @client: pointer to client
122 * @data: pointer to callback data structure
123 *
124 * Callback from Intel service layer for retry counter, which is used by
125 * user to know how many times the images is still allowed to reload
126 * itself before giving up and starting RSU fail-over flow.
127 */
128static void rsu_retry_callback(struct stratix10_svc_client *client,
129 struct stratix10_svc_cb_data *data)
130{
131 struct stratix10_rsu_priv *priv = client->priv;
132 unsigned int *counter = (unsigned int *)data->kaddr1;
133
134 if (data->status == BIT(SVC_STATUS_RSU_OK))
135 priv->retry_counter = *counter;
136 else
137 dev_err(client->dev, "Failed to get retry counter %i\n",
138 data->status);
139
140 complete(&priv->completion);
141}
142
143/**
144 * rsu_send_msg() - send a message to Intel service layer
145 * @priv: pointer to rsu private data
146 * @command: RSU status or update command
147 * @arg: the request argument, the bitstream address or notify status
148 * @callback: function pointer for the callback (status or update)
149 *
150 * Start an Intel service layer transaction to perform the SMC call that
151 * is necessary to get RSU boot log or set the address of bitstream to
152 * boot after reboot.
153 *
154 * Returns 0 on success or -ETIMEDOUT on error.
155 */
156static int rsu_send_msg(struct stratix10_rsu_priv *priv,
157 enum stratix10_svc_command_code command,
158 unsigned long arg,
159 rsu_callback callback)
160{
161 struct stratix10_svc_client_msg msg;
162 int ret;
163
164 mutex_lock(&priv->lock);
165 reinit_completion(&priv->completion);
166 priv->client.receive_cb = callback;
167
168 msg.command = command;
169 if (arg)
170 msg.arg[0] = arg;
171
172 ret = stratix10_svc_send(priv->chan, &msg);
173 if (ret < 0)
174 goto status_done;
175
176 ret = wait_for_completion_interruptible_timeout(&priv->completion,
177 RSU_TIMEOUT);
178 if (!ret) {
179 dev_err(priv->client.dev,
180 "timeout waiting for SMC call\n");
181 ret = -ETIMEDOUT;
182 goto status_done;
183 } else if (ret < 0) {
184 dev_err(priv->client.dev,
185 "error %d waiting for SMC call\n", ret);
186 goto status_done;
187 } else {
188 ret = 0;
189 }
190
191status_done:
192 stratix10_svc_done(priv->chan);
193 mutex_unlock(&priv->lock);
194 return ret;
195}
196
197/*
198 * This driver exposes some optional features of the Intel Stratix 10 SoC FPGA.
199 * The sysfs interfaces exposed here are FPGA Remote System Update (RSU)
200 * related. They allow user space software to query the configuration system
201 * status and to request optional reboot behavior specific to Intel FPGAs.
202 */
203
204static ssize_t current_image_show(struct device *dev,
205 struct device_attribute *attr, char *buf)
206{
207 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
208
209 if (!priv)
210 return -ENODEV;
211
212 return sprintf(buf, "0x%08lx\n", priv->status.current_image);
213}
214
215static ssize_t fail_image_show(struct device *dev,
216 struct device_attribute *attr, char *buf)
217{
218 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
219
220 if (!priv)
221 return -ENODEV;
222
223 return sprintf(buf, "0x%08lx\n", priv->status.fail_image);
224}
225
226static ssize_t version_show(struct device *dev, struct device_attribute *attr,
227 char *buf)
228{
229 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
230
231 if (!priv)
232 return -ENODEV;
233
234 return sprintf(buf, "0x%08x\n", priv->status.version);
235}
236
237static ssize_t state_show(struct device *dev, struct device_attribute *attr,
238 char *buf)
239{
240 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
241
242 if (!priv)
243 return -ENODEV;
244
245 return sprintf(buf, "0x%08x\n", priv->status.state);
246}
247
248static ssize_t error_location_show(struct device *dev,
249 struct device_attribute *attr, char *buf)
250{
251 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
252
253 if (!priv)
254 return -ENODEV;
255
256 return sprintf(buf, "0x%08x\n", priv->status.error_location);
257}
258
259static ssize_t error_details_show(struct device *dev,
260 struct device_attribute *attr, char *buf)
261{
262 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
263
264 if (!priv)
265 return -ENODEV;
266
267 return sprintf(buf, "0x%08x\n", priv->status.error_details);
268}
269
270static ssize_t retry_counter_show(struct device *dev,
271 struct device_attribute *attr, char *buf)
272{
273 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
274
275 if (!priv)
276 return -ENODEV;
277
278 return sprintf(buf, "0x%08x\n", priv->retry_counter);
279}
280
281static ssize_t reboot_image_store(struct device *dev,
282 struct device_attribute *attr,
283 const char *buf, size_t count)
284{
285 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
286 unsigned long address;
287 int ret;
288
289 if (priv == 0)
290 return -ENODEV;
291
292 ret = kstrtoul(buf, 0, &address);
293 if (ret)
294 return ret;
295
296 ret = rsu_send_msg(priv, COMMAND_RSU_UPDATE,
297 address, rsu_command_callback);
298 if (ret) {
299 dev_err(dev, "Error, RSU update returned %i\n", ret);
300 return ret;
301 }
302
303 return count;
304}
305
306static ssize_t notify_store(struct device *dev,
307 struct device_attribute *attr,
308 const char *buf, size_t count)
309{
310 struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
311 unsigned long status;
312 int ret;
313
314 if (priv == 0)
315 return -ENODEV;
316
317 ret = kstrtoul(buf, 0, &status);
318 if (ret)
319 return ret;
320
321 ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY,
322 status, rsu_command_callback);
323 if (ret) {
324 dev_err(dev, "Error, RSU notify returned %i\n", ret);
325 return ret;
326 }
327
328 /* to get the updated state */
329 ret = rsu_send_msg(priv, COMMAND_RSU_STATUS,
330 0, rsu_status_callback);
331 if (ret) {
332 dev_err(dev, "Error, getting RSU status %i\n", ret);
333 return ret;
334 }
335
336 /* only 19.3 or late version FW supports retry counter feature */
337 if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) {
338 ret = rsu_send_msg(priv, COMMAND_RSU_RETRY,
339 0, rsu_retry_callback);
340 if (ret) {
341 dev_err(dev,
342 "Error, getting RSU retry %i\n", ret);
343 return ret;
344 }
345 }
346
347 return count;
348}
349
350static DEVICE_ATTR_RO(current_image);
351static DEVICE_ATTR_RO(fail_image);
352static DEVICE_ATTR_RO(state);
353static DEVICE_ATTR_RO(version);
354static DEVICE_ATTR_RO(error_location);
355static DEVICE_ATTR_RO(error_details);
356static DEVICE_ATTR_RO(retry_counter);
357static DEVICE_ATTR_WO(reboot_image);
358static DEVICE_ATTR_WO(notify);
359
360static struct attribute *rsu_attrs[] = {
361 &dev_attr_current_image.attr,
362 &dev_attr_fail_image.attr,
363 &dev_attr_state.attr,
364 &dev_attr_version.attr,
365 &dev_attr_error_location.attr,
366 &dev_attr_error_details.attr,
367 &dev_attr_retry_counter.attr,
368 &dev_attr_reboot_image.attr,
369 &dev_attr_notify.attr,
370 NULL
371};
372
373ATTRIBUTE_GROUPS(rsu);
374
375static int stratix10_rsu_probe(struct platform_device *pdev)
376{
377 struct device *dev = &pdev->dev;
378 struct stratix10_rsu_priv *priv;
379 int ret;
380
381 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
382 if (!priv)
383 return -ENOMEM;
384
385 priv->client.dev = dev;
386 priv->client.receive_cb = NULL;
387 priv->client.priv = priv;
388 priv->status.current_image = 0;
389 priv->status.fail_image = 0;
390 priv->status.error_location = 0;
391 priv->status.error_details = 0;
392 priv->status.version = 0;
393 priv->status.state = 0;
394 priv->retry_counter = INVALID_RETRY_COUNTER;
395
396 mutex_init(&priv->lock);
397 priv->chan = stratix10_svc_request_channel_byname(&priv->client,
398 SVC_CLIENT_RSU);
399 if (IS_ERR(priv->chan)) {
400 dev_err(dev, "couldn't get service channel %s\n",
401 SVC_CLIENT_RSU);
402 return PTR_ERR(priv->chan);
403 }
404
405 init_completion(&priv->completion);
406 platform_set_drvdata(pdev, priv);
407
408 /* get the initial state from firmware */
409 ret = rsu_send_msg(priv, COMMAND_RSU_STATUS,
410 0, rsu_status_callback);
411 if (ret) {
412 dev_err(dev, "Error, getting RSU status %i\n", ret);
413 stratix10_svc_free_channel(priv->chan);
414 }
415
416 /* only 19.3 or late version FW supports retry counter feature */
417 if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) {
418 ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0,
419 rsu_retry_callback);
420 if (ret) {
421 dev_err(dev,
422 "Error, getting RSU retry %i\n", ret);
423 stratix10_svc_free_channel(priv->chan);
424 }
425 }
426
427 return ret;
428}
429
430static int stratix10_rsu_remove(struct platform_device *pdev)
431{
432 struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev);
433
434 stratix10_svc_free_channel(priv->chan);
435 return 0;
436}
437
438static struct platform_driver stratix10_rsu_driver = {
439 .probe = stratix10_rsu_probe,
440 .remove = stratix10_rsu_remove,
441 .driver = {
442 .name = "stratix10-rsu",
443 .dev_groups = rsu_groups,
444 },
445};
446
447module_platform_driver(stratix10_rsu_driver);
448
449MODULE_LICENSE("GPL v2");
450MODULE_DESCRIPTION("Intel Remote System Update Driver");
451MODULE_AUTHOR("Richard Gong <richard.gong@intel.com>");