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 
adf_hb_stats_read(struct file * file,char __user * user_buffer,size_t count,loff_t * ppos)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 
adf_hb_status_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)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 
adf_hb_cfg_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)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 
adf_hb_cfg_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)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 
adf_heartbeat_dbgfs_add(struct adf_accel_dev * accel_dev)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 
adf_heartbeat_dbgfs_rm(struct adf_accel_dev * accel_dev)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