18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019, Intel Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
78c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
88c2ecf20Sopenharmony_ci#include <linux/completion.h>
98c2ecf20Sopenharmony_ci#include <linux/kobject.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/firmware/intel/stratix10-svc-client.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define RSU_STATE_MASK			GENMASK_ULL(31, 0)
208c2ecf20Sopenharmony_ci#define RSU_VERSION_MASK		GENMASK_ULL(63, 32)
218c2ecf20Sopenharmony_ci#define RSU_ERROR_LOCATION_MASK		GENMASK_ULL(31, 0)
228c2ecf20Sopenharmony_ci#define RSU_ERROR_DETAIL_MASK		GENMASK_ULL(63, 32)
238c2ecf20Sopenharmony_ci#define RSU_DCMF0_MASK			GENMASK_ULL(31, 0)
248c2ecf20Sopenharmony_ci#define RSU_DCMF1_MASK			GENMASK_ULL(63, 32)
258c2ecf20Sopenharmony_ci#define RSU_DCMF2_MASK			GENMASK_ULL(31, 0)
268c2ecf20Sopenharmony_ci#define RSU_DCMF3_MASK			GENMASK_ULL(63, 32)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define RSU_TIMEOUT	(msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS))
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define INVALID_RETRY_COUNTER		0xFF
318c2ecf20Sopenharmony_ci#define INVALID_DCMF_VERSION		0xFF
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_citypedef void (*rsu_callback)(struct stratix10_svc_client *client,
358c2ecf20Sopenharmony_ci			     struct stratix10_svc_cb_data *data);
368c2ecf20Sopenharmony_ci/**
378c2ecf20Sopenharmony_ci * struct stratix10_rsu_priv - rsu data structure
388c2ecf20Sopenharmony_ci * @chan: pointer to the allocated service channel
398c2ecf20Sopenharmony_ci * @client: active service client
408c2ecf20Sopenharmony_ci * @completion: state for callback completion
418c2ecf20Sopenharmony_ci * @lock: a mutex to protect callback completion state
428c2ecf20Sopenharmony_ci * @status.current_image: address of image currently running in flash
438c2ecf20Sopenharmony_ci * @status.fail_image: address of failed image in flash
448c2ecf20Sopenharmony_ci * @status.version: the interface version number of RSU firmware
458c2ecf20Sopenharmony_ci * @status.state: the state of RSU system
468c2ecf20Sopenharmony_ci * @status.error_details: error code
478c2ecf20Sopenharmony_ci * @status.error_location: the error offset inside the image that failed
488c2ecf20Sopenharmony_ci * @dcmf_version.dcmf0: Quartus dcmf0 version
498c2ecf20Sopenharmony_ci * @dcmf_version.dcmf1: Quartus dcmf1 version
508c2ecf20Sopenharmony_ci * @dcmf_version.dcmf2: Quartus dcmf2 version
518c2ecf20Sopenharmony_ci * @dcmf_version.dcmf3: Quartus dcmf3 version
528c2ecf20Sopenharmony_ci * @retry_counter: the current image's retry counter
538c2ecf20Sopenharmony_ci * @max_retry: the preset max retry value
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistruct stratix10_rsu_priv {
568c2ecf20Sopenharmony_ci	struct stratix10_svc_chan *chan;
578c2ecf20Sopenharmony_ci	struct stratix10_svc_client client;
588c2ecf20Sopenharmony_ci	struct completion completion;
598c2ecf20Sopenharmony_ci	struct mutex lock;
608c2ecf20Sopenharmony_ci	struct {
618c2ecf20Sopenharmony_ci		unsigned long current_image;
628c2ecf20Sopenharmony_ci		unsigned long fail_image;
638c2ecf20Sopenharmony_ci		unsigned int version;
648c2ecf20Sopenharmony_ci		unsigned int state;
658c2ecf20Sopenharmony_ci		unsigned int error_details;
668c2ecf20Sopenharmony_ci		unsigned int error_location;
678c2ecf20Sopenharmony_ci	} status;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	struct {
708c2ecf20Sopenharmony_ci		unsigned int dcmf0;
718c2ecf20Sopenharmony_ci		unsigned int dcmf1;
728c2ecf20Sopenharmony_ci		unsigned int dcmf2;
738c2ecf20Sopenharmony_ci		unsigned int dcmf3;
748c2ecf20Sopenharmony_ci	} dcmf_version;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	unsigned int retry_counter;
778c2ecf20Sopenharmony_ci	unsigned int max_retry;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * rsu_status_callback() - Status callback from Intel Service Layer
828c2ecf20Sopenharmony_ci * @client: pointer to service client
838c2ecf20Sopenharmony_ci * @data: pointer to callback data structure
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * Callback from Intel service layer for RSU status request. Status is
868c2ecf20Sopenharmony_ci * only updated after a system reboot, so a get updated status call is
878c2ecf20Sopenharmony_ci * made during driver probe.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic void rsu_status_callback(struct stratix10_svc_client *client,
908c2ecf20Sopenharmony_ci				struct stratix10_svc_cb_data *data)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = client->priv;
938c2ecf20Sopenharmony_ci	struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (data->status == BIT(SVC_STATUS_OK)) {
968c2ecf20Sopenharmony_ci		priv->status.version = FIELD_GET(RSU_VERSION_MASK,
978c2ecf20Sopenharmony_ci						 res->a2);
988c2ecf20Sopenharmony_ci		priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2);
998c2ecf20Sopenharmony_ci		priv->status.fail_image = res->a1;
1008c2ecf20Sopenharmony_ci		priv->status.current_image = res->a0;
1018c2ecf20Sopenharmony_ci		priv->status.error_location =
1028c2ecf20Sopenharmony_ci			FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3);
1038c2ecf20Sopenharmony_ci		priv->status.error_details =
1048c2ecf20Sopenharmony_ci			FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3);
1058c2ecf20Sopenharmony_ci	} else {
1068c2ecf20Sopenharmony_ci		dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n",
1078c2ecf20Sopenharmony_ci			res->a0);
1088c2ecf20Sopenharmony_ci		priv->status.version = 0;
1098c2ecf20Sopenharmony_ci		priv->status.state = 0;
1108c2ecf20Sopenharmony_ci		priv->status.fail_image = 0;
1118c2ecf20Sopenharmony_ci		priv->status.current_image = 0;
1128c2ecf20Sopenharmony_ci		priv->status.error_location = 0;
1138c2ecf20Sopenharmony_ci		priv->status.error_details = 0;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	complete(&priv->completion);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/**
1208c2ecf20Sopenharmony_ci * rsu_command_callback() - Update callback from Intel Service Layer
1218c2ecf20Sopenharmony_ci * @client: pointer to client
1228c2ecf20Sopenharmony_ci * @data: pointer to callback data structure
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Callback from Intel service layer for RSU commands.
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistatic void rsu_command_callback(struct stratix10_svc_client *client,
1278c2ecf20Sopenharmony_ci				 struct stratix10_svc_cb_data *data)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = client->priv;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (data->status == BIT(SVC_STATUS_NO_SUPPORT))
1328c2ecf20Sopenharmony_ci		dev_warn(client->dev, "FW doesn't support notify\n");
1338c2ecf20Sopenharmony_ci	else if (data->status == BIT(SVC_STATUS_ERROR))
1348c2ecf20Sopenharmony_ci		dev_err(client->dev, "Failure, returned status is %lu\n",
1358c2ecf20Sopenharmony_ci			BIT(data->status));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	complete(&priv->completion);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * rsu_retry_callback() - Callback from Intel service layer for getting
1428c2ecf20Sopenharmony_ci * the current image's retry counter from the firmware
1438c2ecf20Sopenharmony_ci * @client: pointer to client
1448c2ecf20Sopenharmony_ci * @data: pointer to callback data structure
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Callback from Intel service layer for retry counter, which is used by
1478c2ecf20Sopenharmony_ci * user to know how many times the images is still allowed to reload
1488c2ecf20Sopenharmony_ci * itself before giving up and starting RSU fail-over flow.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic void rsu_retry_callback(struct stratix10_svc_client *client,
1518c2ecf20Sopenharmony_ci			       struct stratix10_svc_cb_data *data)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = client->priv;
1548c2ecf20Sopenharmony_ci	unsigned int *counter = (unsigned int *)data->kaddr1;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (data->status == BIT(SVC_STATUS_OK))
1578c2ecf20Sopenharmony_ci		priv->retry_counter = *counter;
1588c2ecf20Sopenharmony_ci	else if (data->status == BIT(SVC_STATUS_NO_SUPPORT))
1598c2ecf20Sopenharmony_ci		dev_warn(client->dev, "FW doesn't support retry\n");
1608c2ecf20Sopenharmony_ci	else
1618c2ecf20Sopenharmony_ci		dev_err(client->dev, "Failed to get retry counter %lu\n",
1628c2ecf20Sopenharmony_ci			BIT(data->status));
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	complete(&priv->completion);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/**
1688c2ecf20Sopenharmony_ci * rsu_max_retry_callback() - Callback from Intel service layer for getting
1698c2ecf20Sopenharmony_ci * the max retry value from the firmware
1708c2ecf20Sopenharmony_ci * @client: pointer to client
1718c2ecf20Sopenharmony_ci * @data: pointer to callback data structure
1728c2ecf20Sopenharmony_ci *
1738c2ecf20Sopenharmony_ci * Callback from Intel service layer for max retry.
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic void rsu_max_retry_callback(struct stratix10_svc_client *client,
1768c2ecf20Sopenharmony_ci				   struct stratix10_svc_cb_data *data)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = client->priv;
1798c2ecf20Sopenharmony_ci	unsigned int *max_retry = (unsigned int *)data->kaddr1;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (data->status == BIT(SVC_STATUS_OK))
1828c2ecf20Sopenharmony_ci		priv->max_retry = *max_retry;
1838c2ecf20Sopenharmony_ci	else if (data->status == BIT(SVC_STATUS_NO_SUPPORT))
1848c2ecf20Sopenharmony_ci		dev_warn(client->dev, "FW doesn't support max retry\n");
1858c2ecf20Sopenharmony_ci	else
1868c2ecf20Sopenharmony_ci		dev_err(client->dev, "Failed to get max retry %lu\n",
1878c2ecf20Sopenharmony_ci			BIT(data->status));
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	complete(&priv->completion);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/**
1938c2ecf20Sopenharmony_ci * rsu_dcmf_version_callback() - Callback from Intel service layer for getting
1948c2ecf20Sopenharmony_ci * the DCMF version
1958c2ecf20Sopenharmony_ci * @client: pointer to client
1968c2ecf20Sopenharmony_ci * @data: pointer to callback data structure
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * Callback from Intel service layer for DCMF version number
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic void rsu_dcmf_version_callback(struct stratix10_svc_client *client,
2018c2ecf20Sopenharmony_ci				      struct stratix10_svc_cb_data *data)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = client->priv;
2048c2ecf20Sopenharmony_ci	unsigned long long *value1 = (unsigned long long *)data->kaddr1;
2058c2ecf20Sopenharmony_ci	unsigned long long *value2 = (unsigned long long *)data->kaddr2;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (data->status == BIT(SVC_STATUS_OK)) {
2088c2ecf20Sopenharmony_ci		priv->dcmf_version.dcmf0 = FIELD_GET(RSU_DCMF0_MASK, *value1);
2098c2ecf20Sopenharmony_ci		priv->dcmf_version.dcmf1 = FIELD_GET(RSU_DCMF1_MASK, *value1);
2108c2ecf20Sopenharmony_ci		priv->dcmf_version.dcmf2 = FIELD_GET(RSU_DCMF2_MASK, *value2);
2118c2ecf20Sopenharmony_ci		priv->dcmf_version.dcmf3 = FIELD_GET(RSU_DCMF3_MASK, *value2);
2128c2ecf20Sopenharmony_ci	} else
2138c2ecf20Sopenharmony_ci		dev_err(client->dev, "failed to get DCMF version\n");
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	complete(&priv->completion);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**
2198c2ecf20Sopenharmony_ci * rsu_send_msg() - send a message to Intel service layer
2208c2ecf20Sopenharmony_ci * @priv: pointer to rsu private data
2218c2ecf20Sopenharmony_ci * @command: RSU status or update command
2228c2ecf20Sopenharmony_ci * @arg: the request argument, the bitstream address or notify status
2238c2ecf20Sopenharmony_ci * @callback: function pointer for the callback (status or update)
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci * Start an Intel service layer transaction to perform the SMC call that
2268c2ecf20Sopenharmony_ci * is necessary to get RSU boot log or set the address of bitstream to
2278c2ecf20Sopenharmony_ci * boot after reboot.
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci * Returns 0 on success or -ETIMEDOUT on error.
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_cistatic int rsu_send_msg(struct stratix10_rsu_priv *priv,
2328c2ecf20Sopenharmony_ci			enum stratix10_svc_command_code command,
2338c2ecf20Sopenharmony_ci			unsigned long arg,
2348c2ecf20Sopenharmony_ci			rsu_callback callback)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct stratix10_svc_client_msg msg;
2378c2ecf20Sopenharmony_ci	int ret;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
2408c2ecf20Sopenharmony_ci	reinit_completion(&priv->completion);
2418c2ecf20Sopenharmony_ci	priv->client.receive_cb = callback;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	msg.command = command;
2448c2ecf20Sopenharmony_ci	if (arg)
2458c2ecf20Sopenharmony_ci		msg.arg[0] = arg;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	ret = stratix10_svc_send(priv->chan, &msg);
2488c2ecf20Sopenharmony_ci	if (ret < 0)
2498c2ecf20Sopenharmony_ci		goto status_done;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	ret = wait_for_completion_interruptible_timeout(&priv->completion,
2528c2ecf20Sopenharmony_ci							RSU_TIMEOUT);
2538c2ecf20Sopenharmony_ci	if (!ret) {
2548c2ecf20Sopenharmony_ci		dev_err(priv->client.dev,
2558c2ecf20Sopenharmony_ci			"timeout waiting for SMC call\n");
2568c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
2578c2ecf20Sopenharmony_ci		goto status_done;
2588c2ecf20Sopenharmony_ci	} else if (ret < 0) {
2598c2ecf20Sopenharmony_ci		dev_err(priv->client.dev,
2608c2ecf20Sopenharmony_ci			"error %d waiting for SMC call\n", ret);
2618c2ecf20Sopenharmony_ci		goto status_done;
2628c2ecf20Sopenharmony_ci	} else {
2638c2ecf20Sopenharmony_ci		ret = 0;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatus_done:
2678c2ecf20Sopenharmony_ci	stratix10_svc_done(priv->chan);
2688c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * This driver exposes some optional features of the Intel Stratix 10 SoC FPGA.
2748c2ecf20Sopenharmony_ci * The sysfs interfaces exposed here are FPGA Remote System Update (RSU)
2758c2ecf20Sopenharmony_ci * related. They allow user space software to query the configuration system
2768c2ecf20Sopenharmony_ci * status and to request optional reboot behavior specific to Intel FPGAs.
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic ssize_t current_image_show(struct device *dev,
2808c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (!priv)
2858c2ecf20Sopenharmony_ci		return -ENODEV;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08lx\n", priv->status.current_image);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic ssize_t fail_image_show(struct device *dev,
2918c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (!priv)
2968c2ecf20Sopenharmony_ci		return -ENODEV;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08lx\n", priv->status.fail_image);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic ssize_t version_show(struct device *dev, struct device_attribute *attr,
3028c2ecf20Sopenharmony_ci			    char *buf)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (!priv)
3078c2ecf20Sopenharmony_ci		return -ENODEV;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->status.version);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *attr,
3138c2ecf20Sopenharmony_ci			  char *buf)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (!priv)
3188c2ecf20Sopenharmony_ci		return -ENODEV;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->status.state);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic ssize_t error_location_show(struct device *dev,
3248c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (!priv)
3298c2ecf20Sopenharmony_ci		return -ENODEV;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->status.error_location);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic ssize_t error_details_show(struct device *dev,
3358c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (!priv)
3408c2ecf20Sopenharmony_ci		return -ENODEV;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->status.error_details);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic ssize_t retry_counter_show(struct device *dev,
3468c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!priv)
3518c2ecf20Sopenharmony_ci		return -ENODEV;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->retry_counter);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic ssize_t max_retry_show(struct device *dev,
3578c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!priv)
3628c2ecf20Sopenharmony_ci		return -ENODEV;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->max_retry);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic ssize_t dcmf0_show(struct device *dev,
3688c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!priv)
3738c2ecf20Sopenharmony_ci		return -ENODEV;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf0);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic ssize_t dcmf1_show(struct device *dev,
3798c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (!priv)
3848c2ecf20Sopenharmony_ci		return -ENODEV;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf1);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic ssize_t dcmf2_show(struct device *dev,
3908c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (!priv)
3958c2ecf20Sopenharmony_ci		return -ENODEV;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf2);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic ssize_t dcmf3_show(struct device *dev,
4018c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!priv)
4068c2ecf20Sopenharmony_ci		return -ENODEV;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf3);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic ssize_t reboot_image_store(struct device *dev,
4128c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
4138c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
4168c2ecf20Sopenharmony_ci	unsigned long address;
4178c2ecf20Sopenharmony_ci	int ret;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!priv)
4208c2ecf20Sopenharmony_ci		return -ENODEV;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &address);
4238c2ecf20Sopenharmony_ci	if (ret)
4248c2ecf20Sopenharmony_ci		return ret;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_UPDATE,
4278c2ecf20Sopenharmony_ci			   address, rsu_command_callback);
4288c2ecf20Sopenharmony_ci	if (ret) {
4298c2ecf20Sopenharmony_ci		dev_err(dev, "Error, RSU update returned %i\n", ret);
4308c2ecf20Sopenharmony_ci		return ret;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return count;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic ssize_t notify_store(struct device *dev,
4378c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
4388c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
4418c2ecf20Sopenharmony_ci	unsigned long status;
4428c2ecf20Sopenharmony_ci	int ret;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (!priv)
4458c2ecf20Sopenharmony_ci		return -ENODEV;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &status);
4488c2ecf20Sopenharmony_ci	if (ret)
4498c2ecf20Sopenharmony_ci		return ret;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY,
4528c2ecf20Sopenharmony_ci			   status, rsu_command_callback);
4538c2ecf20Sopenharmony_ci	if (ret) {
4548c2ecf20Sopenharmony_ci		dev_err(dev, "Error, RSU notify returned %i\n", ret);
4558c2ecf20Sopenharmony_ci		return ret;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* to get the updated state */
4598c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_STATUS,
4608c2ecf20Sopenharmony_ci			   0, rsu_status_callback);
4618c2ecf20Sopenharmony_ci	if (ret) {
4628c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting RSU status %i\n", ret);
4638c2ecf20Sopenharmony_ci		return ret;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
4678c2ecf20Sopenharmony_ci	if (ret) {
4688c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting RSU retry %i\n", ret);
4698c2ecf20Sopenharmony_ci		return ret;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return count;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(current_image);
4768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fail_image);
4778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(state);
4788c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(version);
4798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(error_location);
4808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(error_details);
4818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(retry_counter);
4828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(max_retry);
4838c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dcmf0);
4848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dcmf1);
4858c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dcmf2);
4868c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dcmf3);
4878c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(reboot_image);
4888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(notify);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic struct attribute *rsu_attrs[] = {
4918c2ecf20Sopenharmony_ci	&dev_attr_current_image.attr,
4928c2ecf20Sopenharmony_ci	&dev_attr_fail_image.attr,
4938c2ecf20Sopenharmony_ci	&dev_attr_state.attr,
4948c2ecf20Sopenharmony_ci	&dev_attr_version.attr,
4958c2ecf20Sopenharmony_ci	&dev_attr_error_location.attr,
4968c2ecf20Sopenharmony_ci	&dev_attr_error_details.attr,
4978c2ecf20Sopenharmony_ci	&dev_attr_retry_counter.attr,
4988c2ecf20Sopenharmony_ci	&dev_attr_max_retry.attr,
4998c2ecf20Sopenharmony_ci	&dev_attr_dcmf0.attr,
5008c2ecf20Sopenharmony_ci	&dev_attr_dcmf1.attr,
5018c2ecf20Sopenharmony_ci	&dev_attr_dcmf2.attr,
5028c2ecf20Sopenharmony_ci	&dev_attr_dcmf3.attr,
5038c2ecf20Sopenharmony_ci	&dev_attr_reboot_image.attr,
5048c2ecf20Sopenharmony_ci	&dev_attr_notify.attr,
5058c2ecf20Sopenharmony_ci	NULL
5068c2ecf20Sopenharmony_ci};
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(rsu);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic int stratix10_rsu_probe(struct platform_device *pdev)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
5138c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv;
5148c2ecf20Sopenharmony_ci	int ret;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5178c2ecf20Sopenharmony_ci	if (!priv)
5188c2ecf20Sopenharmony_ci		return -ENOMEM;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	priv->client.dev = dev;
5218c2ecf20Sopenharmony_ci	priv->client.receive_cb = NULL;
5228c2ecf20Sopenharmony_ci	priv->client.priv = priv;
5238c2ecf20Sopenharmony_ci	priv->status.current_image = 0;
5248c2ecf20Sopenharmony_ci	priv->status.fail_image = 0;
5258c2ecf20Sopenharmony_ci	priv->status.error_location = 0;
5268c2ecf20Sopenharmony_ci	priv->status.error_details = 0;
5278c2ecf20Sopenharmony_ci	priv->status.version = 0;
5288c2ecf20Sopenharmony_ci	priv->status.state = 0;
5298c2ecf20Sopenharmony_ci	priv->retry_counter = INVALID_RETRY_COUNTER;
5308c2ecf20Sopenharmony_ci	priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION;
5318c2ecf20Sopenharmony_ci	priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION;
5328c2ecf20Sopenharmony_ci	priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION;
5338c2ecf20Sopenharmony_ci	priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION;
5348c2ecf20Sopenharmony_ci	priv->max_retry = INVALID_RETRY_COUNTER;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	mutex_init(&priv->lock);
5378c2ecf20Sopenharmony_ci	priv->chan = stratix10_svc_request_channel_byname(&priv->client,
5388c2ecf20Sopenharmony_ci							  SVC_CLIENT_RSU);
5398c2ecf20Sopenharmony_ci	if (IS_ERR(priv->chan)) {
5408c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't get service channel %s\n",
5418c2ecf20Sopenharmony_ci			SVC_CLIENT_RSU);
5428c2ecf20Sopenharmony_ci		return PTR_ERR(priv->chan);
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	init_completion(&priv->completion);
5468c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* get the initial state from firmware */
5498c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_STATUS,
5508c2ecf20Sopenharmony_ci			   0, rsu_status_callback);
5518c2ecf20Sopenharmony_ci	if (ret) {
5528c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting RSU status %i\n", ret);
5538c2ecf20Sopenharmony_ci		stratix10_svc_free_channel(priv->chan);
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* get DCMF version from firmware */
5578c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION,
5588c2ecf20Sopenharmony_ci			   0, rsu_dcmf_version_callback);
5598c2ecf20Sopenharmony_ci	if (ret) {
5608c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting DCMF version %i\n", ret);
5618c2ecf20Sopenharmony_ci		stratix10_svc_free_channel(priv->chan);
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
5658c2ecf20Sopenharmony_ci	if (ret) {
5668c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting RSU retry %i\n", ret);
5678c2ecf20Sopenharmony_ci		stratix10_svc_free_channel(priv->chan);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0,
5718c2ecf20Sopenharmony_ci			   rsu_max_retry_callback);
5728c2ecf20Sopenharmony_ci	if (ret) {
5738c2ecf20Sopenharmony_ci		dev_err(dev, "Error, getting RSU max retry %i\n", ret);
5748c2ecf20Sopenharmony_ci		stratix10_svc_free_channel(priv->chan);
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return ret;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic int stratix10_rsu_remove(struct platform_device *pdev)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	stratix10_svc_free_channel(priv->chan);
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic struct platform_driver stratix10_rsu_driver = {
5898c2ecf20Sopenharmony_ci	.probe = stratix10_rsu_probe,
5908c2ecf20Sopenharmony_ci	.remove = stratix10_rsu_remove,
5918c2ecf20Sopenharmony_ci	.driver = {
5928c2ecf20Sopenharmony_ci		.name = "stratix10-rsu",
5938c2ecf20Sopenharmony_ci		.dev_groups = rsu_groups,
5948c2ecf20Sopenharmony_ci	},
5958c2ecf20Sopenharmony_ci};
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cimodule_platform_driver(stratix10_rsu_driver);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
6008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Remote System Update Driver");
6018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Gong <richard.gong@intel.com>");
602