summaryrefslogtreecommitdiff
path: root/drivers/firmware/stratix10-rsu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/stratix10-rsu.c')
-rw-r--r--drivers/firmware/stratix10-rsu.c421
1 files changed, 399 insertions, 22 deletions
diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c
index daddb5224794..b360f752d191 100644
--- a/drivers/firmware/stratix10-rsu.c
+++ b/drivers/firmware/stratix10-rsu.c
@@ -7,17 +7,28 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/firmware/intel/stratix10-svc-client.h>
#include <linux/string.h>
#include <linux/sysfs.h>
-#include <linux/delay.h>
-#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32)
+#define RSU_STATE_MASK GENMASK_ULL(31, 0)
+#define RSU_VERSION_MASK GENMASK_ULL(63, 32)
+#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0)
+#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32)
+/*
+ * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as:
+ * [63:32] erase_size, [31:0] size (see stratix10-smc.h).
+ */
+#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0)
+#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32)
+
#define RSU_DCMF0_MASK GENMASK_ULL(31, 0)
#define RSU_DCMF1_MASK GENMASK_ULL(63, 32)
#define RSU_DCMF2_MASK GENMASK_ULL(31, 0)
@@ -33,11 +44,32 @@
#define INVALID_DCMF_VERSION 0xFF
#define INVALID_DCMF_STATUS 0xFFFFFFFF
#define INVALID_SPT_ADDRESS 0x0
+#define INVALID_DEVICE_INFO (~0U)
#define RSU_RETRY_SLEEP_MS (1U)
#define RSU_ASYNC_MSG_RETRY (3U)
+#define RSU_GET_SPT_CMD 0x5A
#define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int))
+struct flash_device_info {
+ unsigned int size;
+ unsigned int erase_size;
+};
+
+/**
+ * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word
+ * @di: slot to fill
+ * @packed: register value: [63:32] erase_size, [31:0] size
+ * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO)
+ */
+static void rsu_device_info_set_from_packed(struct flash_device_info *di,
+ unsigned long packed)
+{
+ di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed);
+ di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK,
+ packed);
+}
+
typedef void (*rsu_callback)(struct stratix10_svc_client *client,
struct stratix10_svc_cb_data *data);
/**
@@ -46,6 +78,7 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
* @client: active service client
* @completion: state for callback completion
* @lock: a mutex to protect callback completion state
+ * @async: supports async operations
* @status.current_image: address of image currently running in flash
* @status.fail_image: address of failed image in flash
* @status.version: the interface version number of RSU firmware
@@ -60,16 +93,20 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
* @dcmf_status.dcmf1: dcmf1 status
* @dcmf_status.dcmf2: dcmf2 status
* @dcmf_status.dcmf3: dcmf3 status
+ * @device_info: per-device flash information array; each entry contains
+ * size and erase size for one flash device
* @retry_counter: the current image's retry counter
* @max_retry: the preset max retry value
* @spt0_address: address of spt0
* @spt1_address: address of spt1
+ * @get_spt_response_buf: response from sdm for get_spt command
*/
struct stratix10_rsu_priv {
struct stratix10_svc_chan *chan;
struct stratix10_svc_client client;
struct completion completion;
struct mutex lock;
+ bool async;
struct {
unsigned long current_image;
unsigned long fail_image;
@@ -93,17 +130,74 @@ struct stratix10_rsu_priv {
unsigned int dcmf3;
} dcmf_status;
+ struct flash_device_info device_info[4];
+
unsigned int retry_counter;
unsigned int max_retry;
unsigned long spt0_address;
unsigned long spt1_address;
+
+ unsigned int *get_spt_response_buf;
};
+/**
+ * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid
+ * @priv: RSU private data
+ */
+static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) {
+ priv->device_info[i].size = INVALID_DEVICE_INFO;
+ priv->device_info[i].erase_size = INVALID_DEVICE_INFO;
+ }
+}
+
typedef void (*rsu_async_callback)(struct device *dev,
struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data);
/**
+ * rsu_status_callback() - Status callback from Intel Service Layer
+ * @client: pointer to service client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for RSU status request. Status is
+ * only updated after a system reboot, so a get updated status call is
+ * made during driver probe.
+ */
+static void rsu_status_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->status.version = FIELD_GET(RSU_VERSION_MASK,
+ res->a2);
+ priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2);
+ priv->status.fail_image = res->a1;
+ priv->status.current_image = res->a0;
+ priv->status.error_location =
+ FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3);
+ priv->status.error_details =
+ FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3);
+ } else {
+ dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n",
+ res->a0);
+ priv->status.version = 0;
+ priv->status.state = 0;
+ priv->status.fail_image = 0;
+ priv->status.current_image = 0;
+ priv->status.error_location = 0;
+ priv->status.error_details = 0;
+ }
+
+ complete(&priv->completion);
+}
+
+/**
* rsu_async_status_callback() - Status callback from rsu_async_send()
* @dev: pointer to device object
* @priv: pointer to priv object
@@ -147,6 +241,32 @@ static void rsu_command_callback(struct stratix10_svc_client *client,
complete(&priv->completion);
}
+/**
+ * rsu_retry_callback() - Callback from Intel service layer for getting
+ * the current image's retry counter from the firmware
+ * @client: pointer to client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for retry counter, which is used by
+ * user to know how many times the images is still allowed to reload
+ * itself before giving up and starting RSU fail-over flow.
+ */
+static void rsu_retry_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ unsigned int *counter = (unsigned int *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK))
+ priv->retry_counter = *counter;
+ else if (data->status == BIT(SVC_STATUS_NO_SUPPORT))
+ dev_warn(client->dev, "Secure FW doesn't support retry\n");
+ else
+ dev_err(client->dev, "Failed to get retry counter %lu\n",
+ BIT(data->status));
+
+ complete(&priv->completion);
+}
/**
* rsu_max_retry_callback() - Callback from Intel service layer for getting
@@ -229,8 +349,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client,
}
/**
- * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send()
- * to retrieve the SPT table information.
+ * rsu_get_device_info_callback() - Callback from Intel service layer for
+ * getting the QSPI device info
+ * @client: pointer to client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for QSPI device info.
+ * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or
+ * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command).
+ */
+static void rsu_get_device_info_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ struct arm_smccc_1_2_regs *res = data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ if (!res) {
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n");
+ rsu_device_info_invalidate(priv);
+ complete(&priv->completion);
+ return;
+ }
+
+ rsu_device_info_set_from_packed(&priv->device_info[0], res->a1);
+ rsu_device_info_set_from_packed(&priv->device_info[1], res->a2);
+ rsu_device_info_set_from_packed(&priv->device_info[2], res->a3);
+ rsu_device_info_set_from_packed(&priv->device_info[3], res->a4);
+
+ } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) {
+ dev_warn(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n");
+ rsu_device_info_invalidate(priv);
+ } else {
+ if (res)
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n",
+ res->a0);
+ else
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n",
+ data->status);
+ rsu_device_info_invalidate(priv);
+ }
+
+ complete(&priv->completion);
+}
+
+/**
+ * rsu_async_get_spt_table_callback() - Callback to be used by the
+ * rsu_async_send() to retrieve the SPT table information.
* @dev: pointer to device object
* @priv: pointer to priv object
* @data: pointer to callback data structure
@@ -243,6 +412,38 @@ static void rsu_async_get_spt_table_callback(struct device *dev,
priv->spt1_address = *((unsigned long *)data->kaddr2);
}
+static void rsu_get_spt_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ unsigned long *mbox_err = (unsigned long *)data->kaddr1;
+ unsigned long *resp_len = (unsigned long *)data->kaddr2;
+
+ if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) ||
+ (*resp_len != RSU_GET_SPT_RESP_LEN))
+ goto error;
+
+ priv->spt0_address = priv->get_spt_response_buf[0];
+ priv->spt0_address <<= 32;
+ priv->spt0_address |= priv->get_spt_response_buf[1];
+ priv->spt1_address = priv->get_spt_response_buf[2];
+ priv->spt1_address <<= 32;
+ priv->spt1_address |= priv->get_spt_response_buf[3];
+
+ goto complete;
+
+error:
+ dev_err(priv->client.dev,
+ "failed to get SPTs (status=%#x, mbox_err=%lu, resp_len=%lu)\n",
+ data->status, mbox_err ? *mbox_err : 0,
+ resp_len ? *resp_len : 0);
+
+complete:
+ stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ complete(&priv->completion);
+}
+
/**
* __rsu_send_msg_locked() - send a message to Intel service layer
* @priv: pointer to rsu private data
@@ -259,7 +460,7 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv,
unsigned long arg,
rsu_callback callback)
{
- struct stratix10_svc_client_msg msg;
+ struct stratix10_svc_client_msg msg = {0};
int ret;
lockdep_assert_held(&priv->lock);
@@ -271,6 +472,14 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv,
if (arg)
msg.arg[0] = arg;
+ if (command == COMMAND_MBOX_SEND_CMD) {
+ msg.arg[1] = 0;
+ msg.payload = NULL;
+ msg.payload_length = 0;
+ msg.payload_output = priv->get_spt_response_buf;
+ msg.payload_length_output = RSU_GET_SPT_RESP_LEN;
+ }
+
ret = stratix10_svc_send(priv->chan, &msg);
if (ret < 0)
goto status_done;
@@ -395,6 +604,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri
if (status && !handle) {
dev_err(dev, "Failed to send async message\n");
+ if (msg.payload_output)
+ stratix10_svc_free_memory(priv->chan, msg.payload_output);
return -ETIMEDOUT;
}
@@ -434,6 +645,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri
}
status_done:
+ if (msg.payload_output)
+ stratix10_svc_free_memory(priv->chan, msg.payload_output);
stratix10_svc_async_done(priv->chan, handle);
return ret;
}
@@ -681,15 +894,30 @@ static ssize_t notify_store(struct device *dev,
if (ret)
return ret;
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL);
+ if (priv->async)
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL);
+ else
+ ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, status, rsu_command_callback);
if (ret) {
dev_err(dev, "Error, RSU notify returned %i\n", ret);
return ret;
}
/* to get the updated state */
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
- rsu_async_status_callback);
+ if (priv->async) {
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
+ rsu_async_status_callback);
+ } else {
+ ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0,
+ rsu_status_callback);
+ /*
+ * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS;
+ * issue it separately only in synchronous mode.
+ */
+ if (!ret)
+ ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0,
+ rsu_retry_callback);
+ }
if (ret) {
dev_err(dev, "Error, getting RSU status %i\n", ret);
return ret;
@@ -698,6 +926,75 @@ static ssize_t notify_store(struct device *dev,
return count;
}
+static ssize_t rsu_device_info_show(struct device *dev, char *buf,
+ unsigned int index, bool erase_size)
+{
+ struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
+ unsigned int value;
+
+ if (!priv)
+ return -ENODEV;
+
+ if (index >= ARRAY_SIZE(priv->device_info))
+ return -EINVAL;
+
+ value = erase_size ? priv->device_info[index].erase_size :
+ priv->device_info[index].size;
+
+ if (value == INVALID_DEVICE_INFO)
+ return -EIO;
+
+ return sysfs_emit(buf, "0x%08x\n", value);
+}
+
+static ssize_t size0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 0, false);
+}
+
+static ssize_t size1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 1, false);
+}
+
+static ssize_t size2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 2, false);
+}
+
+static ssize_t size3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 3, false);
+}
+
+static ssize_t erase_size0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 0, true);
+}
+
+static ssize_t erase_size1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 1, true);
+}
+
+static ssize_t erase_size2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 2, true);
+}
+
+static ssize_t erase_size3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 3, true);
+}
+
static ssize_t spt0_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -742,6 +1039,14 @@ static DEVICE_ATTR_RO(dcmf0_status);
static DEVICE_ATTR_RO(dcmf1_status);
static DEVICE_ATTR_RO(dcmf2_status);
static DEVICE_ATTR_RO(dcmf3_status);
+static DEVICE_ATTR_RO(size0);
+static DEVICE_ATTR_RO(size1);
+static DEVICE_ATTR_RO(size2);
+static DEVICE_ATTR_RO(size3);
+static DEVICE_ATTR_RO(erase_size0);
+static DEVICE_ATTR_RO(erase_size1);
+static DEVICE_ATTR_RO(erase_size2);
+static DEVICE_ATTR_RO(erase_size3);
static DEVICE_ATTR_WO(reboot_image);
static DEVICE_ATTR_WO(notify);
static DEVICE_ATTR_RO(spt0_address);
@@ -764,6 +1069,14 @@ static struct attribute *rsu_attrs[] = {
&dev_attr_dcmf1_status.attr,
&dev_attr_dcmf2_status.attr,
&dev_attr_dcmf3_status.attr,
+ &dev_attr_size0.attr,
+ &dev_attr_size1.attr,
+ &dev_attr_size2.attr,
+ &dev_attr_size3.attr,
+ &dev_attr_erase_size0.attr,
+ &dev_attr_erase_size1.attr,
+ &dev_attr_erase_size2.attr,
+ &dev_attr_erase_size3.attr,
&dev_attr_reboot_image.attr,
&dev_attr_notify.attr,
&dev_attr_spt0_address.attr,
@@ -795,7 +1108,12 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS;
- /* spt0/1_address and status fields default to 0 from kzalloc */
+ priv->max_retry = INVALID_RETRY_COUNTER;
+ priv->spt0_address = INVALID_SPT_ADDRESS;
+ priv->spt1_address = INVALID_SPT_ADDRESS;
+ priv->get_spt_response_buf = NULL;
+ priv->async = false;
+ rsu_device_info_invalidate(priv);
mutex_init(&priv->lock);
init_completion(&priv->completion);
@@ -809,19 +1127,39 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
}
ret = stratix10_svc_add_async_client(priv->chan, false);
- if (ret) {
- dev_err(dev, "failed to add async client\n");
- goto free_channel;
+ if (ret < 0) {
+ dev_dbg(dev, "Async operations not supported, fallback to non-async mode\n");
+ priv->async = false;
+ } else {
+ priv->async = true;
}
platform_set_drvdata(pdev, priv);
/* get the initial state from firmware */
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
- rsu_async_status_callback);
+ if (priv->async)
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
+ rsu_async_status_callback);
+ else
+ ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0,
+ rsu_status_callback);
if (ret) {
dev_err(dev, "Error, getting RSU status %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
+ }
+
+ /*
+ * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS;
+ * issue it separately only in synchronous mode.
+ */
+ if (!priv->async) {
+ ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
+ if (ret) {
+ dev_err(dev, "Error, getting RSU retry %i\n", ret);
+ goto free_channel;
+ }
}
/* get DCMF version from firmware */
@@ -829,28 +1167,64 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
rsu_dcmf_version_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF version %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0,
rsu_dcmf_status_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF status %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0,
rsu_max_retry_callback);
if (ret) {
dev_err(dev, "Error, getting RSU max retry %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
+ }
+
+ /* get QSPI device info from firmware */
+ ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0,
+ rsu_get_device_info_callback);
+ if (ret) {
+ dev_err(dev, "Error, getting QSPI Device Info %i\n", ret);
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0,
- rsu_async_get_spt_table_callback);
+ if (priv->async) {
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE,
+ 0, rsu_async_get_spt_table_callback);
+ } else {
+ priv->get_spt_response_buf =
+ stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN);
+ if (IS_ERR(priv->get_spt_response_buf)) {
+ ret = PTR_ERR(priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ dev_err(dev, "failed to allocate get spt buffer\n");
+ } else {
+ ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD,
+ RSU_GET_SPT_CMD, rsu_get_spt_callback);
+ }
+ }
if (ret) {
dev_err(dev, "Error, getting SPT table %i\n", ret);
- goto remove_async_client;
+ if (priv->async) {
+ goto remove_async_client;
+ } else if (!IS_ERR_OR_NULL(priv->get_spt_response_buf)) {
+ stratix10_svc_free_memory(priv->chan,
+ priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ }
+ goto free_channel;
}
return 0;
@@ -866,6 +1240,9 @@ static void stratix10_rsu_remove(struct platform_device *pdev)
{
struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev);
+ if (priv->async)
+ stratix10_svc_remove_async_client(priv->chan);
+
stratix10_svc_free_channel(priv->chan);
}