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