1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2023 Intel Corporation */ 3 4 #include <linux/debugfs.h> 5 #include <linux/errno.h> 6 #include <linux/export.h> 7 #include <linux/fs.h> 8 #include <linux/kernel.h> 9 #include <linux/kstrtox.h> 10 #include <linux/types.h> 11 #include "adf_cfg.h" 12 #include "adf_common_drv.h" 13 #include "adf_heartbeat.h" 14 #include "adf_heartbeat_dbgfs.h" 15 16 #define HB_OK 0 17 #define HB_ERROR -1 18 #define HB_STATUS_MAX_STRLEN 4 19 #define HB_STATS_MAX_STRLEN 16 20 21 static ssize_t adf_hb_stats_read(struct file *file, char __user *user_buffer, 22 size_t count, loff_t *ppos) 23 { 24 char buf[HB_STATS_MAX_STRLEN]; 25 unsigned int *value; 26 int len; 27 28 if (*ppos > 0) 29 return 0; 30 31 value = file->private_data; 32 len = scnprintf(buf, sizeof(buf), "%u\n", *value); 33 34 return simple_read_from_buffer(user_buffer, count, ppos, buf, len + 1); 35 } 36 37 static const struct file_operations adf_hb_stats_fops = { 38 .owner = THIS_MODULE, 39 .open = simple_open, 40 .read = adf_hb_stats_read, 41 }; 42 43 static ssize_t adf_hb_status_read(struct file *file, char __user *user_buf, 44 size_t count, loff_t *ppos) 45 { 46 enum adf_device_heartbeat_status hb_status; 47 char ret_str[HB_STATUS_MAX_STRLEN]; 48 struct adf_accel_dev *accel_dev; 49 int ret_code; 50 size_t len; 51 52 if (*ppos > 0) 53 return 0; 54 55 accel_dev = file->private_data; 56 ret_code = HB_OK; 57 58 adf_heartbeat_status(accel_dev, &hb_status); 59 60 if (hb_status != HB_DEV_ALIVE) 61 ret_code = HB_ERROR; 62 63 len = scnprintf(ret_str, sizeof(ret_str), "%d\n", ret_code); 64 65 return simple_read_from_buffer(user_buf, count, ppos, ret_str, len + 1); 66 } 67 68 static const struct file_operations adf_hb_status_fops = { 69 .owner = THIS_MODULE, 70 .open = simple_open, 71 .read = adf_hb_status_read, 72 }; 73 74 static ssize_t adf_hb_cfg_read(struct file *file, char __user *user_buf, 75 size_t count, loff_t *ppos) 76 { 77 char timer_str[ADF_CFG_MAX_VAL_LEN_IN_BYTES]; 78 struct adf_accel_dev *accel_dev; 79 unsigned int timer_ms; 80 int len; 81 82 if (*ppos > 0) 83 return 0; 84 85 accel_dev = file->private_data; 86 timer_ms = accel_dev->heartbeat->hb_timer; 87 len = scnprintf(timer_str, sizeof(timer_str), "%u\n", timer_ms); 88 89 return simple_read_from_buffer(user_buf, count, ppos, timer_str, 90 len + 1); 91 } 92 93 static ssize_t adf_hb_cfg_write(struct file *file, const char __user *user_buf, 94 size_t count, loff_t *ppos) 95 { 96 char input_str[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { }; 97 struct adf_accel_dev *accel_dev; 98 int ret, written_chars; 99 unsigned int timer_ms; 100 u32 ticks; 101 102 accel_dev = file->private_data; 103 timer_ms = ADF_CFG_HB_TIMER_DEFAULT_MS; 104 105 /* last byte left as string termination */ 106 if (count > sizeof(input_str) - 1) 107 return -EINVAL; 108 109 written_chars = simple_write_to_buffer(input_str, sizeof(input_str) - 1, 110 ppos, user_buf, count); 111 if (written_chars > 0) { 112 ret = kstrtouint(input_str, 10, &timer_ms); 113 if (ret) { 114 dev_err(&GET_DEV(accel_dev), 115 "heartbeat_cfg: Invalid value\n"); 116 return ret; 117 } 118 119 if (timer_ms < ADF_CFG_HB_TIMER_MIN_MS) { 120 dev_err(&GET_DEV(accel_dev), 121 "heartbeat_cfg: Invalid value\n"); 122 return -EINVAL; 123 } 124 125 /* 126 * On 4xxx devices adf_timer is responsible for HB updates and 127 * its period is fixed to 200ms 128 */ 129 if (accel_dev->timer) 130 timer_ms = ADF_CFG_HB_TIMER_MIN_MS; 131 132 ret = adf_heartbeat_save_cfg_param(accel_dev, timer_ms); 133 if (ret) 134 return ret; 135 136 ret = adf_heartbeat_ms_to_ticks(accel_dev, timer_ms, &ticks); 137 if (ret) 138 return ret; 139 140 ret = adf_send_admin_hb_timer(accel_dev, ticks); 141 if (ret) 142 return ret; 143 144 accel_dev->heartbeat->hb_timer = timer_ms; 145 } 146 147 return written_chars; 148 } 149 150 static const struct file_operations adf_hb_cfg_fops = { 151 .owner = THIS_MODULE, 152 .open = simple_open, 153 .read = adf_hb_cfg_read, 154 .write = adf_hb_cfg_write, 155 }; 156 157 void adf_heartbeat_dbgfs_add(struct adf_accel_dev *accel_dev) 158 { 159 struct adf_heartbeat *hb = accel_dev->heartbeat; 160 161 if (!hb) 162 return; 163 164 hb->dbgfs.base_dir = debugfs_create_dir("heartbeat", accel_dev->debugfs_dir); 165 hb->dbgfs.status = debugfs_create_file("status", 0400, hb->dbgfs.base_dir, 166 accel_dev, &adf_hb_status_fops); 167 hb->dbgfs.sent = debugfs_create_file("queries_sent", 0400, hb->dbgfs.base_dir, 168 &hb->hb_sent_counter, &adf_hb_stats_fops); 169 hb->dbgfs.failed = debugfs_create_file("queries_failed", 0400, hb->dbgfs.base_dir, 170 &hb->hb_failed_counter, &adf_hb_stats_fops); 171 hb->dbgfs.cfg = debugfs_create_file("config", 0600, hb->dbgfs.base_dir, 172 accel_dev, &adf_hb_cfg_fops); 173 } 174 EXPORT_SYMBOL_GPL(adf_heartbeat_dbgfs_add); 175 176 void adf_heartbeat_dbgfs_rm(struct adf_accel_dev *accel_dev) 177 { 178 struct adf_heartbeat *hb = accel_dev->heartbeat; 179 180 if (!hb) 181 return; 182 183 debugfs_remove(hb->dbgfs.status); 184 hb->dbgfs.status = NULL; 185 debugfs_remove(hb->dbgfs.sent); 186 hb->dbgfs.sent = NULL; 187 debugfs_remove(hb->dbgfs.failed); 188 hb->dbgfs.failed = NULL; 189 debugfs_remove(hb->dbgfs.cfg); 190 hb->dbgfs.cfg = NULL; 191 debugfs_remove(hb->dbgfs.base_dir); 192 hb->dbgfs.base_dir = NULL; 193 } 194 EXPORT_SYMBOL_GPL(adf_heartbeat_dbgfs_rm); 195