summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-31 14:14:51 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-31 14:14:51 +0200
commita00ea3c09274c24f01587d2183df20bc3ee593cc (patch)
tree34cf4be145c87f250edff98892f20f55e26442d4 /drivers/firmware
parent0957fbab972a9499626ac457fd924b24491c6315 (diff)
parent758331ab5f9355352ccac3d7a83d3586bec19f9e (diff)
Merge tag 'svc_updates_for_v7.3' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux into char-misc-next
SoCFPGA firmware updates for v7.3 - Document stratix10-rsu for QSPI size and erase size - Add method to retrieve device info table using RSU - Add support for hardware monitoring using service driver Resolves merge conflicts in: drivers/firmware/stratix10-svc.c Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> * tag 'svc_updates_for_v7.3' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux: hwmon: add Altera SoC FPGA hardware monitoring driver firmware: stratix10-svc: add async HWMON read commands and register socfpga-hwmon device firmware: stratix10-rsu: Add synchronous fallback for async SVC operations firmware: stratix10-rsu: Add flash device info retrieval via SMC Documentation: ABI: add stratix10-rsu QSPI size and erase_size sysfs
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/stratix10-rsu.c421
-rw-r--r--drivers/firmware/stratix10-svc.c139
2 files changed, 526 insertions, 34 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;
}
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0,
- rsu_async_get_spt_table_callback);
+ /* 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;
+ }
+
+ 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);
}
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index 5e20057ee344..f8c2da207cb4 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/firmware/intel/stratix10-smc.h>
#include <linux/firmware/intel/stratix10-svc-client.h>
#include <linux/types.h>
@@ -45,6 +46,7 @@
/* stratix10 service layer clients */
#define STRATIX10_RSU "stratix10-rsu"
+#define SOCFPGA_HWMON "socfpga-hwmon"
/* Maximum number of SDM client IDs. */
#define MAX_SDM_CLIENT_IDS 16
@@ -104,9 +106,11 @@ struct stratix10_svc_chan;
/**
* struct stratix10_svc - svc private data
* @stratix10_svc_rsu: pointer to stratix10 RSU device
+ * @stratix10_svc_hwmon: pointer to stratix10 HWMON device
*/
struct stratix10_svc {
struct platform_device *stratix10_svc_rsu;
+ struct platform_device *stratix10_svc_hwmon;
};
/**
@@ -445,13 +449,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl,
* svc_thread_recv_status_ok() - handle the successful status
* @p_data: pointer to service data structure
* @cb_data: pointer to callback data structure to service client
- * @res: result from SMC or HVC call
+ * @res: result from SMC or HVC call (a0-a3; used for routing and most commands)
+ * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL
*
* Send back the correspond status to the service clients.
*/
static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
struct stratix10_svc_cb_data *cb_data,
- struct arm_smccc_res res)
+ struct arm_smccc_res res,
+ struct arm_smccc_1_2_regs *res12)
{
cb_data->kaddr1 = NULL;
cb_data->kaddr2 = NULL;
@@ -513,6 +519,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
cb_data->kaddr2 = &res.a2;
break;
+ case COMMAND_RSU_GET_DEVICE_INFO:
+ if (WARN_ON(!res12)) {
+ cb_data->status = BIT(SVC_STATUS_ERROR);
+ break;
+ }
+ cb_data->status = BIT(SVC_STATUS_OK);
+ cb_data->kaddr1 = res12;
+ cb_data->kaddr2 = NULL;
+ cb_data->kaddr3 = NULL;
+ break;
default:
pr_warn("it shouldn't happen\n");
break;
@@ -522,6 +538,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data);
}
+static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl,
+ const struct arm_smccc_1_2_regs *args,
+ struct arm_smccc_1_2_regs *res);
+
/**
* svc_normal_to_secure_thread() - the function to run in the kthread
* @data: data pointer for kthread function
@@ -539,6 +559,7 @@ static int svc_normal_to_secure_thread(void *data)
struct stratix10_svc_data *pdata = NULL;
struct stratix10_svc_cb_data *cbdata = NULL;
struct arm_smccc_res res;
+ struct arm_smccc_1_2_regs res12 = { 0 };
unsigned long a0, a1, a2, a3, a4, a5, a6, a7;
int ret_fifo = 0;
@@ -727,6 +748,16 @@ static int svc_normal_to_secure_thread(void *data)
a5 = (unsigned long)pdata->paddr_output;
a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE;
break;
+ case COMMAND_RSU_GET_DEVICE_INFO:
+ a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO;
+ a1 = 0;
+ a2 = 0;
+ a3 = 0;
+ a4 = 0;
+ a5 = 0;
+ a6 = 0;
+ a7 = 0;
+ break;
default:
pr_warn("it shouldn't happen\n");
mutex_unlock(&ctrl->sdm_lock);
@@ -740,7 +771,18 @@ static int svc_normal_to_secure_thread(void *data)
pr_debug(" a3=0x%016x\n", (unsigned int)a3);
pr_debug(" a4=0x%016x\n", (unsigned int)a4);
pr_debug(" a5=0x%016x\n", (unsigned int)a5);
- ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
+ if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) {
+ struct arm_smccc_1_2_regs args12 = { 0 };
+
+ args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO;
+ svc_smccc_1_2_full(ctrl, &args12, &res12);
+ res.a0 = res12.a0;
+ res.a1 = res12.a1;
+ res.a2 = res12.a2;
+ res.a3 = res12.a3;
+ } else {
+ ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
+ }
pr_debug("%s: %s: after SMC call -- res.a0=0x%016x",
__func__, chan->name, (unsigned int)res.a0);
@@ -763,9 +805,15 @@ static int svc_normal_to_secure_thread(void *data)
}
switch (res.a0) {
- case INTEL_SIP_SMC_STATUS_OK:
- svc_thread_recv_status_ok(pdata, cbdata, res);
+ case INTEL_SIP_SMC_STATUS_OK: {
+ struct arm_smccc_1_2_regs *devinfo_res =
+ (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ?
+ &res12 : NULL;
+
+ svc_thread_recv_status_ok(pdata, cbdata, res,
+ devinfo_res);
break;
+ }
case INTEL_SIP_SMC_STATUS_BUSY:
switch (pdata->command) {
case COMMAND_RECONFIG_DATA_SUBMIT:
@@ -806,10 +854,16 @@ static int svc_normal_to_secure_thread(void *data)
case INTEL_SIP_SMC_RSU_ERROR:
pr_err("%s: STATUS_ERROR\n", __func__);
cbdata->status = BIT(SVC_STATUS_ERROR);
- cbdata->kaddr1 = &res.a1;
- cbdata->kaddr2 = (res.a2) ?
- svc_pa_to_va(res.a2) : NULL;
- cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
+ if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) {
+ cbdata->kaddr1 = &res12;
+ cbdata->kaddr2 = NULL;
+ cbdata->kaddr3 = NULL;
+ } else {
+ cbdata->kaddr1 = &res.a1;
+ cbdata->kaddr2 = (res.a2) ?
+ svc_pa_to_va(res.a2) : NULL;
+ cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
+ }
pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
break;
default:
@@ -1026,6 +1080,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1,
}
/**
+ * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method
+ * @ctrl: service controller (selects SMC vs HVC)
+ * @args: arguments
+ * @res: full register-file result (a0-a17)
+ */
+static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl,
+ const struct arm_smccc_1_2_regs *args,
+ struct arm_smccc_1_2_regs *res)
+{
+ if (ctrl->invoke_fn == svc_smccc_smc) {
+ arm_smccc_1_2_smc(args, res);
+ } else if (ctrl->invoke_fn == svc_smccc_hvc) {
+ arm_smccc_1_2_hvc(args, res);
+ } else {
+ WARN_ON_ONCE(1);
+ /*
+ * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted
+ * as success. Force an error path and clear fabricated payload.
+ */
+ memset(res, 0, sizeof(*res));
+ res->a0 = INTEL_SIP_SMC_STATUS_ERROR;
+ }
+}
+
+/**
* get_invoke_func() - invoke SMC or HVC call
* @dev: pointer to device
*
@@ -1329,6 +1408,14 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg,
args.a0 = INTEL_SIP_SMC_ASYNC_RSU_NOTIFY;
args.a2 = p_msg->arg[0];
break;
+ case COMMAND_HWMON_READTEMP:
+ args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READTEMP;
+ args.a2 = p_msg->arg[0];
+ break;
+ case COMMAND_HWMON_READVOLT:
+ args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READVOLT;
+ args.a2 = p_msg->arg[0];
+ break;
default:
dev_err(ctrl->dev, "Invalid command ,%d\n", p_msg->command);
ret = -EINVAL;
@@ -1422,6 +1509,10 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan,
*/
data->kaddr1 = (void *)&handle->res;
break;
+ case COMMAND_HWMON_READTEMP:
+ case COMMAND_HWMON_READVOLT:
+ data->kaddr1 = (void *)&handle->res.a2;
+ break;
default:
dev_alert(ctrl->dev, "Invalid command\n ,%d", p_msg->command);
@@ -2016,16 +2107,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
if (ret)
goto err_put_device;
+ if (IS_ENABLED(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON)) {
+ svc->stratix10_svc_hwmon =
+ platform_device_alloc(SOCFPGA_HWMON, 0);
+ if (!svc->stratix10_svc_hwmon) {
+ dev_err(dev, "failed to allocate %s device\n",
+ SOCFPGA_HWMON);
+ } else {
+ svc->stratix10_svc_hwmon->dev.parent = dev;
+
+ ret = platform_device_add(svc->stratix10_svc_hwmon);
+ if (ret) {
+ dev_err(dev, "failed to add %s device: %d\n",
+ SOCFPGA_HWMON, ret);
+ platform_device_put(svc->stratix10_svc_hwmon);
+ svc->stratix10_svc_hwmon = NULL;
+ }
+ }
+ }
+
ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
if (ret)
- goto err_unregister_rsu_dev;
+ goto err_unregister_clients;
pr_info("Intel Service Layer Driver Initialized\n");
return 0;
-err_unregister_rsu_dev:
- platform_device_unregister(svc->stratix10_svc_rsu);
+err_unregister_clients:
+ if (svc->stratix10_svc_hwmon)
+ platform_device_unregister(svc->stratix10_svc_hwmon);
+ if (svc->stratix10_svc_rsu)
+ platform_device_unregister(svc->stratix10_svc_rsu);
goto err_free_fifos;
err_put_device:
platform_device_put(svc->stratix10_svc_rsu);
@@ -2050,6 +2163,8 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
struct stratix10_svc *svc = ctrl->svc;
platform_device_unregister(svc->stratix10_svc_rsu);
+ if (svc->stratix10_svc_hwmon)
+ platform_device_unregister(svc->stratix10_svc_hwmon);
stratix10_svc_async_exit(ctrl);