14526ebbcSRichard Gong // SPDX-License-Identifier: GPL-2.0 24526ebbcSRichard Gong /* 34526ebbcSRichard Gong * Copyright (C) 2018-2019, Intel Corporation 44526ebbcSRichard Gong */ 54526ebbcSRichard Gong 64526ebbcSRichard Gong #include <linux/arm-smccc.h> 74526ebbcSRichard Gong #include <linux/bitfield.h> 84526ebbcSRichard Gong #include <linux/completion.h> 94526ebbcSRichard Gong #include <linux/kobject.h> 104526ebbcSRichard Gong #include <linux/module.h> 114526ebbcSRichard Gong #include <linux/mutex.h> 124526ebbcSRichard Gong #include <linux/of.h> 134526ebbcSRichard Gong #include <linux/of_platform.h> 144526ebbcSRichard Gong #include <linux/platform_device.h> 154526ebbcSRichard Gong #include <linux/firmware/intel/stratix10-svc-client.h> 164526ebbcSRichard Gong #include <linux/string.h> 174526ebbcSRichard Gong #include <linux/sysfs.h> 184526ebbcSRichard Gong 194526ebbcSRichard Gong #define RSU_STATE_MASK GENMASK_ULL(31, 0) 204526ebbcSRichard Gong #define RSU_VERSION_MASK GENMASK_ULL(63, 32) 214526ebbcSRichard Gong #define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) 224526ebbcSRichard Gong #define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) 2375bc73fcSRichard Gong #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) 2475bc73fcSRichard Gong #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) 2575bc73fcSRichard Gong #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) 2675bc73fcSRichard Gong #define RSU_DCMF3_MASK GENMASK_ULL(63, 32) 27*4a6c8c56SKah Jing Lee #define RSU_DCMF0_STATUS_MASK GENMASK_ULL(15, 0) 28*4a6c8c56SKah Jing Lee #define RSU_DCMF1_STATUS_MASK GENMASK_ULL(31, 16) 29*4a6c8c56SKah Jing Lee #define RSU_DCMF2_STATUS_MASK GENMASK_ULL(47, 32) 30*4a6c8c56SKah Jing Lee #define RSU_DCMF3_STATUS_MASK GENMASK_ULL(63, 48) 314526ebbcSRichard Gong 324526ebbcSRichard Gong #define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS)) 334526ebbcSRichard Gong 3475bc73fcSRichard Gong #define INVALID_RETRY_COUNTER 0xFF 3575bc73fcSRichard Gong #define INVALID_DCMF_VERSION 0xFF 36*4a6c8c56SKah Jing Lee #define INVALID_DCMF_STATUS 0xFFFFFFFF 374526ebbcSRichard Gong 384526ebbcSRichard Gong typedef void (*rsu_callback)(struct stratix10_svc_client *client, 394526ebbcSRichard Gong struct stratix10_svc_cb_data *data); 404526ebbcSRichard Gong /** 414526ebbcSRichard Gong * struct stratix10_rsu_priv - rsu data structure 424526ebbcSRichard Gong * @chan: pointer to the allocated service channel 434526ebbcSRichard Gong * @client: active service client 444526ebbcSRichard Gong * @completion: state for callback completion 454526ebbcSRichard Gong * @lock: a mutex to protect callback completion state 464526ebbcSRichard Gong * @status.current_image: address of image currently running in flash 474526ebbcSRichard Gong * @status.fail_image: address of failed image in flash 4875bc73fcSRichard Gong * @status.version: the interface version number of RSU firmware 494526ebbcSRichard Gong * @status.state: the state of RSU system 504526ebbcSRichard Gong * @status.error_details: error code 514526ebbcSRichard Gong * @status.error_location: the error offset inside the image that failed 5275bc73fcSRichard Gong * @dcmf_version.dcmf0: Quartus dcmf0 version 5375bc73fcSRichard Gong * @dcmf_version.dcmf1: Quartus dcmf1 version 5475bc73fcSRichard Gong * @dcmf_version.dcmf2: Quartus dcmf2 version 5575bc73fcSRichard Gong * @dcmf_version.dcmf3: Quartus dcmf3 version 56*4a6c8c56SKah Jing Lee * @dcmf_status.dcmf0: dcmf0 status 57*4a6c8c56SKah Jing Lee * @dcmf_status.dcmf1: dcmf1 status 58*4a6c8c56SKah Jing Lee * @dcmf_status.dcmf2: dcmf2 status 59*4a6c8c56SKah Jing Lee * @dcmf_status.dcmf3: dcmf3 status 604526ebbcSRichard Gong * @retry_counter: the current image's retry counter 6175bc73fcSRichard Gong * @max_retry: the preset max retry value 624526ebbcSRichard Gong */ 634526ebbcSRichard Gong struct stratix10_rsu_priv { 644526ebbcSRichard Gong struct stratix10_svc_chan *chan; 654526ebbcSRichard Gong struct stratix10_svc_client client; 664526ebbcSRichard Gong struct completion completion; 674526ebbcSRichard Gong struct mutex lock; 684526ebbcSRichard Gong struct { 694526ebbcSRichard Gong unsigned long current_image; 704526ebbcSRichard Gong unsigned long fail_image; 714526ebbcSRichard Gong unsigned int version; 724526ebbcSRichard Gong unsigned int state; 734526ebbcSRichard Gong unsigned int error_details; 744526ebbcSRichard Gong unsigned int error_location; 754526ebbcSRichard Gong } status; 7675bc73fcSRichard Gong 7775bc73fcSRichard Gong struct { 7875bc73fcSRichard Gong unsigned int dcmf0; 7975bc73fcSRichard Gong unsigned int dcmf1; 8075bc73fcSRichard Gong unsigned int dcmf2; 8175bc73fcSRichard Gong unsigned int dcmf3; 8275bc73fcSRichard Gong } dcmf_version; 8375bc73fcSRichard Gong 84*4a6c8c56SKah Jing Lee struct { 85*4a6c8c56SKah Jing Lee unsigned int dcmf0; 86*4a6c8c56SKah Jing Lee unsigned int dcmf1; 87*4a6c8c56SKah Jing Lee unsigned int dcmf2; 88*4a6c8c56SKah Jing Lee unsigned int dcmf3; 89*4a6c8c56SKah Jing Lee } dcmf_status; 90*4a6c8c56SKah Jing Lee 914526ebbcSRichard Gong unsigned int retry_counter; 9275bc73fcSRichard Gong unsigned int max_retry; 934526ebbcSRichard Gong }; 944526ebbcSRichard Gong 954526ebbcSRichard Gong /** 964526ebbcSRichard Gong * rsu_status_callback() - Status callback from Intel Service Layer 974526ebbcSRichard Gong * @client: pointer to service client 984526ebbcSRichard Gong * @data: pointer to callback data structure 994526ebbcSRichard Gong * 1004526ebbcSRichard Gong * Callback from Intel service layer for RSU status request. Status is 1014526ebbcSRichard Gong * only updated after a system reboot, so a get updated status call is 1024526ebbcSRichard Gong * made during driver probe. 1034526ebbcSRichard Gong */ 1044526ebbcSRichard Gong static void rsu_status_callback(struct stratix10_svc_client *client, 1054526ebbcSRichard Gong struct stratix10_svc_cb_data *data) 1064526ebbcSRichard Gong { 1074526ebbcSRichard Gong struct stratix10_rsu_priv *priv = client->priv; 1084526ebbcSRichard Gong struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1; 1094526ebbcSRichard Gong 1107536ad8dSRichard Gong if (data->status == BIT(SVC_STATUS_OK)) { 1114526ebbcSRichard Gong priv->status.version = FIELD_GET(RSU_VERSION_MASK, 1124526ebbcSRichard Gong res->a2); 1134526ebbcSRichard Gong priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2); 1144526ebbcSRichard Gong priv->status.fail_image = res->a1; 1154526ebbcSRichard Gong priv->status.current_image = res->a0; 1164526ebbcSRichard Gong priv->status.error_location = 1174526ebbcSRichard Gong FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3); 1184526ebbcSRichard Gong priv->status.error_details = 1194526ebbcSRichard Gong FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3); 1204526ebbcSRichard Gong } else { 1214526ebbcSRichard Gong dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n", 1224526ebbcSRichard Gong res->a0); 1234526ebbcSRichard Gong priv->status.version = 0; 1244526ebbcSRichard Gong priv->status.state = 0; 1254526ebbcSRichard Gong priv->status.fail_image = 0; 1264526ebbcSRichard Gong priv->status.current_image = 0; 1274526ebbcSRichard Gong priv->status.error_location = 0; 1284526ebbcSRichard Gong priv->status.error_details = 0; 1294526ebbcSRichard Gong } 1304526ebbcSRichard Gong 1314526ebbcSRichard Gong complete(&priv->completion); 1324526ebbcSRichard Gong } 1334526ebbcSRichard Gong 1344526ebbcSRichard Gong /** 1354526ebbcSRichard Gong * rsu_command_callback() - Update callback from Intel Service Layer 1364526ebbcSRichard Gong * @client: pointer to client 1374526ebbcSRichard Gong * @data: pointer to callback data structure 1384526ebbcSRichard Gong * 1394526ebbcSRichard Gong * Callback from Intel service layer for RSU commands. 1404526ebbcSRichard Gong */ 1414526ebbcSRichard Gong static void rsu_command_callback(struct stratix10_svc_client *client, 1424526ebbcSRichard Gong struct stratix10_svc_cb_data *data) 1434526ebbcSRichard Gong { 1444526ebbcSRichard Gong struct stratix10_rsu_priv *priv = client->priv; 1454526ebbcSRichard Gong 1467536ad8dSRichard Gong if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) 147*4a6c8c56SKah Jing Lee dev_warn(client->dev, "Secure FW doesn't support notify\n"); 1487536ad8dSRichard Gong else if (data->status == BIT(SVC_STATUS_ERROR)) 149e9cb0497SRichard Gong dev_err(client->dev, "Failure, returned status is %lu\n", 150e9cb0497SRichard Gong BIT(data->status)); 151e9cb0497SRichard Gong 1524526ebbcSRichard Gong complete(&priv->completion); 1534526ebbcSRichard Gong } 1544526ebbcSRichard Gong 1554526ebbcSRichard Gong /** 1564526ebbcSRichard Gong * rsu_retry_callback() - Callback from Intel service layer for getting 15775bc73fcSRichard Gong * the current image's retry counter from the firmware 1584526ebbcSRichard Gong * @client: pointer to client 1594526ebbcSRichard Gong * @data: pointer to callback data structure 1604526ebbcSRichard Gong * 1614526ebbcSRichard Gong * Callback from Intel service layer for retry counter, which is used by 1624526ebbcSRichard Gong * user to know how many times the images is still allowed to reload 1634526ebbcSRichard Gong * itself before giving up and starting RSU fail-over flow. 1644526ebbcSRichard Gong */ 1654526ebbcSRichard Gong static void rsu_retry_callback(struct stratix10_svc_client *client, 1664526ebbcSRichard Gong struct stratix10_svc_cb_data *data) 1674526ebbcSRichard Gong { 1684526ebbcSRichard Gong struct stratix10_rsu_priv *priv = client->priv; 1694526ebbcSRichard Gong unsigned int *counter = (unsigned int *)data->kaddr1; 1704526ebbcSRichard Gong 1717536ad8dSRichard Gong if (data->status == BIT(SVC_STATUS_OK)) 1724526ebbcSRichard Gong priv->retry_counter = *counter; 1737536ad8dSRichard Gong else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) 174*4a6c8c56SKah Jing Lee dev_warn(client->dev, "Secure FW doesn't support retry\n"); 1754526ebbcSRichard Gong else 176e9cb0497SRichard Gong dev_err(client->dev, "Failed to get retry counter %lu\n", 177e9cb0497SRichard Gong BIT(data->status)); 1784526ebbcSRichard Gong 1794526ebbcSRichard Gong complete(&priv->completion); 1804526ebbcSRichard Gong } 1814526ebbcSRichard Gong 1824526ebbcSRichard Gong /** 18375bc73fcSRichard Gong * rsu_max_retry_callback() - Callback from Intel service layer for getting 18475bc73fcSRichard Gong * the max retry value from the firmware 18575bc73fcSRichard Gong * @client: pointer to client 18675bc73fcSRichard Gong * @data: pointer to callback data structure 18775bc73fcSRichard Gong * 18875bc73fcSRichard Gong * Callback from Intel service layer for max retry. 18975bc73fcSRichard Gong */ 19075bc73fcSRichard Gong static void rsu_max_retry_callback(struct stratix10_svc_client *client, 19175bc73fcSRichard Gong struct stratix10_svc_cb_data *data) 19275bc73fcSRichard Gong { 19375bc73fcSRichard Gong struct stratix10_rsu_priv *priv = client->priv; 19475bc73fcSRichard Gong unsigned int *max_retry = (unsigned int *)data->kaddr1; 19575bc73fcSRichard Gong 19675bc73fcSRichard Gong if (data->status == BIT(SVC_STATUS_OK)) 19775bc73fcSRichard Gong priv->max_retry = *max_retry; 19875bc73fcSRichard Gong else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) 199*4a6c8c56SKah Jing Lee dev_warn(client->dev, "Secure FW doesn't support max retry\n"); 20075bc73fcSRichard Gong else 20175bc73fcSRichard Gong dev_err(client->dev, "Failed to get max retry %lu\n", 20275bc73fcSRichard Gong BIT(data->status)); 20375bc73fcSRichard Gong 20475bc73fcSRichard Gong complete(&priv->completion); 20575bc73fcSRichard Gong } 20675bc73fcSRichard Gong 20775bc73fcSRichard Gong /** 20875bc73fcSRichard Gong * rsu_dcmf_version_callback() - Callback from Intel service layer for getting 20975bc73fcSRichard Gong * the DCMF version 21075bc73fcSRichard Gong * @client: pointer to client 21175bc73fcSRichard Gong * @data: pointer to callback data structure 21275bc73fcSRichard Gong * 21375bc73fcSRichard Gong * Callback from Intel service layer for DCMF version number 21475bc73fcSRichard Gong */ 21575bc73fcSRichard Gong static void rsu_dcmf_version_callback(struct stratix10_svc_client *client, 21675bc73fcSRichard Gong struct stratix10_svc_cb_data *data) 21775bc73fcSRichard Gong { 21875bc73fcSRichard Gong struct stratix10_rsu_priv *priv = client->priv; 21975bc73fcSRichard Gong unsigned long long *value1 = (unsigned long long *)data->kaddr1; 22075bc73fcSRichard Gong unsigned long long *value2 = (unsigned long long *)data->kaddr2; 22175bc73fcSRichard Gong 22275bc73fcSRichard Gong if (data->status == BIT(SVC_STATUS_OK)) { 22375bc73fcSRichard Gong priv->dcmf_version.dcmf0 = FIELD_GET(RSU_DCMF0_MASK, *value1); 22475bc73fcSRichard Gong priv->dcmf_version.dcmf1 = FIELD_GET(RSU_DCMF1_MASK, *value1); 22575bc73fcSRichard Gong priv->dcmf_version.dcmf2 = FIELD_GET(RSU_DCMF2_MASK, *value2); 22675bc73fcSRichard Gong priv->dcmf_version.dcmf3 = FIELD_GET(RSU_DCMF3_MASK, *value2); 22775bc73fcSRichard Gong } else 22875bc73fcSRichard Gong dev_err(client->dev, "failed to get DCMF version\n"); 22975bc73fcSRichard Gong 23075bc73fcSRichard Gong complete(&priv->completion); 23175bc73fcSRichard Gong } 23275bc73fcSRichard Gong 23375bc73fcSRichard Gong /** 234*4a6c8c56SKah Jing Lee * rsu_dcmf_status_callback() - Callback from Intel service layer for getting 235*4a6c8c56SKah Jing Lee * the DCMF status 236*4a6c8c56SKah Jing Lee * @client: pointer to client 237*4a6c8c56SKah Jing Lee * @data: pointer to callback data structure 238*4a6c8c56SKah Jing Lee * 239*4a6c8c56SKah Jing Lee * Callback from Intel service layer for DCMF status 240*4a6c8c56SKah Jing Lee */ 241*4a6c8c56SKah Jing Lee static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, 242*4a6c8c56SKah Jing Lee struct stratix10_svc_cb_data *data) 243*4a6c8c56SKah Jing Lee { 244*4a6c8c56SKah Jing Lee struct stratix10_rsu_priv *priv = client->priv; 245*4a6c8c56SKah Jing Lee unsigned long long *value = (unsigned long long *)data->kaddr1; 246*4a6c8c56SKah Jing Lee 247*4a6c8c56SKah Jing Lee if (data->status == BIT(SVC_STATUS_OK)) { 248*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf0 = FIELD_GET(RSU_DCMF0_STATUS_MASK, 249*4a6c8c56SKah Jing Lee *value); 250*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf1 = FIELD_GET(RSU_DCMF1_STATUS_MASK, 251*4a6c8c56SKah Jing Lee *value); 252*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf2 = FIELD_GET(RSU_DCMF2_STATUS_MASK, 253*4a6c8c56SKah Jing Lee *value); 254*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf3 = FIELD_GET(RSU_DCMF3_STATUS_MASK, 255*4a6c8c56SKah Jing Lee *value); 256*4a6c8c56SKah Jing Lee } else 257*4a6c8c56SKah Jing Lee dev_err(client->dev, "failed to get DCMF status\n"); 258*4a6c8c56SKah Jing Lee 259*4a6c8c56SKah Jing Lee complete(&priv->completion); 260*4a6c8c56SKah Jing Lee } 261*4a6c8c56SKah Jing Lee 262*4a6c8c56SKah Jing Lee /** 2634526ebbcSRichard Gong * rsu_send_msg() - send a message to Intel service layer 2644526ebbcSRichard Gong * @priv: pointer to rsu private data 2654526ebbcSRichard Gong * @command: RSU status or update command 2664526ebbcSRichard Gong * @arg: the request argument, the bitstream address or notify status 2674526ebbcSRichard Gong * @callback: function pointer for the callback (status or update) 2684526ebbcSRichard Gong * 2694526ebbcSRichard Gong * Start an Intel service layer transaction to perform the SMC call that 2704526ebbcSRichard Gong * is necessary to get RSU boot log or set the address of bitstream to 2714526ebbcSRichard Gong * boot after reboot. 2724526ebbcSRichard Gong * 2734526ebbcSRichard Gong * Returns 0 on success or -ETIMEDOUT on error. 2744526ebbcSRichard Gong */ 2754526ebbcSRichard Gong static int rsu_send_msg(struct stratix10_rsu_priv *priv, 2764526ebbcSRichard Gong enum stratix10_svc_command_code command, 2774526ebbcSRichard Gong unsigned long arg, 2784526ebbcSRichard Gong rsu_callback callback) 2794526ebbcSRichard Gong { 2804526ebbcSRichard Gong struct stratix10_svc_client_msg msg; 2814526ebbcSRichard Gong int ret; 2824526ebbcSRichard Gong 2834526ebbcSRichard Gong mutex_lock(&priv->lock); 2844526ebbcSRichard Gong reinit_completion(&priv->completion); 2854526ebbcSRichard Gong priv->client.receive_cb = callback; 2864526ebbcSRichard Gong 2874526ebbcSRichard Gong msg.command = command; 2884526ebbcSRichard Gong if (arg) 2894526ebbcSRichard Gong msg.arg[0] = arg; 2904526ebbcSRichard Gong 2914526ebbcSRichard Gong ret = stratix10_svc_send(priv->chan, &msg); 2924526ebbcSRichard Gong if (ret < 0) 2934526ebbcSRichard Gong goto status_done; 2944526ebbcSRichard Gong 2954526ebbcSRichard Gong ret = wait_for_completion_interruptible_timeout(&priv->completion, 2964526ebbcSRichard Gong RSU_TIMEOUT); 2974526ebbcSRichard Gong if (!ret) { 2984526ebbcSRichard Gong dev_err(priv->client.dev, 2994526ebbcSRichard Gong "timeout waiting for SMC call\n"); 3004526ebbcSRichard Gong ret = -ETIMEDOUT; 3014526ebbcSRichard Gong goto status_done; 3024526ebbcSRichard Gong } else if (ret < 0) { 3034526ebbcSRichard Gong dev_err(priv->client.dev, 3044526ebbcSRichard Gong "error %d waiting for SMC call\n", ret); 3054526ebbcSRichard Gong goto status_done; 3064526ebbcSRichard Gong } else { 3074526ebbcSRichard Gong ret = 0; 3084526ebbcSRichard Gong } 3094526ebbcSRichard Gong 3104526ebbcSRichard Gong status_done: 3114526ebbcSRichard Gong stratix10_svc_done(priv->chan); 3124526ebbcSRichard Gong mutex_unlock(&priv->lock); 3134526ebbcSRichard Gong return ret; 3144526ebbcSRichard Gong } 3154526ebbcSRichard Gong 3164526ebbcSRichard Gong /* 3174526ebbcSRichard Gong * This driver exposes some optional features of the Intel Stratix 10 SoC FPGA. 3184526ebbcSRichard Gong * The sysfs interfaces exposed here are FPGA Remote System Update (RSU) 3194526ebbcSRichard Gong * related. They allow user space software to query the configuration system 3204526ebbcSRichard Gong * status and to request optional reboot behavior specific to Intel FPGAs. 3214526ebbcSRichard Gong */ 3224526ebbcSRichard Gong 3234526ebbcSRichard Gong static ssize_t current_image_show(struct device *dev, 3244526ebbcSRichard Gong struct device_attribute *attr, char *buf) 3254526ebbcSRichard Gong { 3264526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3274526ebbcSRichard Gong 3284526ebbcSRichard Gong if (!priv) 3294526ebbcSRichard Gong return -ENODEV; 3304526ebbcSRichard Gong 3314526ebbcSRichard Gong return sprintf(buf, "0x%08lx\n", priv->status.current_image); 3324526ebbcSRichard Gong } 3334526ebbcSRichard Gong 3344526ebbcSRichard Gong static ssize_t fail_image_show(struct device *dev, 3354526ebbcSRichard Gong struct device_attribute *attr, char *buf) 3364526ebbcSRichard Gong { 3374526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3384526ebbcSRichard Gong 3394526ebbcSRichard Gong if (!priv) 3404526ebbcSRichard Gong return -ENODEV; 3414526ebbcSRichard Gong 3424526ebbcSRichard Gong return sprintf(buf, "0x%08lx\n", priv->status.fail_image); 3434526ebbcSRichard Gong } 3444526ebbcSRichard Gong 3454526ebbcSRichard Gong static ssize_t version_show(struct device *dev, struct device_attribute *attr, 3464526ebbcSRichard Gong char *buf) 3474526ebbcSRichard Gong { 3484526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3494526ebbcSRichard Gong 3504526ebbcSRichard Gong if (!priv) 3514526ebbcSRichard Gong return -ENODEV; 3524526ebbcSRichard Gong 3534526ebbcSRichard Gong return sprintf(buf, "0x%08x\n", priv->status.version); 3544526ebbcSRichard Gong } 3554526ebbcSRichard Gong 3564526ebbcSRichard Gong static ssize_t state_show(struct device *dev, struct device_attribute *attr, 3574526ebbcSRichard Gong char *buf) 3584526ebbcSRichard Gong { 3594526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3604526ebbcSRichard Gong 3614526ebbcSRichard Gong if (!priv) 3624526ebbcSRichard Gong return -ENODEV; 3634526ebbcSRichard Gong 3644526ebbcSRichard Gong return sprintf(buf, "0x%08x\n", priv->status.state); 3654526ebbcSRichard Gong } 3664526ebbcSRichard Gong 3674526ebbcSRichard Gong static ssize_t error_location_show(struct device *dev, 3684526ebbcSRichard Gong struct device_attribute *attr, char *buf) 3694526ebbcSRichard Gong { 3704526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3714526ebbcSRichard Gong 3724526ebbcSRichard Gong if (!priv) 3734526ebbcSRichard Gong return -ENODEV; 3744526ebbcSRichard Gong 3754526ebbcSRichard Gong return sprintf(buf, "0x%08x\n", priv->status.error_location); 3764526ebbcSRichard Gong } 3774526ebbcSRichard Gong 3784526ebbcSRichard Gong static ssize_t error_details_show(struct device *dev, 3794526ebbcSRichard Gong struct device_attribute *attr, char *buf) 3804526ebbcSRichard Gong { 3814526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3824526ebbcSRichard Gong 3834526ebbcSRichard Gong if (!priv) 3844526ebbcSRichard Gong return -ENODEV; 3854526ebbcSRichard Gong 3864526ebbcSRichard Gong return sprintf(buf, "0x%08x\n", priv->status.error_details); 3874526ebbcSRichard Gong } 3884526ebbcSRichard Gong 3894526ebbcSRichard Gong static ssize_t retry_counter_show(struct device *dev, 3904526ebbcSRichard Gong struct device_attribute *attr, char *buf) 3914526ebbcSRichard Gong { 3924526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 3934526ebbcSRichard Gong 3944526ebbcSRichard Gong if (!priv) 3954526ebbcSRichard Gong return -ENODEV; 3964526ebbcSRichard Gong 3974526ebbcSRichard Gong return sprintf(buf, "0x%08x\n", priv->retry_counter); 3984526ebbcSRichard Gong } 3994526ebbcSRichard Gong 40075bc73fcSRichard Gong static ssize_t max_retry_show(struct device *dev, 40175bc73fcSRichard Gong struct device_attribute *attr, char *buf) 40275bc73fcSRichard Gong { 40375bc73fcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 40475bc73fcSRichard Gong 40575bc73fcSRichard Gong if (!priv) 40675bc73fcSRichard Gong return -ENODEV; 40775bc73fcSRichard Gong 408*4a6c8c56SKah Jing Lee return scnprintf(buf, sizeof(priv->max_retry), 409*4a6c8c56SKah Jing Lee "0x%08x\n", priv->max_retry); 41075bc73fcSRichard Gong } 41175bc73fcSRichard Gong 41275bc73fcSRichard Gong static ssize_t dcmf0_show(struct device *dev, 41375bc73fcSRichard Gong struct device_attribute *attr, char *buf) 41475bc73fcSRichard Gong { 41575bc73fcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 41675bc73fcSRichard Gong 41775bc73fcSRichard Gong if (!priv) 41875bc73fcSRichard Gong return -ENODEV; 41975bc73fcSRichard Gong 42075bc73fcSRichard Gong return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf0); 42175bc73fcSRichard Gong } 42275bc73fcSRichard Gong 42375bc73fcSRichard Gong static ssize_t dcmf1_show(struct device *dev, 42475bc73fcSRichard Gong struct device_attribute *attr, char *buf) 42575bc73fcSRichard Gong { 42675bc73fcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 42775bc73fcSRichard Gong 42875bc73fcSRichard Gong if (!priv) 42975bc73fcSRichard Gong return -ENODEV; 43075bc73fcSRichard Gong 43175bc73fcSRichard Gong return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf1); 43275bc73fcSRichard Gong } 43375bc73fcSRichard Gong 43475bc73fcSRichard Gong static ssize_t dcmf2_show(struct device *dev, 43575bc73fcSRichard Gong struct device_attribute *attr, char *buf) 43675bc73fcSRichard Gong { 43775bc73fcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 43875bc73fcSRichard Gong 43975bc73fcSRichard Gong if (!priv) 44075bc73fcSRichard Gong return -ENODEV; 44175bc73fcSRichard Gong 44275bc73fcSRichard Gong return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf2); 44375bc73fcSRichard Gong } 44475bc73fcSRichard Gong 44575bc73fcSRichard Gong static ssize_t dcmf3_show(struct device *dev, 44675bc73fcSRichard Gong struct device_attribute *attr, char *buf) 44775bc73fcSRichard Gong { 44875bc73fcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 44975bc73fcSRichard Gong 45075bc73fcSRichard Gong if (!priv) 45175bc73fcSRichard Gong return -ENODEV; 45275bc73fcSRichard Gong 45375bc73fcSRichard Gong return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf3); 45475bc73fcSRichard Gong } 45575bc73fcSRichard Gong 456*4a6c8c56SKah Jing Lee static ssize_t dcmf0_status_show(struct device *dev, 457*4a6c8c56SKah Jing Lee struct device_attribute *attr, char *buf) 458*4a6c8c56SKah Jing Lee { 459*4a6c8c56SKah Jing Lee struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 460*4a6c8c56SKah Jing Lee 461*4a6c8c56SKah Jing Lee if (!priv) 462*4a6c8c56SKah Jing Lee return -ENODEV; 463*4a6c8c56SKah Jing Lee 464*4a6c8c56SKah Jing Lee if (priv->dcmf_status.dcmf0 == INVALID_DCMF_STATUS) 465*4a6c8c56SKah Jing Lee return -EIO; 466*4a6c8c56SKah Jing Lee 467*4a6c8c56SKah Jing Lee return sprintf(buf, "0x%08x\n", priv->dcmf_status.dcmf0); 468*4a6c8c56SKah Jing Lee } 469*4a6c8c56SKah Jing Lee 470*4a6c8c56SKah Jing Lee static ssize_t dcmf1_status_show(struct device *dev, 471*4a6c8c56SKah Jing Lee struct device_attribute *attr, char *buf) 472*4a6c8c56SKah Jing Lee { 473*4a6c8c56SKah Jing Lee struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 474*4a6c8c56SKah Jing Lee 475*4a6c8c56SKah Jing Lee if (!priv) 476*4a6c8c56SKah Jing Lee return -ENODEV; 477*4a6c8c56SKah Jing Lee 478*4a6c8c56SKah Jing Lee if (priv->dcmf_status.dcmf1 == INVALID_DCMF_STATUS) 479*4a6c8c56SKah Jing Lee return -EIO; 480*4a6c8c56SKah Jing Lee 481*4a6c8c56SKah Jing Lee return sprintf(buf, "0x%08x\n", priv->dcmf_status.dcmf1); 482*4a6c8c56SKah Jing Lee } 483*4a6c8c56SKah Jing Lee 484*4a6c8c56SKah Jing Lee static ssize_t dcmf2_status_show(struct device *dev, 485*4a6c8c56SKah Jing Lee struct device_attribute *attr, char *buf) 486*4a6c8c56SKah Jing Lee { 487*4a6c8c56SKah Jing Lee struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 488*4a6c8c56SKah Jing Lee 489*4a6c8c56SKah Jing Lee if (!priv) 490*4a6c8c56SKah Jing Lee return -ENODEV; 491*4a6c8c56SKah Jing Lee 492*4a6c8c56SKah Jing Lee if (priv->dcmf_status.dcmf2 == INVALID_DCMF_STATUS) 493*4a6c8c56SKah Jing Lee return -EIO; 494*4a6c8c56SKah Jing Lee 495*4a6c8c56SKah Jing Lee return sprintf(buf, "0x%08x\n", priv->dcmf_status.dcmf2); 496*4a6c8c56SKah Jing Lee } 497*4a6c8c56SKah Jing Lee 498*4a6c8c56SKah Jing Lee static ssize_t dcmf3_status_show(struct device *dev, 499*4a6c8c56SKah Jing Lee struct device_attribute *attr, char *buf) 500*4a6c8c56SKah Jing Lee { 501*4a6c8c56SKah Jing Lee struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 502*4a6c8c56SKah Jing Lee 503*4a6c8c56SKah Jing Lee if (!priv) 504*4a6c8c56SKah Jing Lee return -ENODEV; 505*4a6c8c56SKah Jing Lee 506*4a6c8c56SKah Jing Lee if (priv->dcmf_status.dcmf3 == INVALID_DCMF_STATUS) 507*4a6c8c56SKah Jing Lee return -EIO; 508*4a6c8c56SKah Jing Lee 509*4a6c8c56SKah Jing Lee return sprintf(buf, "0x%08x\n", priv->dcmf_status.dcmf3); 510*4a6c8c56SKah Jing Lee } 5114526ebbcSRichard Gong static ssize_t reboot_image_store(struct device *dev, 5124526ebbcSRichard Gong struct device_attribute *attr, 5134526ebbcSRichard Gong const char *buf, size_t count) 5144526ebbcSRichard Gong { 5154526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 5164526ebbcSRichard Gong unsigned long address; 5174526ebbcSRichard Gong int ret; 5184526ebbcSRichard Gong 51952f944eeSRichard Gong if (!priv) 5204526ebbcSRichard Gong return -ENODEV; 5214526ebbcSRichard Gong 5224526ebbcSRichard Gong ret = kstrtoul(buf, 0, &address); 5234526ebbcSRichard Gong if (ret) 5244526ebbcSRichard Gong return ret; 5254526ebbcSRichard Gong 5264526ebbcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_UPDATE, 5274526ebbcSRichard Gong address, rsu_command_callback); 5284526ebbcSRichard Gong if (ret) { 5294526ebbcSRichard Gong dev_err(dev, "Error, RSU update returned %i\n", ret); 5304526ebbcSRichard Gong return ret; 5314526ebbcSRichard Gong } 5324526ebbcSRichard Gong 5334526ebbcSRichard Gong return count; 5344526ebbcSRichard Gong } 5354526ebbcSRichard Gong 5364526ebbcSRichard Gong static ssize_t notify_store(struct device *dev, 5374526ebbcSRichard Gong struct device_attribute *attr, 5384526ebbcSRichard Gong const char *buf, size_t count) 5394526ebbcSRichard Gong { 5404526ebbcSRichard Gong struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); 5414526ebbcSRichard Gong unsigned long status; 5424526ebbcSRichard Gong int ret; 5434526ebbcSRichard Gong 54452f944eeSRichard Gong if (!priv) 5454526ebbcSRichard Gong return -ENODEV; 5464526ebbcSRichard Gong 5474526ebbcSRichard Gong ret = kstrtoul(buf, 0, &status); 5484526ebbcSRichard Gong if (ret) 5494526ebbcSRichard Gong return ret; 5504526ebbcSRichard Gong 5514526ebbcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, 5524526ebbcSRichard Gong status, rsu_command_callback); 5534526ebbcSRichard Gong if (ret) { 5544526ebbcSRichard Gong dev_err(dev, "Error, RSU notify returned %i\n", ret); 5554526ebbcSRichard Gong return ret; 5564526ebbcSRichard Gong } 5574526ebbcSRichard Gong 5584526ebbcSRichard Gong /* to get the updated state */ 5594526ebbcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 5604526ebbcSRichard Gong 0, rsu_status_callback); 5614526ebbcSRichard Gong if (ret) { 5624526ebbcSRichard Gong dev_err(dev, "Error, getting RSU status %i\n", ret); 5634526ebbcSRichard Gong return ret; 5644526ebbcSRichard Gong } 5654526ebbcSRichard Gong 566e9cb0497SRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); 5674526ebbcSRichard Gong if (ret) { 568e9cb0497SRichard Gong dev_err(dev, "Error, getting RSU retry %i\n", ret); 5694526ebbcSRichard Gong return ret; 5704526ebbcSRichard Gong } 5714526ebbcSRichard Gong 5724526ebbcSRichard Gong return count; 5734526ebbcSRichard Gong } 5744526ebbcSRichard Gong 5754526ebbcSRichard Gong static DEVICE_ATTR_RO(current_image); 5764526ebbcSRichard Gong static DEVICE_ATTR_RO(fail_image); 5774526ebbcSRichard Gong static DEVICE_ATTR_RO(state); 5784526ebbcSRichard Gong static DEVICE_ATTR_RO(version); 5794526ebbcSRichard Gong static DEVICE_ATTR_RO(error_location); 5804526ebbcSRichard Gong static DEVICE_ATTR_RO(error_details); 5814526ebbcSRichard Gong static DEVICE_ATTR_RO(retry_counter); 58275bc73fcSRichard Gong static DEVICE_ATTR_RO(max_retry); 58375bc73fcSRichard Gong static DEVICE_ATTR_RO(dcmf0); 58475bc73fcSRichard Gong static DEVICE_ATTR_RO(dcmf1); 58575bc73fcSRichard Gong static DEVICE_ATTR_RO(dcmf2); 58675bc73fcSRichard Gong static DEVICE_ATTR_RO(dcmf3); 587*4a6c8c56SKah Jing Lee static DEVICE_ATTR_RO(dcmf0_status); 588*4a6c8c56SKah Jing Lee static DEVICE_ATTR_RO(dcmf1_status); 589*4a6c8c56SKah Jing Lee static DEVICE_ATTR_RO(dcmf2_status); 590*4a6c8c56SKah Jing Lee static DEVICE_ATTR_RO(dcmf3_status); 5914526ebbcSRichard Gong static DEVICE_ATTR_WO(reboot_image); 5924526ebbcSRichard Gong static DEVICE_ATTR_WO(notify); 5934526ebbcSRichard Gong 5944526ebbcSRichard Gong static struct attribute *rsu_attrs[] = { 5954526ebbcSRichard Gong &dev_attr_current_image.attr, 5964526ebbcSRichard Gong &dev_attr_fail_image.attr, 5974526ebbcSRichard Gong &dev_attr_state.attr, 5984526ebbcSRichard Gong &dev_attr_version.attr, 5994526ebbcSRichard Gong &dev_attr_error_location.attr, 6004526ebbcSRichard Gong &dev_attr_error_details.attr, 6014526ebbcSRichard Gong &dev_attr_retry_counter.attr, 60275bc73fcSRichard Gong &dev_attr_max_retry.attr, 60375bc73fcSRichard Gong &dev_attr_dcmf0.attr, 60475bc73fcSRichard Gong &dev_attr_dcmf1.attr, 60575bc73fcSRichard Gong &dev_attr_dcmf2.attr, 60675bc73fcSRichard Gong &dev_attr_dcmf3.attr, 607*4a6c8c56SKah Jing Lee &dev_attr_dcmf0_status.attr, 608*4a6c8c56SKah Jing Lee &dev_attr_dcmf1_status.attr, 609*4a6c8c56SKah Jing Lee &dev_attr_dcmf2_status.attr, 610*4a6c8c56SKah Jing Lee &dev_attr_dcmf3_status.attr, 6114526ebbcSRichard Gong &dev_attr_reboot_image.attr, 6124526ebbcSRichard Gong &dev_attr_notify.attr, 6134526ebbcSRichard Gong NULL 6144526ebbcSRichard Gong }; 6154526ebbcSRichard Gong 6164526ebbcSRichard Gong ATTRIBUTE_GROUPS(rsu); 6174526ebbcSRichard Gong 6184526ebbcSRichard Gong static int stratix10_rsu_probe(struct platform_device *pdev) 6194526ebbcSRichard Gong { 6204526ebbcSRichard Gong struct device *dev = &pdev->dev; 6214526ebbcSRichard Gong struct stratix10_rsu_priv *priv; 6224526ebbcSRichard Gong int ret; 6234526ebbcSRichard Gong 6244526ebbcSRichard Gong priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 6254526ebbcSRichard Gong if (!priv) 6264526ebbcSRichard Gong return -ENOMEM; 6274526ebbcSRichard Gong 6284526ebbcSRichard Gong priv->client.dev = dev; 6294526ebbcSRichard Gong priv->client.receive_cb = NULL; 6304526ebbcSRichard Gong priv->client.priv = priv; 6314526ebbcSRichard Gong priv->status.current_image = 0; 6324526ebbcSRichard Gong priv->status.fail_image = 0; 6334526ebbcSRichard Gong priv->status.error_location = 0; 6344526ebbcSRichard Gong priv->status.error_details = 0; 6354526ebbcSRichard Gong priv->status.version = 0; 6364526ebbcSRichard Gong priv->status.state = 0; 6374526ebbcSRichard Gong priv->retry_counter = INVALID_RETRY_COUNTER; 63875bc73fcSRichard Gong priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION; 63975bc73fcSRichard Gong priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; 64075bc73fcSRichard Gong priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; 64175bc73fcSRichard Gong priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION; 64275bc73fcSRichard Gong priv->max_retry = INVALID_RETRY_COUNTER; 643*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf0 = INVALID_DCMF_STATUS; 644*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; 645*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; 646*4a6c8c56SKah Jing Lee priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; 6474526ebbcSRichard Gong 6484526ebbcSRichard Gong mutex_init(&priv->lock); 6494526ebbcSRichard Gong priv->chan = stratix10_svc_request_channel_byname(&priv->client, 6504526ebbcSRichard Gong SVC_CLIENT_RSU); 6514526ebbcSRichard Gong if (IS_ERR(priv->chan)) { 6524526ebbcSRichard Gong dev_err(dev, "couldn't get service channel %s\n", 6534526ebbcSRichard Gong SVC_CLIENT_RSU); 6544526ebbcSRichard Gong return PTR_ERR(priv->chan); 6554526ebbcSRichard Gong } 6564526ebbcSRichard Gong 6574526ebbcSRichard Gong init_completion(&priv->completion); 6584526ebbcSRichard Gong platform_set_drvdata(pdev, priv); 6594526ebbcSRichard Gong 6604526ebbcSRichard Gong /* get the initial state from firmware */ 6614526ebbcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 6624526ebbcSRichard Gong 0, rsu_status_callback); 6634526ebbcSRichard Gong if (ret) { 6644526ebbcSRichard Gong dev_err(dev, "Error, getting RSU status %i\n", ret); 6654526ebbcSRichard Gong stratix10_svc_free_channel(priv->chan); 6664526ebbcSRichard Gong } 6674526ebbcSRichard Gong 66875bc73fcSRichard Gong /* get DCMF version from firmware */ 66975bc73fcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, 67075bc73fcSRichard Gong 0, rsu_dcmf_version_callback); 67175bc73fcSRichard Gong if (ret) { 67275bc73fcSRichard Gong dev_err(dev, "Error, getting DCMF version %i\n", ret); 67375bc73fcSRichard Gong stratix10_svc_free_channel(priv->chan); 67475bc73fcSRichard Gong } 67575bc73fcSRichard Gong 676*4a6c8c56SKah Jing Lee ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 677*4a6c8c56SKah Jing Lee 0, rsu_dcmf_status_callback); 678*4a6c8c56SKah Jing Lee if (ret) { 679*4a6c8c56SKah Jing Lee dev_err(dev, "Error, getting DCMF status %i\n", ret); 680*4a6c8c56SKah Jing Lee stratix10_svc_free_channel(priv->chan); 681*4a6c8c56SKah Jing Lee } 682*4a6c8c56SKah Jing Lee 683e9cb0497SRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); 6844526ebbcSRichard Gong if (ret) { 685e9cb0497SRichard Gong dev_err(dev, "Error, getting RSU retry %i\n", ret); 6864526ebbcSRichard Gong stratix10_svc_free_channel(priv->chan); 6874526ebbcSRichard Gong } 6884526ebbcSRichard Gong 68975bc73fcSRichard Gong ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, 69075bc73fcSRichard Gong rsu_max_retry_callback); 69175bc73fcSRichard Gong if (ret) { 69275bc73fcSRichard Gong dev_err(dev, "Error, getting RSU max retry %i\n", ret); 69375bc73fcSRichard Gong stratix10_svc_free_channel(priv->chan); 69475bc73fcSRichard Gong } 69575bc73fcSRichard Gong 6964526ebbcSRichard Gong return ret; 6974526ebbcSRichard Gong } 6984526ebbcSRichard Gong 6994526ebbcSRichard Gong static int stratix10_rsu_remove(struct platform_device *pdev) 7004526ebbcSRichard Gong { 7014526ebbcSRichard Gong struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); 7024526ebbcSRichard Gong 7034526ebbcSRichard Gong stratix10_svc_free_channel(priv->chan); 7044526ebbcSRichard Gong return 0; 7054526ebbcSRichard Gong } 7064526ebbcSRichard Gong 7074526ebbcSRichard Gong static struct platform_driver stratix10_rsu_driver = { 7084526ebbcSRichard Gong .probe = stratix10_rsu_probe, 7094526ebbcSRichard Gong .remove = stratix10_rsu_remove, 7104526ebbcSRichard Gong .driver = { 7114526ebbcSRichard Gong .name = "stratix10-rsu", 7124526ebbcSRichard Gong .dev_groups = rsu_groups, 7134526ebbcSRichard Gong }, 7144526ebbcSRichard Gong }; 7154526ebbcSRichard Gong 7164526ebbcSRichard Gong module_platform_driver(stratix10_rsu_driver); 7174526ebbcSRichard Gong 7184526ebbcSRichard Gong MODULE_LICENSE("GPL v2"); 7194526ebbcSRichard Gong MODULE_DESCRIPTION("Intel Remote System Update Driver"); 7204526ebbcSRichard Gong MODULE_AUTHOR("Richard Gong <richard.gong@intel.com>"); 721