xref: /freebsd/sys/contrib/dev/athk/ath10k/debug.c (revision 07724ba62b4c432ea04dce9465a5ab6e2c3f5a0d)
1da8fa4e3SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2da8fa4e3SBjoern A. Zeeb /*
3da8fa4e3SBjoern A. Zeeb  * Copyright (c) 2005-2011 Atheros Communications Inc.
4da8fa4e3SBjoern A. Zeeb  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
5da8fa4e3SBjoern A. Zeeb  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
6da8fa4e3SBjoern A. Zeeb  */
7da8fa4e3SBjoern A. Zeeb 
8da8fa4e3SBjoern A. Zeeb #include <linux/module.h>
9da8fa4e3SBjoern A. Zeeb #include <linux/debugfs.h>
10da8fa4e3SBjoern A. Zeeb #include <linux/vmalloc.h>
11da8fa4e3SBjoern A. Zeeb #include <linux/crc32.h>
12da8fa4e3SBjoern A. Zeeb #include <linux/firmware.h>
13*07724ba6SBjoern A. Zeeb #include <linux/kstrtox.h>
14da8fa4e3SBjoern A. Zeeb 
15da8fa4e3SBjoern A. Zeeb #if defined(__FreeBSD__)
16da8fa4e3SBjoern A. Zeeb #ifdef CONFIG_ATH10K_DEBUG
17da8fa4e3SBjoern A. Zeeb #include <sys/sbuf.h>
18da8fa4e3SBjoern A. Zeeb #endif
19da8fa4e3SBjoern A. Zeeb #endif
20da8fa4e3SBjoern A. Zeeb 
21da8fa4e3SBjoern A. Zeeb #include "core.h"
22da8fa4e3SBjoern A. Zeeb #include "debug.h"
23da8fa4e3SBjoern A. Zeeb #include "hif.h"
24da8fa4e3SBjoern A. Zeeb #include "wmi-ops.h"
25da8fa4e3SBjoern A. Zeeb 
26da8fa4e3SBjoern A. Zeeb /* ms */
27da8fa4e3SBjoern A. Zeeb #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
28da8fa4e3SBjoern A. Zeeb 
29da8fa4e3SBjoern A. Zeeb #define ATH10K_DEBUG_CAL_DATA_LEN 12064
30da8fa4e3SBjoern A. Zeeb 
ath10k_info(struct ath10k * ar,const char * fmt,...)31da8fa4e3SBjoern A. Zeeb void ath10k_info(struct ath10k *ar, const char *fmt, ...)
32da8fa4e3SBjoern A. Zeeb {
33da8fa4e3SBjoern A. Zeeb 	struct va_format vaf = {
34da8fa4e3SBjoern A. Zeeb 		.fmt = fmt,
35da8fa4e3SBjoern A. Zeeb 	};
36da8fa4e3SBjoern A. Zeeb 	va_list args;
37da8fa4e3SBjoern A. Zeeb 
38da8fa4e3SBjoern A. Zeeb 	va_start(args, fmt);
39da8fa4e3SBjoern A. Zeeb 	vaf.va = &args;
40da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
41da8fa4e3SBjoern A. Zeeb 	dev_info(ar->dev, "%pV", &vaf);
42da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
43da8fa4e3SBjoern A. Zeeb 	{
44da8fa4e3SBjoern A. Zeeb 		char *str;
45da8fa4e3SBjoern A. Zeeb 		vasprintf(&str, M_KMALLOC, fmt, args);
46da8fa4e3SBjoern A. Zeeb 		dev_printk(KERN_DEBUG, ar->dev, "%s", str);
47da8fa4e3SBjoern A. Zeeb 		free(str, M_KMALLOC);
48da8fa4e3SBjoern A. Zeeb 	}
49da8fa4e3SBjoern A. Zeeb #endif
50da8fa4e3SBjoern A. Zeeb 	trace_ath10k_log_info(ar, &vaf);
51da8fa4e3SBjoern A. Zeeb 	va_end(args);
52da8fa4e3SBjoern A. Zeeb }
53da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_info);
54da8fa4e3SBjoern A. Zeeb 
ath10k_debug_print_hwfw_info(struct ath10k * ar)55da8fa4e3SBjoern A. Zeeb void ath10k_debug_print_hwfw_info(struct ath10k *ar)
56da8fa4e3SBjoern A. Zeeb {
57da8fa4e3SBjoern A. Zeeb 	const struct firmware *firmware;
58da8fa4e3SBjoern A. Zeeb 	char fw_features[128] = {};
59da8fa4e3SBjoern A. Zeeb 	u32 crc = 0;
60da8fa4e3SBjoern A. Zeeb 
61da8fa4e3SBjoern A. Zeeb 	ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));
62da8fa4e3SBjoern A. Zeeb 
63da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
64da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x",
65da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
66da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x\n",
67da8fa4e3SBjoern A. Zeeb #endif
68da8fa4e3SBjoern A. Zeeb 		    ar->hw_params.name,
69da8fa4e3SBjoern A. Zeeb 		    ar->target_version,
70da8fa4e3SBjoern A. Zeeb 		    ar->bus_param.chip_id,
71da8fa4e3SBjoern A. Zeeb 		    ar->id.subsystem_vendor, ar->id.subsystem_device);
72da8fa4e3SBjoern A. Zeeb 
73da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n",
74da8fa4e3SBjoern A. Zeeb 		    IS_ENABLED(CONFIG_ATH10K_DEBUG),
75da8fa4e3SBjoern A. Zeeb 		    IS_ENABLED(CONFIG_ATH10K_DEBUGFS),
76da8fa4e3SBjoern A. Zeeb 		    IS_ENABLED(CONFIG_ATH10K_TRACING),
77da8fa4e3SBjoern A. Zeeb 		    IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED),
78da8fa4e3SBjoern A. Zeeb 		    IS_ENABLED(CONFIG_NL80211_TESTMODE));
79da8fa4e3SBjoern A. Zeeb 
80da8fa4e3SBjoern A. Zeeb 	firmware = ar->normal_mode_fw.fw_file.firmware;
81da8fa4e3SBjoern A. Zeeb 	if (firmware)
82da8fa4e3SBjoern A. Zeeb 		crc = crc32_le(0, firmware->data, firmware->size);
83da8fa4e3SBjoern A. Zeeb 
84da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",
85da8fa4e3SBjoern A. Zeeb 		    ar->hw->wiphy->fw_version,
86da8fa4e3SBjoern A. Zeeb 		    ar->fw_api,
87da8fa4e3SBjoern A. Zeeb 		    fw_features,
88da8fa4e3SBjoern A. Zeeb 		    crc);
89da8fa4e3SBjoern A. Zeeb }
90da8fa4e3SBjoern A. Zeeb 
ath10k_debug_print_board_info(struct ath10k * ar)91da8fa4e3SBjoern A. Zeeb void ath10k_debug_print_board_info(struct ath10k *ar)
92da8fa4e3SBjoern A. Zeeb {
93da8fa4e3SBjoern A. Zeeb 	char boardinfo[100];
94da8fa4e3SBjoern A. Zeeb 	const struct firmware *board;
95da8fa4e3SBjoern A. Zeeb 	u32 crc;
96da8fa4e3SBjoern A. Zeeb 
97da8fa4e3SBjoern A. Zeeb 	if (ar->id.bmi_ids_valid)
98da8fa4e3SBjoern A. Zeeb 		scnprintf(boardinfo, sizeof(boardinfo), "%d:%d",
99da8fa4e3SBjoern A. Zeeb 			  ar->id.bmi_chip_id, ar->id.bmi_board_id);
100da8fa4e3SBjoern A. Zeeb 	else
101da8fa4e3SBjoern A. Zeeb 		scnprintf(boardinfo, sizeof(boardinfo), "N/A");
102da8fa4e3SBjoern A. Zeeb 
103da8fa4e3SBjoern A. Zeeb 	board = ar->normal_mode_fw.board;
104da8fa4e3SBjoern A. Zeeb 	if (!IS_ERR_OR_NULL(board))
105da8fa4e3SBjoern A. Zeeb 		crc = crc32_le(0, board->data, board->size);
106da8fa4e3SBjoern A. Zeeb 	else
107da8fa4e3SBjoern A. Zeeb 		crc = 0;
108da8fa4e3SBjoern A. Zeeb 
109da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
110da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x",
111da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
112da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x\n",
113da8fa4e3SBjoern A. Zeeb #endif
114da8fa4e3SBjoern A. Zeeb 		    ar->bd_api,
115da8fa4e3SBjoern A. Zeeb 		    boardinfo,
116da8fa4e3SBjoern A. Zeeb 		    crc);
117da8fa4e3SBjoern A. Zeeb }
118da8fa4e3SBjoern A. Zeeb 
ath10k_debug_print_boot_info(struct ath10k * ar)119da8fa4e3SBjoern A. Zeeb void ath10k_debug_print_boot_info(struct ath10k *ar)
120da8fa4e3SBjoern A. Zeeb {
121da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n",
122da8fa4e3SBjoern A. Zeeb 		    ar->htt.target_version_major,
123da8fa4e3SBjoern A. Zeeb 		    ar->htt.target_version_minor,
124da8fa4e3SBjoern A. Zeeb 		    ar->normal_mode_fw.fw_file.wmi_op_version,
125da8fa4e3SBjoern A. Zeeb 		    ar->normal_mode_fw.fw_file.htt_op_version,
126da8fa4e3SBjoern A. Zeeb 		    ath10k_cal_mode_str(ar->cal_mode),
127da8fa4e3SBjoern A. Zeeb 		    ar->max_num_stations,
128da8fa4e3SBjoern A. Zeeb 		    test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),
129da8fa4e3SBjoern A. Zeeb 		    !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags));
130da8fa4e3SBjoern A. Zeeb }
131da8fa4e3SBjoern A. Zeeb 
ath10k_print_driver_info(struct ath10k * ar)132da8fa4e3SBjoern A. Zeeb void ath10k_print_driver_info(struct ath10k *ar)
133da8fa4e3SBjoern A. Zeeb {
134da8fa4e3SBjoern A. Zeeb 	ath10k_debug_print_hwfw_info(ar);
135da8fa4e3SBjoern A. Zeeb 	ath10k_debug_print_board_info(ar);
136da8fa4e3SBjoern A. Zeeb 	ath10k_debug_print_boot_info(ar);
137da8fa4e3SBjoern A. Zeeb }
138da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_print_driver_info);
139da8fa4e3SBjoern A. Zeeb 
ath10k_err(struct ath10k * ar,const char * fmt,...)140da8fa4e3SBjoern A. Zeeb void ath10k_err(struct ath10k *ar, const char *fmt, ...)
141da8fa4e3SBjoern A. Zeeb {
142da8fa4e3SBjoern A. Zeeb 	struct va_format vaf = {
143da8fa4e3SBjoern A. Zeeb 		.fmt = fmt,
144da8fa4e3SBjoern A. Zeeb 	};
145da8fa4e3SBjoern A. Zeeb 	va_list args;
146da8fa4e3SBjoern A. Zeeb 
147da8fa4e3SBjoern A. Zeeb 	va_start(args, fmt);
148da8fa4e3SBjoern A. Zeeb 	vaf.va = &args;
149da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
150da8fa4e3SBjoern A. Zeeb 	dev_err(ar->dev, "%pV", &vaf);
151da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
152da8fa4e3SBjoern A. Zeeb 	{
153da8fa4e3SBjoern A. Zeeb 		char *str;
154da8fa4e3SBjoern A. Zeeb 		vasprintf(&str, M_KMALLOC, fmt, args);
155da8fa4e3SBjoern A. Zeeb 		dev_err(ar->dev, "%s", str);
156da8fa4e3SBjoern A. Zeeb 		free(str, M_KMALLOC);
157da8fa4e3SBjoern A. Zeeb 	}
158da8fa4e3SBjoern A. Zeeb #endif
159da8fa4e3SBjoern A. Zeeb 	trace_ath10k_log_err(ar, &vaf);
160da8fa4e3SBjoern A. Zeeb 	va_end(args);
161da8fa4e3SBjoern A. Zeeb }
162da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_err);
163da8fa4e3SBjoern A. Zeeb 
ath10k_warn(struct ath10k * ar,const char * fmt,...)164da8fa4e3SBjoern A. Zeeb void ath10k_warn(struct ath10k *ar, const char *fmt, ...)
165da8fa4e3SBjoern A. Zeeb {
166da8fa4e3SBjoern A. Zeeb 	struct va_format vaf = {
167da8fa4e3SBjoern A. Zeeb 		.fmt = fmt,
168da8fa4e3SBjoern A. Zeeb 	};
169da8fa4e3SBjoern A. Zeeb 	va_list args;
170da8fa4e3SBjoern A. Zeeb 
171da8fa4e3SBjoern A. Zeeb 	va_start(args, fmt);
172da8fa4e3SBjoern A. Zeeb 	vaf.va = &args;
173da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
174da8fa4e3SBjoern A. Zeeb 	dev_warn_ratelimited(ar->dev, "%pV", &vaf);
175da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
176da8fa4e3SBjoern A. Zeeb 	{
177da8fa4e3SBjoern A. Zeeb 		char *str;
178da8fa4e3SBjoern A. Zeeb 		vasprintf(&str, M_KMALLOC, fmt, args);
179da8fa4e3SBjoern A. Zeeb 		dev_warn_ratelimited(ar->dev, "%s", str);
180da8fa4e3SBjoern A. Zeeb 		free(str, M_KMALLOC);
181da8fa4e3SBjoern A. Zeeb 	}
182da8fa4e3SBjoern A. Zeeb #endif
183da8fa4e3SBjoern A. Zeeb 	trace_ath10k_log_warn(ar, &vaf);
184da8fa4e3SBjoern A. Zeeb 
185da8fa4e3SBjoern A. Zeeb 	va_end(args);
186da8fa4e3SBjoern A. Zeeb }
187da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_warn);
188da8fa4e3SBjoern A. Zeeb 
189da8fa4e3SBjoern A. Zeeb #ifdef CONFIG_ATH10K_DEBUGFS
190da8fa4e3SBjoern A. Zeeb 
ath10k_read_wmi_services(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)191da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_wmi_services(struct file *file,
192da8fa4e3SBjoern A. Zeeb 					char __user *user_buf,
193da8fa4e3SBjoern A. Zeeb 					size_t count, loff_t *ppos)
194da8fa4e3SBjoern A. Zeeb {
195da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
196da8fa4e3SBjoern A. Zeeb 	char *buf;
197da8fa4e3SBjoern A. Zeeb 	size_t len = 0, buf_len = 8192;
198da8fa4e3SBjoern A. Zeeb 	const char *name;
199da8fa4e3SBjoern A. Zeeb 	ssize_t ret_cnt;
200da8fa4e3SBjoern A. Zeeb 	bool enabled;
201da8fa4e3SBjoern A. Zeeb 	int i;
202da8fa4e3SBjoern A. Zeeb 
203da8fa4e3SBjoern A. Zeeb 	buf = kzalloc(buf_len, GFP_KERNEL);
204da8fa4e3SBjoern A. Zeeb 	if (!buf)
205da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
206da8fa4e3SBjoern A. Zeeb 
207da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
208da8fa4e3SBjoern A. Zeeb 
209da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
210da8fa4e3SBjoern A. Zeeb 	for (i = 0; i < WMI_SERVICE_MAX; i++) {
211da8fa4e3SBjoern A. Zeeb 		enabled = test_bit(i, ar->wmi.svc_map);
212da8fa4e3SBjoern A. Zeeb 		name = wmi_service_name(i);
213da8fa4e3SBjoern A. Zeeb 
214da8fa4e3SBjoern A. Zeeb 		if (!name) {
215da8fa4e3SBjoern A. Zeeb 			if (enabled)
216da8fa4e3SBjoern A. Zeeb 				len += scnprintf(buf + len, buf_len - len,
217da8fa4e3SBjoern A. Zeeb 						 "%-40s %s (bit %d)\n",
218da8fa4e3SBjoern A. Zeeb 						 "unknown", "enabled", i);
219da8fa4e3SBjoern A. Zeeb 
220da8fa4e3SBjoern A. Zeeb 			continue;
221da8fa4e3SBjoern A. Zeeb 		}
222da8fa4e3SBjoern A. Zeeb 
223da8fa4e3SBjoern A. Zeeb 		len += scnprintf(buf + len, buf_len - len,
224da8fa4e3SBjoern A. Zeeb 				 "%-40s %s\n",
225da8fa4e3SBjoern A. Zeeb 				 name, enabled ? "enabled" : "-");
226da8fa4e3SBjoern A. Zeeb 	}
227da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
228da8fa4e3SBjoern A. Zeeb 
229da8fa4e3SBjoern A. Zeeb 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
230da8fa4e3SBjoern A. Zeeb 
231da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
232da8fa4e3SBjoern A. Zeeb 
233da8fa4e3SBjoern A. Zeeb 	kfree(buf);
234da8fa4e3SBjoern A. Zeeb 	return ret_cnt;
235da8fa4e3SBjoern A. Zeeb }
236da8fa4e3SBjoern A. Zeeb 
237da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_wmi_services = {
238da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_wmi_services,
239da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
240da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
241da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
242da8fa4e3SBjoern A. Zeeb };
243da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_pdevs_free(struct list_head * head)244da8fa4e3SBjoern A. Zeeb static void ath10k_fw_stats_pdevs_free(struct list_head *head)
245da8fa4e3SBjoern A. Zeeb {
246da8fa4e3SBjoern A. Zeeb 	struct ath10k_fw_stats_pdev *i, *tmp;
247da8fa4e3SBjoern A. Zeeb 
248da8fa4e3SBjoern A. Zeeb 	list_for_each_entry_safe(i, tmp, head, list) {
249da8fa4e3SBjoern A. Zeeb 		list_del(&i->list);
250da8fa4e3SBjoern A. Zeeb 		kfree(i);
251da8fa4e3SBjoern A. Zeeb 	}
252da8fa4e3SBjoern A. Zeeb }
253da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_vdevs_free(struct list_head * head)254da8fa4e3SBjoern A. Zeeb static void ath10k_fw_stats_vdevs_free(struct list_head *head)
255da8fa4e3SBjoern A. Zeeb {
256da8fa4e3SBjoern A. Zeeb 	struct ath10k_fw_stats_vdev *i, *tmp;
257da8fa4e3SBjoern A. Zeeb 
258da8fa4e3SBjoern A. Zeeb 	list_for_each_entry_safe(i, tmp, head, list) {
259da8fa4e3SBjoern A. Zeeb 		list_del(&i->list);
260da8fa4e3SBjoern A. Zeeb 		kfree(i);
261da8fa4e3SBjoern A. Zeeb 	}
262da8fa4e3SBjoern A. Zeeb }
263da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_peers_free(struct list_head * head)264da8fa4e3SBjoern A. Zeeb static void ath10k_fw_stats_peers_free(struct list_head *head)
265da8fa4e3SBjoern A. Zeeb {
266da8fa4e3SBjoern A. Zeeb 	struct ath10k_fw_stats_peer *i, *tmp;
267da8fa4e3SBjoern A. Zeeb 
268da8fa4e3SBjoern A. Zeeb 	list_for_each_entry_safe(i, tmp, head, list) {
269da8fa4e3SBjoern A. Zeeb 		list_del(&i->list);
270da8fa4e3SBjoern A. Zeeb 		kfree(i);
271da8fa4e3SBjoern A. Zeeb 	}
272da8fa4e3SBjoern A. Zeeb }
273da8fa4e3SBjoern A. Zeeb 
ath10k_fw_extd_stats_peers_free(struct list_head * head)274da8fa4e3SBjoern A. Zeeb static void ath10k_fw_extd_stats_peers_free(struct list_head *head)
275da8fa4e3SBjoern A. Zeeb {
276da8fa4e3SBjoern A. Zeeb 	struct ath10k_fw_extd_stats_peer *i, *tmp;
277da8fa4e3SBjoern A. Zeeb 
278da8fa4e3SBjoern A. Zeeb 	list_for_each_entry_safe(i, tmp, head, list) {
279da8fa4e3SBjoern A. Zeeb 		list_del(&i->list);
280da8fa4e3SBjoern A. Zeeb 		kfree(i);
281da8fa4e3SBjoern A. Zeeb 	}
282da8fa4e3SBjoern A. Zeeb }
283da8fa4e3SBjoern A. Zeeb 
ath10k_debug_fw_stats_reset(struct ath10k * ar)284da8fa4e3SBjoern A. Zeeb static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
285da8fa4e3SBjoern A. Zeeb {
286da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
287da8fa4e3SBjoern A. Zeeb 	ar->debug.fw_stats_done = false;
288da8fa4e3SBjoern A. Zeeb 	ar->debug.fw_stats.extended = false;
289da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
290da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
291da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
292da8fa4e3SBjoern A. Zeeb 	ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);
293da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
294da8fa4e3SBjoern A. Zeeb }
295da8fa4e3SBjoern A. Zeeb 
ath10k_debug_fw_stats_process(struct ath10k * ar,struct sk_buff * skb)296da8fa4e3SBjoern A. Zeeb void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
297da8fa4e3SBjoern A. Zeeb {
298da8fa4e3SBjoern A. Zeeb 	struct ath10k_fw_stats stats = {};
299da8fa4e3SBjoern A. Zeeb 	bool is_start, is_started, is_end;
300da8fa4e3SBjoern A. Zeeb 	size_t num_peers;
301da8fa4e3SBjoern A. Zeeb 	size_t num_vdevs;
302da8fa4e3SBjoern A. Zeeb 	int ret;
303da8fa4e3SBjoern A. Zeeb 
304da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&stats.pdevs);
305da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&stats.vdevs);
306da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&stats.peers);
307da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&stats.peers_extd);
308da8fa4e3SBjoern A. Zeeb 
309da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
310da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
311da8fa4e3SBjoern A. Zeeb 	if (ret) {
312da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
313da8fa4e3SBjoern A. Zeeb 		goto free;
314da8fa4e3SBjoern A. Zeeb 	}
315da8fa4e3SBjoern A. Zeeb 
316da8fa4e3SBjoern A. Zeeb 	/* Stat data may exceed htc-wmi buffer limit. In such case firmware
317da8fa4e3SBjoern A. Zeeb 	 * splits the stats data and delivers it in a ping-pong fashion of
318da8fa4e3SBjoern A. Zeeb 	 * request cmd-update event.
319da8fa4e3SBjoern A. Zeeb 	 *
320da8fa4e3SBjoern A. Zeeb 	 * However there is no explicit end-of-data. Instead start-of-data is
321da8fa4e3SBjoern A. Zeeb 	 * used as an implicit one. This works as follows:
322da8fa4e3SBjoern A. Zeeb 	 *  a) discard stat update events until one with pdev stats is
323da8fa4e3SBjoern A. Zeeb 	 *     delivered - this skips session started at end of (b)
324da8fa4e3SBjoern A. Zeeb 	 *  b) consume stat update events until another one with pdev stats is
325da8fa4e3SBjoern A. Zeeb 	 *     delivered which is treated as end-of-data and is itself discarded
326da8fa4e3SBjoern A. Zeeb 	 */
327da8fa4e3SBjoern A. Zeeb 	if (ath10k_peer_stats_enabled(ar))
328da8fa4e3SBjoern A. Zeeb 		ath10k_sta_update_rx_duration(ar, &stats);
329da8fa4e3SBjoern A. Zeeb 
330da8fa4e3SBjoern A. Zeeb 	if (ar->debug.fw_stats_done) {
331da8fa4e3SBjoern A. Zeeb 		if (!ath10k_peer_stats_enabled(ar))
332da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "received unsolicited stats update event\n");
333da8fa4e3SBjoern A. Zeeb 
334da8fa4e3SBjoern A. Zeeb 		goto free;
335da8fa4e3SBjoern A. Zeeb 	}
336da8fa4e3SBjoern A. Zeeb 
337*07724ba6SBjoern A. Zeeb 	num_peers = list_count_nodes(&ar->debug.fw_stats.peers);
338*07724ba6SBjoern A. Zeeb 	num_vdevs = list_count_nodes(&ar->debug.fw_stats.vdevs);
339da8fa4e3SBjoern A. Zeeb 	is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
340da8fa4e3SBjoern A. Zeeb 		    !list_empty(&stats.pdevs));
341da8fa4e3SBjoern A. Zeeb 	is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
342da8fa4e3SBjoern A. Zeeb 		  !list_empty(&stats.pdevs));
343da8fa4e3SBjoern A. Zeeb 
344da8fa4e3SBjoern A. Zeeb 	if (is_start)
345da8fa4e3SBjoern A. Zeeb 		list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
346da8fa4e3SBjoern A. Zeeb 
347da8fa4e3SBjoern A. Zeeb 	if (is_end)
348da8fa4e3SBjoern A. Zeeb 		ar->debug.fw_stats_done = true;
349da8fa4e3SBjoern A. Zeeb 
350da8fa4e3SBjoern A. Zeeb 	if (stats.extended)
351da8fa4e3SBjoern A. Zeeb 		ar->debug.fw_stats.extended = true;
352da8fa4e3SBjoern A. Zeeb 
353da8fa4e3SBjoern A. Zeeb 	is_started = !list_empty(&ar->debug.fw_stats.pdevs);
354da8fa4e3SBjoern A. Zeeb 
355da8fa4e3SBjoern A. Zeeb 	if (is_started && !is_end) {
356da8fa4e3SBjoern A. Zeeb 		if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
357da8fa4e3SBjoern A. Zeeb 			/* Although this is unlikely impose a sane limit to
358da8fa4e3SBjoern A. Zeeb 			 * prevent firmware from DoS-ing the host.
359da8fa4e3SBjoern A. Zeeb 			 */
360da8fa4e3SBjoern A. Zeeb 			ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
361da8fa4e3SBjoern A. Zeeb 			ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);
362da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "dropping fw peer stats\n");
363da8fa4e3SBjoern A. Zeeb 			goto free;
364da8fa4e3SBjoern A. Zeeb 		}
365da8fa4e3SBjoern A. Zeeb 
366da8fa4e3SBjoern A. Zeeb 		if (num_vdevs >= BITS_PER_LONG) {
367da8fa4e3SBjoern A. Zeeb 			ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
368da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "dropping fw vdev stats\n");
369da8fa4e3SBjoern A. Zeeb 			goto free;
370da8fa4e3SBjoern A. Zeeb 		}
371da8fa4e3SBjoern A. Zeeb 
372da8fa4e3SBjoern A. Zeeb 		if (!list_empty(&stats.peers))
373da8fa4e3SBjoern A. Zeeb 			list_splice_tail_init(&stats.peers_extd,
374da8fa4e3SBjoern A. Zeeb 					      &ar->debug.fw_stats.peers_extd);
375da8fa4e3SBjoern A. Zeeb 
376da8fa4e3SBjoern A. Zeeb 		list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
377da8fa4e3SBjoern A. Zeeb 		list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
378da8fa4e3SBjoern A. Zeeb 	}
379da8fa4e3SBjoern A. Zeeb 
380da8fa4e3SBjoern A. Zeeb 	complete(&ar->debug.fw_stats_complete);
381da8fa4e3SBjoern A. Zeeb 
382da8fa4e3SBjoern A. Zeeb free:
383da8fa4e3SBjoern A. Zeeb 	/* In some cases lists have been spliced and cleared. Free up
384da8fa4e3SBjoern A. Zeeb 	 * resources if that is not the case.
385da8fa4e3SBjoern A. Zeeb 	 */
386da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_pdevs_free(&stats.pdevs);
387da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_vdevs_free(&stats.vdevs);
388da8fa4e3SBjoern A. Zeeb 	ath10k_fw_stats_peers_free(&stats.peers);
389da8fa4e3SBjoern A. Zeeb 	ath10k_fw_extd_stats_peers_free(&stats.peers_extd);
390da8fa4e3SBjoern A. Zeeb 
391da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
392da8fa4e3SBjoern A. Zeeb }
393da8fa4e3SBjoern A. Zeeb 
ath10k_debug_fw_stats_request(struct ath10k * ar)394da8fa4e3SBjoern A. Zeeb int ath10k_debug_fw_stats_request(struct ath10k *ar)
395da8fa4e3SBjoern A. Zeeb {
396da8fa4e3SBjoern A. Zeeb 	unsigned long timeout, time_left;
397da8fa4e3SBjoern A. Zeeb 	int ret;
398da8fa4e3SBjoern A. Zeeb 
399da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
400da8fa4e3SBjoern A. Zeeb 
401da8fa4e3SBjoern A. Zeeb 	timeout = jiffies + msecs_to_jiffies(1 * HZ);
402da8fa4e3SBjoern A. Zeeb 
403da8fa4e3SBjoern A. Zeeb 	ath10k_debug_fw_stats_reset(ar);
404da8fa4e3SBjoern A. Zeeb 
405da8fa4e3SBjoern A. Zeeb 	for (;;) {
406da8fa4e3SBjoern A. Zeeb 		if (time_after(jiffies, timeout))
407da8fa4e3SBjoern A. Zeeb 			return -ETIMEDOUT;
408da8fa4e3SBjoern A. Zeeb 
409da8fa4e3SBjoern A. Zeeb 		reinit_completion(&ar->debug.fw_stats_complete);
410da8fa4e3SBjoern A. Zeeb 
411da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_request_stats(ar, ar->fw_stats_req_mask);
412da8fa4e3SBjoern A. Zeeb 		if (ret) {
413da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "could not request stats (%d)\n", ret);
414da8fa4e3SBjoern A. Zeeb 			return ret;
415da8fa4e3SBjoern A. Zeeb 		}
416da8fa4e3SBjoern A. Zeeb 
417da8fa4e3SBjoern A. Zeeb 		time_left =
418da8fa4e3SBjoern A. Zeeb 		wait_for_completion_timeout(&ar->debug.fw_stats_complete,
419da8fa4e3SBjoern A. Zeeb 					    1 * HZ);
420da8fa4e3SBjoern A. Zeeb 		if (!time_left)
421da8fa4e3SBjoern A. Zeeb 			return -ETIMEDOUT;
422da8fa4e3SBjoern A. Zeeb 
423da8fa4e3SBjoern A. Zeeb 		spin_lock_bh(&ar->data_lock);
424da8fa4e3SBjoern A. Zeeb 		if (ar->debug.fw_stats_done) {
425da8fa4e3SBjoern A. Zeeb 			spin_unlock_bh(&ar->data_lock);
426da8fa4e3SBjoern A. Zeeb 			break;
427da8fa4e3SBjoern A. Zeeb 		}
428da8fa4e3SBjoern A. Zeeb 		spin_unlock_bh(&ar->data_lock);
429da8fa4e3SBjoern A. Zeeb 	}
430da8fa4e3SBjoern A. Zeeb 
431da8fa4e3SBjoern A. Zeeb 	return 0;
432da8fa4e3SBjoern A. Zeeb }
433da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_open(struct inode * inode,struct file * file)434da8fa4e3SBjoern A. Zeeb static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
435da8fa4e3SBjoern A. Zeeb {
436da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = inode->i_private;
437da8fa4e3SBjoern A. Zeeb 	void *buf = NULL;
438da8fa4e3SBjoern A. Zeeb 	int ret;
439da8fa4e3SBjoern A. Zeeb 
440da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
441da8fa4e3SBjoern A. Zeeb 
442da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
443da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
444da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
445da8fa4e3SBjoern A. Zeeb 	}
446da8fa4e3SBjoern A. Zeeb 
447da8fa4e3SBjoern A. Zeeb 	buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
448da8fa4e3SBjoern A. Zeeb 	if (!buf) {
449da8fa4e3SBjoern A. Zeeb 		ret = -ENOMEM;
450da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
451da8fa4e3SBjoern A. Zeeb 	}
452da8fa4e3SBjoern A. Zeeb 
453da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_fw_stats_request(ar);
454da8fa4e3SBjoern A. Zeeb 	if (ret) {
455da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
456da8fa4e3SBjoern A. Zeeb 		goto err_free;
457da8fa4e3SBjoern A. Zeeb 	}
458da8fa4e3SBjoern A. Zeeb 
459da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
460da8fa4e3SBjoern A. Zeeb 	if (ret) {
461da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to fill fw stats: %d\n", ret);
462da8fa4e3SBjoern A. Zeeb 		goto err_free;
463da8fa4e3SBjoern A. Zeeb 	}
464da8fa4e3SBjoern A. Zeeb 
465da8fa4e3SBjoern A. Zeeb 	file->private_data = buf;
466da8fa4e3SBjoern A. Zeeb 
467da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
468da8fa4e3SBjoern A. Zeeb 	return 0;
469da8fa4e3SBjoern A. Zeeb 
470da8fa4e3SBjoern A. Zeeb err_free:
471da8fa4e3SBjoern A. Zeeb 	vfree(buf);
472da8fa4e3SBjoern A. Zeeb 
473da8fa4e3SBjoern A. Zeeb err_unlock:
474da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
475da8fa4e3SBjoern A. Zeeb 	return ret;
476da8fa4e3SBjoern A. Zeeb }
477da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_release(struct inode * inode,struct file * file)478da8fa4e3SBjoern A. Zeeb static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
479da8fa4e3SBjoern A. Zeeb {
480da8fa4e3SBjoern A. Zeeb 	vfree(file->private_data);
481da8fa4e3SBjoern A. Zeeb 
482da8fa4e3SBjoern A. Zeeb 	return 0;
483da8fa4e3SBjoern A. Zeeb }
484da8fa4e3SBjoern A. Zeeb 
ath10k_fw_stats_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)485da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
486da8fa4e3SBjoern A. Zeeb 				    size_t count, loff_t *ppos)
487da8fa4e3SBjoern A. Zeeb {
488da8fa4e3SBjoern A. Zeeb 	const char *buf = file->private_data;
489da8fa4e3SBjoern A. Zeeb 	size_t len = strlen(buf);
490da8fa4e3SBjoern A. Zeeb 
491da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
492da8fa4e3SBjoern A. Zeeb }
493da8fa4e3SBjoern A. Zeeb 
494da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_fw_stats = {
495da8fa4e3SBjoern A. Zeeb 	.open = ath10k_fw_stats_open,
496da8fa4e3SBjoern A. Zeeb 	.release = ath10k_fw_stats_release,
497da8fa4e3SBjoern A. Zeeb 	.read = ath10k_fw_stats_read,
498da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
499da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
500da8fa4e3SBjoern A. Zeeb };
501da8fa4e3SBjoern A. Zeeb 
ath10k_debug_fw_reset_stats_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)502da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
503da8fa4e3SBjoern A. Zeeb 						char __user *user_buf,
504da8fa4e3SBjoern A. Zeeb 						size_t count, loff_t *ppos)
505da8fa4e3SBjoern A. Zeeb {
506da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
507da8fa4e3SBjoern A. Zeeb 	int ret;
508da8fa4e3SBjoern A. Zeeb 	size_t len = 0, buf_len = 500;
509da8fa4e3SBjoern A. Zeeb 	char *buf;
510da8fa4e3SBjoern A. Zeeb 
511da8fa4e3SBjoern A. Zeeb 	buf = kmalloc(buf_len, GFP_KERNEL);
512da8fa4e3SBjoern A. Zeeb 	if (!buf)
513da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
514da8fa4e3SBjoern A. Zeeb 
515da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
516da8fa4e3SBjoern A. Zeeb 
517da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
518da8fa4e3SBjoern A. Zeeb 			 "fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);
519da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
520da8fa4e3SBjoern A. Zeeb 			 "fw_warm_reset_counter\t\t%d\n",
521da8fa4e3SBjoern A. Zeeb 			 ar->stats.fw_warm_reset_counter);
522da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
523da8fa4e3SBjoern A. Zeeb 			 "fw_cold_reset_counter\t\t%d\n",
524da8fa4e3SBjoern A. Zeeb 			 ar->stats.fw_cold_reset_counter);
525da8fa4e3SBjoern A. Zeeb 
526da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
527da8fa4e3SBjoern A. Zeeb 
528da8fa4e3SBjoern A. Zeeb 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
529da8fa4e3SBjoern A. Zeeb 
530da8fa4e3SBjoern A. Zeeb 	kfree(buf);
531da8fa4e3SBjoern A. Zeeb 
532da8fa4e3SBjoern A. Zeeb 	return ret;
533da8fa4e3SBjoern A. Zeeb }
534da8fa4e3SBjoern A. Zeeb 
535da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_fw_reset_stats = {
536da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
537da8fa4e3SBjoern A. Zeeb 	.read = ath10k_debug_fw_reset_stats_read,
538da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
539da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
540da8fa4e3SBjoern A. Zeeb };
541da8fa4e3SBjoern A. Zeeb 
542da8fa4e3SBjoern A. Zeeb /* This is a clean assert crash in firmware. */
ath10k_debug_fw_assert(struct ath10k * ar)543da8fa4e3SBjoern A. Zeeb static int ath10k_debug_fw_assert(struct ath10k *ar)
544da8fa4e3SBjoern A. Zeeb {
545da8fa4e3SBjoern A. Zeeb 	struct wmi_vdev_install_key_cmd *cmd;
546da8fa4e3SBjoern A. Zeeb 	struct sk_buff *skb;
547da8fa4e3SBjoern A. Zeeb 
548da8fa4e3SBjoern A. Zeeb 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + 16);
549da8fa4e3SBjoern A. Zeeb 	if (!skb)
550da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
551da8fa4e3SBjoern A. Zeeb 
552da8fa4e3SBjoern A. Zeeb 	cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
553da8fa4e3SBjoern A. Zeeb 	memset(cmd, 0, sizeof(*cmd));
554da8fa4e3SBjoern A. Zeeb 
555da8fa4e3SBjoern A. Zeeb 	/* big enough number so that firmware asserts */
556da8fa4e3SBjoern A. Zeeb 	cmd->vdev_id = __cpu_to_le32(0x7ffe);
557da8fa4e3SBjoern A. Zeeb 
558da8fa4e3SBjoern A. Zeeb 	return ath10k_wmi_cmd_send(ar, skb,
559da8fa4e3SBjoern A. Zeeb 				   ar->wmi.cmd->vdev_install_key_cmdid);
560da8fa4e3SBjoern A. Zeeb }
561da8fa4e3SBjoern A. Zeeb 
ath10k_read_simulate_fw_crash(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)562da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
563da8fa4e3SBjoern A. Zeeb 					     char __user *user_buf,
564da8fa4e3SBjoern A. Zeeb 					     size_t count, loff_t *ppos)
565da8fa4e3SBjoern A. Zeeb {
566da8fa4e3SBjoern A. Zeeb 	const char buf[] =
567da8fa4e3SBjoern A. Zeeb 		"To simulate firmware crash write one of the keywords to this file:\n"
568da8fa4e3SBjoern A. Zeeb 		"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"
569da8fa4e3SBjoern A. Zeeb 		"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"
570da8fa4e3SBjoern A. Zeeb 		"`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"
571da8fa4e3SBjoern A. Zeeb 		"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
572da8fa4e3SBjoern A. Zeeb 
573da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
574da8fa4e3SBjoern A. Zeeb }
575da8fa4e3SBjoern A. Zeeb 
576da8fa4e3SBjoern A. Zeeb /* Simulate firmware crash:
577da8fa4e3SBjoern A. Zeeb  * 'soft': Call wmi command causing firmware hang. This firmware hang is
578da8fa4e3SBjoern A. Zeeb  * recoverable by warm firmware reset.
579da8fa4e3SBjoern A. Zeeb  * 'hard': Force firmware crash by setting any vdev parameter for not allowed
580da8fa4e3SBjoern A. Zeeb  * vdev id. This is hard firmware crash because it is recoverable only by cold
581da8fa4e3SBjoern A. Zeeb  * firmware reset.
582da8fa4e3SBjoern A. Zeeb  */
ath10k_write_simulate_fw_crash(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)583da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
584da8fa4e3SBjoern A. Zeeb 					      const char __user *user_buf,
585da8fa4e3SBjoern A. Zeeb 					      size_t count, loff_t *ppos)
586da8fa4e3SBjoern A. Zeeb {
587da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
588da8fa4e3SBjoern A. Zeeb 	char buf[32] = {0};
589da8fa4e3SBjoern A. Zeeb 	ssize_t rc;
590da8fa4e3SBjoern A. Zeeb 	int ret;
591da8fa4e3SBjoern A. Zeeb 
592da8fa4e3SBjoern A. Zeeb 	/* filter partial writes and invalid commands */
593da8fa4e3SBjoern A. Zeeb 	if (*ppos != 0 || count >= sizeof(buf) || count == 0)
594da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
595da8fa4e3SBjoern A. Zeeb 
596da8fa4e3SBjoern A. Zeeb 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
597da8fa4e3SBjoern A. Zeeb 	if (rc < 0)
598da8fa4e3SBjoern A. Zeeb 		return rc;
599da8fa4e3SBjoern A. Zeeb 
600da8fa4e3SBjoern A. Zeeb 	/* drop the possible '\n' from the end */
601da8fa4e3SBjoern A. Zeeb 	if (buf[*ppos - 1] == '\n')
602da8fa4e3SBjoern A. Zeeb 		buf[*ppos - 1] = '\0';
603da8fa4e3SBjoern A. Zeeb 
604da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
605da8fa4e3SBjoern A. Zeeb 
606da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
607da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_RESTARTED) {
608da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
609da8fa4e3SBjoern A. Zeeb 		goto exit;
610da8fa4e3SBjoern A. Zeeb 	}
611da8fa4e3SBjoern A. Zeeb 
612da8fa4e3SBjoern A. Zeeb 	if (!strcmp(buf, "soft")) {
613da8fa4e3SBjoern A. Zeeb 		ath10k_info(ar, "simulating soft firmware crash\n");
614da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
615da8fa4e3SBjoern A. Zeeb 	} else if (!strcmp(buf, "hard")) {
616da8fa4e3SBjoern A. Zeeb 		ath10k_info(ar, "simulating hard firmware crash\n");
617da8fa4e3SBjoern A. Zeeb 		/* 0x7fff is vdev id, and it is always out of range for all
618da8fa4e3SBjoern A. Zeeb 		 * firmware variants in order to force a firmware crash.
619da8fa4e3SBjoern A. Zeeb 		 */
620da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_vdev_set_param(ar, 0x7fff,
621da8fa4e3SBjoern A. Zeeb 						ar->wmi.vdev_param->rts_threshold,
622da8fa4e3SBjoern A. Zeeb 						0);
623da8fa4e3SBjoern A. Zeeb 	} else if (!strcmp(buf, "assert")) {
624da8fa4e3SBjoern A. Zeeb 		ath10k_info(ar, "simulating firmware assert crash\n");
625da8fa4e3SBjoern A. Zeeb 		ret = ath10k_debug_fw_assert(ar);
626da8fa4e3SBjoern A. Zeeb 	} else if (!strcmp(buf, "hw-restart")) {
627da8fa4e3SBjoern A. Zeeb 		ath10k_info(ar, "user requested hw restart\n");
628da8fa4e3SBjoern A. Zeeb 		ath10k_core_start_recovery(ar);
629da8fa4e3SBjoern A. Zeeb 		ret = 0;
630da8fa4e3SBjoern A. Zeeb 	} else {
631da8fa4e3SBjoern A. Zeeb 		ret = -EINVAL;
632da8fa4e3SBjoern A. Zeeb 		goto exit;
633da8fa4e3SBjoern A. Zeeb 	}
634da8fa4e3SBjoern A. Zeeb 
635da8fa4e3SBjoern A. Zeeb 	if (ret) {
636da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret);
637da8fa4e3SBjoern A. Zeeb 		goto exit;
638da8fa4e3SBjoern A. Zeeb 	}
639da8fa4e3SBjoern A. Zeeb 
640da8fa4e3SBjoern A. Zeeb 	ret = count;
641da8fa4e3SBjoern A. Zeeb 
642da8fa4e3SBjoern A. Zeeb exit:
643da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
644da8fa4e3SBjoern A. Zeeb 	return ret;
645da8fa4e3SBjoern A. Zeeb }
646da8fa4e3SBjoern A. Zeeb 
647da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_simulate_fw_crash = {
648da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_simulate_fw_crash,
649da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_simulate_fw_crash,
650da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
651da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
652da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
653da8fa4e3SBjoern A. Zeeb };
654da8fa4e3SBjoern A. Zeeb 
ath10k_read_chip_id(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)655da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,
656da8fa4e3SBjoern A. Zeeb 				   size_t count, loff_t *ppos)
657da8fa4e3SBjoern A. Zeeb {
658da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
659da8fa4e3SBjoern A. Zeeb 	size_t len;
660da8fa4e3SBjoern A. Zeeb 	char buf[50];
661da8fa4e3SBjoern A. Zeeb 
662da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->bus_param.chip_id);
663da8fa4e3SBjoern A. Zeeb 
664da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
665da8fa4e3SBjoern A. Zeeb }
666da8fa4e3SBjoern A. Zeeb 
667da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_chip_id = {
668da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_chip_id,
669da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
670da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
671da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
672da8fa4e3SBjoern A. Zeeb };
673da8fa4e3SBjoern A. Zeeb 
ath10k_reg_addr_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)674da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_reg_addr_read(struct file *file,
675da8fa4e3SBjoern A. Zeeb 				    char __user *user_buf,
676da8fa4e3SBjoern A. Zeeb 				    size_t count, loff_t *ppos)
677da8fa4e3SBjoern A. Zeeb {
678da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
679da8fa4e3SBjoern A. Zeeb 	u8 buf[32];
680da8fa4e3SBjoern A. Zeeb 	size_t len = 0;
681da8fa4e3SBjoern A. Zeeb 	u32 reg_addr;
682da8fa4e3SBjoern A. Zeeb 
683da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
684da8fa4e3SBjoern A. Zeeb 	reg_addr = ar->debug.reg_addr;
685da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
686da8fa4e3SBjoern A. Zeeb 
687da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
688da8fa4e3SBjoern A. Zeeb 
689da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
690da8fa4e3SBjoern A. Zeeb }
691da8fa4e3SBjoern A. Zeeb 
ath10k_reg_addr_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)692da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_reg_addr_write(struct file *file,
693da8fa4e3SBjoern A. Zeeb 				     const char __user *user_buf,
694da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
695da8fa4e3SBjoern A. Zeeb {
696da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
697da8fa4e3SBjoern A. Zeeb 	u32 reg_addr;
698da8fa4e3SBjoern A. Zeeb 	int ret;
699da8fa4e3SBjoern A. Zeeb 
700da8fa4e3SBjoern A. Zeeb 	ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
701da8fa4e3SBjoern A. Zeeb 	if (ret)
702da8fa4e3SBjoern A. Zeeb 		return ret;
703da8fa4e3SBjoern A. Zeeb 
704da8fa4e3SBjoern A. Zeeb 	if (!IS_ALIGNED(reg_addr, 4))
705da8fa4e3SBjoern A. Zeeb 		return -EFAULT;
706da8fa4e3SBjoern A. Zeeb 
707da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
708da8fa4e3SBjoern A. Zeeb 	ar->debug.reg_addr = reg_addr;
709da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
710da8fa4e3SBjoern A. Zeeb 
711da8fa4e3SBjoern A. Zeeb 	return count;
712da8fa4e3SBjoern A. Zeeb }
713da8fa4e3SBjoern A. Zeeb 
714da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_reg_addr = {
715da8fa4e3SBjoern A. Zeeb 	.read = ath10k_reg_addr_read,
716da8fa4e3SBjoern A. Zeeb 	.write = ath10k_reg_addr_write,
717da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
718da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
719da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
720da8fa4e3SBjoern A. Zeeb };
721da8fa4e3SBjoern A. Zeeb 
ath10k_reg_value_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)722da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_reg_value_read(struct file *file,
723da8fa4e3SBjoern A. Zeeb 				     char __user *user_buf,
724da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
725da8fa4e3SBjoern A. Zeeb {
726da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
727da8fa4e3SBjoern A. Zeeb 	u8 buf[48];
728da8fa4e3SBjoern A. Zeeb 	size_t len;
729da8fa4e3SBjoern A. Zeeb 	u32 reg_addr, reg_val;
730da8fa4e3SBjoern A. Zeeb 	int ret;
731da8fa4e3SBjoern A. Zeeb 
732da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
733da8fa4e3SBjoern A. Zeeb 
734da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
735da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_UTF) {
736da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
737da8fa4e3SBjoern A. Zeeb 		goto exit;
738da8fa4e3SBjoern A. Zeeb 	}
739da8fa4e3SBjoern A. Zeeb 
740da8fa4e3SBjoern A. Zeeb 	reg_addr = ar->debug.reg_addr;
741da8fa4e3SBjoern A. Zeeb 
742da8fa4e3SBjoern A. Zeeb 	reg_val = ath10k_hif_read32(ar, reg_addr);
743da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
744da8fa4e3SBjoern A. Zeeb 
745da8fa4e3SBjoern A. Zeeb 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
746da8fa4e3SBjoern A. Zeeb 
747da8fa4e3SBjoern A. Zeeb exit:
748da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
749da8fa4e3SBjoern A. Zeeb 
750da8fa4e3SBjoern A. Zeeb 	return ret;
751da8fa4e3SBjoern A. Zeeb }
752da8fa4e3SBjoern A. Zeeb 
ath10k_reg_value_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)753da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_reg_value_write(struct file *file,
754da8fa4e3SBjoern A. Zeeb 				      const char __user *user_buf,
755da8fa4e3SBjoern A. Zeeb 				      size_t count, loff_t *ppos)
756da8fa4e3SBjoern A. Zeeb {
757da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
758da8fa4e3SBjoern A. Zeeb 	u32 reg_addr, reg_val;
759da8fa4e3SBjoern A. Zeeb 	int ret;
760da8fa4e3SBjoern A. Zeeb 
761da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
762da8fa4e3SBjoern A. Zeeb 
763da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
764da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_UTF) {
765da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
766da8fa4e3SBjoern A. Zeeb 		goto exit;
767da8fa4e3SBjoern A. Zeeb 	}
768da8fa4e3SBjoern A. Zeeb 
769da8fa4e3SBjoern A. Zeeb 	reg_addr = ar->debug.reg_addr;
770da8fa4e3SBjoern A. Zeeb 
771da8fa4e3SBjoern A. Zeeb 	ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
772da8fa4e3SBjoern A. Zeeb 	if (ret)
773da8fa4e3SBjoern A. Zeeb 		goto exit;
774da8fa4e3SBjoern A. Zeeb 
775da8fa4e3SBjoern A. Zeeb 	ath10k_hif_write32(ar, reg_addr, reg_val);
776da8fa4e3SBjoern A. Zeeb 
777da8fa4e3SBjoern A. Zeeb 	ret = count;
778da8fa4e3SBjoern A. Zeeb 
779da8fa4e3SBjoern A. Zeeb exit:
780da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
781da8fa4e3SBjoern A. Zeeb 
782da8fa4e3SBjoern A. Zeeb 	return ret;
783da8fa4e3SBjoern A. Zeeb }
784da8fa4e3SBjoern A. Zeeb 
785da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_reg_value = {
786da8fa4e3SBjoern A. Zeeb 	.read = ath10k_reg_value_read,
787da8fa4e3SBjoern A. Zeeb 	.write = ath10k_reg_value_write,
788da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
789da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
790da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
791da8fa4e3SBjoern A. Zeeb };
792da8fa4e3SBjoern A. Zeeb 
ath10k_mem_value_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)793da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_mem_value_read(struct file *file,
794da8fa4e3SBjoern A. Zeeb 				     char __user *user_buf,
795da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
796da8fa4e3SBjoern A. Zeeb {
797da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
798da8fa4e3SBjoern A. Zeeb 	u8 *buf;
799da8fa4e3SBjoern A. Zeeb 	int ret;
800da8fa4e3SBjoern A. Zeeb 
801da8fa4e3SBjoern A. Zeeb 	if (*ppos < 0)
802da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
803da8fa4e3SBjoern A. Zeeb 
804da8fa4e3SBjoern A. Zeeb 	if (!count)
805da8fa4e3SBjoern A. Zeeb 		return 0;
806da8fa4e3SBjoern A. Zeeb 
807da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
808da8fa4e3SBjoern A. Zeeb 
809da8fa4e3SBjoern A. Zeeb 	buf = vmalloc(count);
810da8fa4e3SBjoern A. Zeeb 	if (!buf) {
811da8fa4e3SBjoern A. Zeeb 		ret = -ENOMEM;
812da8fa4e3SBjoern A. Zeeb 		goto exit;
813da8fa4e3SBjoern A. Zeeb 	}
814da8fa4e3SBjoern A. Zeeb 
815da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
816da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_UTF) {
817da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
818da8fa4e3SBjoern A. Zeeb 		goto exit;
819da8fa4e3SBjoern A. Zeeb 	}
820da8fa4e3SBjoern A. Zeeb 
821da8fa4e3SBjoern A. Zeeb 	ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
822da8fa4e3SBjoern A. Zeeb 	if (ret) {
823da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to read address 0x%08x via diagnose window from debugfs: %d\n",
824da8fa4e3SBjoern A. Zeeb 			    (u32)(*ppos), ret);
825da8fa4e3SBjoern A. Zeeb 		goto exit;
826da8fa4e3SBjoern A. Zeeb 	}
827da8fa4e3SBjoern A. Zeeb 
828da8fa4e3SBjoern A. Zeeb 	ret = copy_to_user(user_buf, buf, count);
829da8fa4e3SBjoern A. Zeeb 	if (ret) {
830da8fa4e3SBjoern A. Zeeb 		ret = -EFAULT;
831da8fa4e3SBjoern A. Zeeb 		goto exit;
832da8fa4e3SBjoern A. Zeeb 	}
833da8fa4e3SBjoern A. Zeeb 
834da8fa4e3SBjoern A. Zeeb 	count -= ret;
835da8fa4e3SBjoern A. Zeeb 	*ppos += count;
836da8fa4e3SBjoern A. Zeeb 	ret = count;
837da8fa4e3SBjoern A. Zeeb 
838da8fa4e3SBjoern A. Zeeb exit:
839da8fa4e3SBjoern A. Zeeb 	vfree(buf);
840da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
841da8fa4e3SBjoern A. Zeeb 
842da8fa4e3SBjoern A. Zeeb 	return ret;
843da8fa4e3SBjoern A. Zeeb }
844da8fa4e3SBjoern A. Zeeb 
ath10k_mem_value_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)845da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_mem_value_write(struct file *file,
846da8fa4e3SBjoern A. Zeeb 				      const char __user *user_buf,
847da8fa4e3SBjoern A. Zeeb 				      size_t count, loff_t *ppos)
848da8fa4e3SBjoern A. Zeeb {
849da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
850da8fa4e3SBjoern A. Zeeb 	u8 *buf;
851da8fa4e3SBjoern A. Zeeb 	int ret;
852da8fa4e3SBjoern A. Zeeb 
853da8fa4e3SBjoern A. Zeeb 	if (*ppos < 0)
854da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
855da8fa4e3SBjoern A. Zeeb 
856da8fa4e3SBjoern A. Zeeb 	if (!count)
857da8fa4e3SBjoern A. Zeeb 		return 0;
858da8fa4e3SBjoern A. Zeeb 
859da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
860da8fa4e3SBjoern A. Zeeb 
861da8fa4e3SBjoern A. Zeeb 	buf = vmalloc(count);
862da8fa4e3SBjoern A. Zeeb 	if (!buf) {
863da8fa4e3SBjoern A. Zeeb 		ret = -ENOMEM;
864da8fa4e3SBjoern A. Zeeb 		goto exit;
865da8fa4e3SBjoern A. Zeeb 	}
866da8fa4e3SBjoern A. Zeeb 
867da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
868da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_UTF) {
869da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
870da8fa4e3SBjoern A. Zeeb 		goto exit;
871da8fa4e3SBjoern A. Zeeb 	}
872da8fa4e3SBjoern A. Zeeb 
873da8fa4e3SBjoern A. Zeeb 	ret = copy_from_user(buf, user_buf, count);
874da8fa4e3SBjoern A. Zeeb 	if (ret) {
875da8fa4e3SBjoern A. Zeeb 		ret = -EFAULT;
876da8fa4e3SBjoern A. Zeeb 		goto exit;
877da8fa4e3SBjoern A. Zeeb 	}
878da8fa4e3SBjoern A. Zeeb 
879da8fa4e3SBjoern A. Zeeb 	ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
880da8fa4e3SBjoern A. Zeeb 	if (ret) {
881da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
882da8fa4e3SBjoern A. Zeeb 			    (u32)(*ppos), ret);
883da8fa4e3SBjoern A. Zeeb 		goto exit;
884da8fa4e3SBjoern A. Zeeb 	}
885da8fa4e3SBjoern A. Zeeb 
886da8fa4e3SBjoern A. Zeeb 	*ppos += count;
887da8fa4e3SBjoern A. Zeeb 	ret = count;
888da8fa4e3SBjoern A. Zeeb 
889da8fa4e3SBjoern A. Zeeb exit:
890da8fa4e3SBjoern A. Zeeb 	vfree(buf);
891da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
892da8fa4e3SBjoern A. Zeeb 
893da8fa4e3SBjoern A. Zeeb 	return ret;
894da8fa4e3SBjoern A. Zeeb }
895da8fa4e3SBjoern A. Zeeb 
896da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_mem_value = {
897da8fa4e3SBjoern A. Zeeb 	.read = ath10k_mem_value_read,
898da8fa4e3SBjoern A. Zeeb 	.write = ath10k_mem_value_write,
899da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
900da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
901da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
902da8fa4e3SBjoern A. Zeeb };
903da8fa4e3SBjoern A. Zeeb 
ath10k_debug_htt_stats_req(struct ath10k * ar)904da8fa4e3SBjoern A. Zeeb static int ath10k_debug_htt_stats_req(struct ath10k *ar)
905da8fa4e3SBjoern A. Zeeb {
906da8fa4e3SBjoern A. Zeeb 	u64 cookie;
907da8fa4e3SBjoern A. Zeeb 	int ret;
908da8fa4e3SBjoern A. Zeeb 
909da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
910da8fa4e3SBjoern A. Zeeb 
911da8fa4e3SBjoern A. Zeeb 	if (ar->debug.htt_stats_mask == 0)
912da8fa4e3SBjoern A. Zeeb 		/* htt stats are disabled */
913da8fa4e3SBjoern A. Zeeb 		return 0;
914da8fa4e3SBjoern A. Zeeb 
915da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON)
916da8fa4e3SBjoern A. Zeeb 		return 0;
917da8fa4e3SBjoern A. Zeeb 
918da8fa4e3SBjoern A. Zeeb 	cookie = get_jiffies_64();
919da8fa4e3SBjoern A. Zeeb 
920da8fa4e3SBjoern A. Zeeb 	ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
921da8fa4e3SBjoern A. Zeeb 				       ar->debug.reset_htt_stats, cookie);
922da8fa4e3SBjoern A. Zeeb 	if (ret) {
923da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to send htt stats request: %d\n", ret);
924da8fa4e3SBjoern A. Zeeb 		return ret;
925da8fa4e3SBjoern A. Zeeb 	}
926da8fa4e3SBjoern A. Zeeb 
927da8fa4e3SBjoern A. Zeeb 	queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,
928da8fa4e3SBjoern A. Zeeb 			   msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));
929da8fa4e3SBjoern A. Zeeb 
930da8fa4e3SBjoern A. Zeeb 	return 0;
931da8fa4e3SBjoern A. Zeeb }
932da8fa4e3SBjoern A. Zeeb 
ath10k_debug_htt_stats_dwork(struct work_struct * work)933da8fa4e3SBjoern A. Zeeb static void ath10k_debug_htt_stats_dwork(struct work_struct *work)
934da8fa4e3SBjoern A. Zeeb {
935da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = container_of(work, struct ath10k,
936da8fa4e3SBjoern A. Zeeb 					 debug.htt_stats_dwork.work);
937da8fa4e3SBjoern A. Zeeb 
938da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
939da8fa4e3SBjoern A. Zeeb 
940da8fa4e3SBjoern A. Zeeb 	ath10k_debug_htt_stats_req(ar);
941da8fa4e3SBjoern A. Zeeb 
942da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
943da8fa4e3SBjoern A. Zeeb }
944da8fa4e3SBjoern A. Zeeb 
ath10k_read_htt_stats_mask(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)945da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_htt_stats_mask(struct file *file,
946da8fa4e3SBjoern A. Zeeb 					  char __user *user_buf,
947da8fa4e3SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
948da8fa4e3SBjoern A. Zeeb {
949da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
950da8fa4e3SBjoern A. Zeeb 	char buf[32];
951da8fa4e3SBjoern A. Zeeb 	size_t len;
952da8fa4e3SBjoern A. Zeeb 
953da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);
954da8fa4e3SBjoern A. Zeeb 
955da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
956da8fa4e3SBjoern A. Zeeb }
957da8fa4e3SBjoern A. Zeeb 
ath10k_write_htt_stats_mask(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)958da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_htt_stats_mask(struct file *file,
959da8fa4e3SBjoern A. Zeeb 					   const char __user *user_buf,
960da8fa4e3SBjoern A. Zeeb 					   size_t count, loff_t *ppos)
961da8fa4e3SBjoern A. Zeeb {
962da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
963da8fa4e3SBjoern A. Zeeb 	unsigned long mask;
964da8fa4e3SBjoern A. Zeeb 	int ret;
965da8fa4e3SBjoern A. Zeeb 
966da8fa4e3SBjoern A. Zeeb 	ret = kstrtoul_from_user(user_buf, count, 0, &mask);
967da8fa4e3SBjoern A. Zeeb 	if (ret)
968da8fa4e3SBjoern A. Zeeb 		return ret;
969da8fa4e3SBjoern A. Zeeb 
970da8fa4e3SBjoern A. Zeeb 	/* max 17 bit masks (for now) */
971da8fa4e3SBjoern A. Zeeb 	if (mask > HTT_STATS_BIT_MASK)
972da8fa4e3SBjoern A. Zeeb 		return -E2BIG;
973da8fa4e3SBjoern A. Zeeb 
974da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
975da8fa4e3SBjoern A. Zeeb 
976da8fa4e3SBjoern A. Zeeb 	ar->debug.htt_stats_mask = mask;
977da8fa4e3SBjoern A. Zeeb 
978da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_htt_stats_req(ar);
979da8fa4e3SBjoern A. Zeeb 	if (ret)
980da8fa4e3SBjoern A. Zeeb 		goto out;
981da8fa4e3SBjoern A. Zeeb 
982da8fa4e3SBjoern A. Zeeb 	ret = count;
983da8fa4e3SBjoern A. Zeeb 
984da8fa4e3SBjoern A. Zeeb out:
985da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
986da8fa4e3SBjoern A. Zeeb 
987da8fa4e3SBjoern A. Zeeb 	return ret;
988da8fa4e3SBjoern A. Zeeb }
989da8fa4e3SBjoern A. Zeeb 
990da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_htt_stats_mask = {
991da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_htt_stats_mask,
992da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_htt_stats_mask,
993da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
994da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
995da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
996da8fa4e3SBjoern A. Zeeb };
997da8fa4e3SBjoern A. Zeeb 
ath10k_read_htt_max_amsdu_ampdu(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)998da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
999da8fa4e3SBjoern A. Zeeb 					       char __user *user_buf,
1000da8fa4e3SBjoern A. Zeeb 					       size_t count, loff_t *ppos)
1001da8fa4e3SBjoern A. Zeeb {
1002da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1003da8fa4e3SBjoern A. Zeeb 	char buf[64];
1004da8fa4e3SBjoern A. Zeeb 	u8 amsdu, ampdu;
1005da8fa4e3SBjoern A. Zeeb 	size_t len;
1006da8fa4e3SBjoern A. Zeeb 
1007da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1008da8fa4e3SBjoern A. Zeeb 
1009da8fa4e3SBjoern A. Zeeb 	amsdu = ar->htt.max_num_amsdu;
1010da8fa4e3SBjoern A. Zeeb 	ampdu = ar->htt.max_num_ampdu;
1011da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1012da8fa4e3SBjoern A. Zeeb 
1013da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);
1014da8fa4e3SBjoern A. Zeeb 
1015da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1016da8fa4e3SBjoern A. Zeeb }
1017da8fa4e3SBjoern A. Zeeb 
ath10k_write_htt_max_amsdu_ampdu(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1018da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,
1019da8fa4e3SBjoern A. Zeeb 						const char __user *user_buf,
1020da8fa4e3SBjoern A. Zeeb 						size_t count, loff_t *ppos)
1021da8fa4e3SBjoern A. Zeeb {
1022da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1023da8fa4e3SBjoern A. Zeeb 	int res;
1024da8fa4e3SBjoern A. Zeeb 	char buf[64] = {0};
1025da8fa4e3SBjoern A. Zeeb 	unsigned int amsdu, ampdu;
1026da8fa4e3SBjoern A. Zeeb 
1027da8fa4e3SBjoern A. Zeeb 	res = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
1028da8fa4e3SBjoern A. Zeeb 				     user_buf, count);
1029da8fa4e3SBjoern A. Zeeb 	if (res <= 0)
1030da8fa4e3SBjoern A. Zeeb 		return res;
1031da8fa4e3SBjoern A. Zeeb 
1032da8fa4e3SBjoern A. Zeeb 	res = sscanf(buf, "%u %u", &amsdu, &ampdu);
1033da8fa4e3SBjoern A. Zeeb 
1034da8fa4e3SBjoern A. Zeeb 	if (res != 2)
1035da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1036da8fa4e3SBjoern A. Zeeb 
1037da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1038da8fa4e3SBjoern A. Zeeb 
1039da8fa4e3SBjoern A. Zeeb 	res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);
1040da8fa4e3SBjoern A. Zeeb 	if (res)
1041da8fa4e3SBjoern A. Zeeb 		goto out;
1042da8fa4e3SBjoern A. Zeeb 
1043da8fa4e3SBjoern A. Zeeb 	res = count;
1044da8fa4e3SBjoern A. Zeeb 	ar->htt.max_num_amsdu = amsdu;
1045da8fa4e3SBjoern A. Zeeb 	ar->htt.max_num_ampdu = ampdu;
1046da8fa4e3SBjoern A. Zeeb 
1047da8fa4e3SBjoern A. Zeeb out:
1048da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1049da8fa4e3SBjoern A. Zeeb 	return res;
1050da8fa4e3SBjoern A. Zeeb }
1051da8fa4e3SBjoern A. Zeeb 
1052da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_htt_max_amsdu_ampdu = {
1053da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_htt_max_amsdu_ampdu,
1054da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_htt_max_amsdu_ampdu,
1055da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1056da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1057da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1058da8fa4e3SBjoern A. Zeeb };
1059da8fa4e3SBjoern A. Zeeb 
ath10k_read_fw_dbglog(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1060da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_fw_dbglog(struct file *file,
1061da8fa4e3SBjoern A. Zeeb 				     char __user *user_buf,
1062da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
1063da8fa4e3SBjoern A. Zeeb {
1064da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1065da8fa4e3SBjoern A. Zeeb 	size_t len;
1066da8fa4e3SBjoern A. Zeeb 	char buf[96];
1067da8fa4e3SBjoern A. Zeeb 
1068da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "0x%16llx %u\n",
1069da8fa4e3SBjoern A. Zeeb 			ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);
1070da8fa4e3SBjoern A. Zeeb 
1071da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1072da8fa4e3SBjoern A. Zeeb }
1073da8fa4e3SBjoern A. Zeeb 
ath10k_write_fw_dbglog(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1074da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_fw_dbglog(struct file *file,
1075da8fa4e3SBjoern A. Zeeb 				      const char __user *user_buf,
1076da8fa4e3SBjoern A. Zeeb 				      size_t count, loff_t *ppos)
1077da8fa4e3SBjoern A. Zeeb {
1078da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1079da8fa4e3SBjoern A. Zeeb 	int ret;
1080da8fa4e3SBjoern A. Zeeb 	char buf[96] = {0};
1081da8fa4e3SBjoern A. Zeeb 	unsigned int log_level;
1082da8fa4e3SBjoern A. Zeeb 	u64 mask;
1083da8fa4e3SBjoern A. Zeeb 
1084da8fa4e3SBjoern A. Zeeb 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
1085da8fa4e3SBjoern A. Zeeb 				     user_buf, count);
1086da8fa4e3SBjoern A. Zeeb 	if (ret <= 0)
1087da8fa4e3SBjoern A. Zeeb 		return ret;
1088da8fa4e3SBjoern A. Zeeb 
1089da8fa4e3SBjoern A. Zeeb 	ret = sscanf(buf, "%llx %u", &mask, &log_level);
1090da8fa4e3SBjoern A. Zeeb 
1091da8fa4e3SBjoern A. Zeeb 	if (!ret)
1092da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1093da8fa4e3SBjoern A. Zeeb 
1094da8fa4e3SBjoern A. Zeeb 	if (ret == 1)
1095da8fa4e3SBjoern A. Zeeb 		/* default if user did not specify */
1096da8fa4e3SBjoern A. Zeeb 		log_level = ATH10K_DBGLOG_LEVEL_WARN;
1097da8fa4e3SBjoern A. Zeeb 
1098da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1099da8fa4e3SBjoern A. Zeeb 
1100da8fa4e3SBjoern A. Zeeb 	ar->debug.fw_dbglog_mask = mask;
1101da8fa4e3SBjoern A. Zeeb 	ar->debug.fw_dbglog_level = log_level;
1102da8fa4e3SBjoern A. Zeeb 
1103da8fa4e3SBjoern A. Zeeb 	if (ar->state == ATH10K_STATE_ON) {
1104da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
1105da8fa4e3SBjoern A. Zeeb 					    ar->debug.fw_dbglog_level);
1106da8fa4e3SBjoern A. Zeeb 		if (ret) {
1107da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",
1108da8fa4e3SBjoern A. Zeeb 				    ret);
1109da8fa4e3SBjoern A. Zeeb 			goto exit;
1110da8fa4e3SBjoern A. Zeeb 		}
1111da8fa4e3SBjoern A. Zeeb 	}
1112da8fa4e3SBjoern A. Zeeb 
1113da8fa4e3SBjoern A. Zeeb 	ret = count;
1114da8fa4e3SBjoern A. Zeeb 
1115da8fa4e3SBjoern A. Zeeb exit:
1116da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1117da8fa4e3SBjoern A. Zeeb 
1118da8fa4e3SBjoern A. Zeeb 	return ret;
1119da8fa4e3SBjoern A. Zeeb }
1120da8fa4e3SBjoern A. Zeeb 
1121da8fa4e3SBjoern A. Zeeb /* TODO:  Would be nice to always support ethtool stats, would need to
1122da8fa4e3SBjoern A. Zeeb  * move the stats storage out of ath10k_debug, or always have ath10k_debug
1123da8fa4e3SBjoern A. Zeeb  * struct available..
1124da8fa4e3SBjoern A. Zeeb  */
1125da8fa4e3SBjoern A. Zeeb 
1126*07724ba6SBjoern A. Zeeb /* This generally corresponds to the debugfs fw_stats file */
1127da8fa4e3SBjoern A. Zeeb static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {
1128da8fa4e3SBjoern A. Zeeb 	"tx_pkts_nic",
1129da8fa4e3SBjoern A. Zeeb 	"tx_bytes_nic",
1130da8fa4e3SBjoern A. Zeeb 	"rx_pkts_nic",
1131da8fa4e3SBjoern A. Zeeb 	"rx_bytes_nic",
1132da8fa4e3SBjoern A. Zeeb 	"d_noise_floor",
1133da8fa4e3SBjoern A. Zeeb 	"d_cycle_count",
1134da8fa4e3SBjoern A. Zeeb 	"d_phy_error",
1135da8fa4e3SBjoern A. Zeeb 	"d_rts_bad",
1136da8fa4e3SBjoern A. Zeeb 	"d_rts_good",
1137da8fa4e3SBjoern A. Zeeb 	"d_tx_power", /* in .5 dbM I think */
1138da8fa4e3SBjoern A. Zeeb 	"d_rx_crc_err", /* fcs_bad */
1139da8fa4e3SBjoern A. Zeeb 	"d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */
1140da8fa4e3SBjoern A. Zeeb 	"d_no_beacon",
1141da8fa4e3SBjoern A. Zeeb 	"d_tx_mpdus_queued",
1142da8fa4e3SBjoern A. Zeeb 	"d_tx_msdu_queued",
1143da8fa4e3SBjoern A. Zeeb 	"d_tx_msdu_dropped",
1144da8fa4e3SBjoern A. Zeeb 	"d_local_enqued",
1145da8fa4e3SBjoern A. Zeeb 	"d_local_freed",
1146da8fa4e3SBjoern A. Zeeb 	"d_tx_ppdu_hw_queued",
1147da8fa4e3SBjoern A. Zeeb 	"d_tx_ppdu_reaped",
1148da8fa4e3SBjoern A. Zeeb 	"d_tx_fifo_underrun",
1149da8fa4e3SBjoern A. Zeeb 	"d_tx_ppdu_abort",
1150da8fa4e3SBjoern A. Zeeb 	"d_tx_mpdu_requeued",
1151da8fa4e3SBjoern A. Zeeb 	"d_tx_excessive_retries",
1152da8fa4e3SBjoern A. Zeeb 	"d_tx_hw_rate",
1153da8fa4e3SBjoern A. Zeeb 	"d_tx_dropped_sw_retries",
1154da8fa4e3SBjoern A. Zeeb 	"d_tx_illegal_rate",
1155da8fa4e3SBjoern A. Zeeb 	"d_tx_continuous_xretries",
1156da8fa4e3SBjoern A. Zeeb 	"d_tx_timeout",
1157da8fa4e3SBjoern A. Zeeb 	"d_tx_mpdu_txop_limit",
1158da8fa4e3SBjoern A. Zeeb 	"d_pdev_resets",
1159da8fa4e3SBjoern A. Zeeb 	"d_rx_mid_ppdu_route_change",
1160da8fa4e3SBjoern A. Zeeb 	"d_rx_status",
1161da8fa4e3SBjoern A. Zeeb 	"d_rx_extra_frags_ring0",
1162da8fa4e3SBjoern A. Zeeb 	"d_rx_extra_frags_ring1",
1163da8fa4e3SBjoern A. Zeeb 	"d_rx_extra_frags_ring2",
1164da8fa4e3SBjoern A. Zeeb 	"d_rx_extra_frags_ring3",
1165da8fa4e3SBjoern A. Zeeb 	"d_rx_msdu_htt",
1166da8fa4e3SBjoern A. Zeeb 	"d_rx_mpdu_htt",
1167da8fa4e3SBjoern A. Zeeb 	"d_rx_msdu_stack",
1168da8fa4e3SBjoern A. Zeeb 	"d_rx_mpdu_stack",
1169da8fa4e3SBjoern A. Zeeb 	"d_rx_phy_err",
1170da8fa4e3SBjoern A. Zeeb 	"d_rx_phy_err_drops",
1171da8fa4e3SBjoern A. Zeeb 	"d_rx_mpdu_errors", /* FCS, MIC, ENC */
1172da8fa4e3SBjoern A. Zeeb 	"d_fw_crash_count",
1173da8fa4e3SBjoern A. Zeeb 	"d_fw_warm_reset_count",
1174da8fa4e3SBjoern A. Zeeb 	"d_fw_cold_reset_count",
1175da8fa4e3SBjoern A. Zeeb };
1176da8fa4e3SBjoern A. Zeeb 
1177da8fa4e3SBjoern A. Zeeb #define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)
1178da8fa4e3SBjoern A. Zeeb 
ath10k_debug_get_et_strings(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 sset,u8 * data)1179da8fa4e3SBjoern A. Zeeb void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
1180da8fa4e3SBjoern A. Zeeb 				 struct ieee80211_vif *vif,
1181da8fa4e3SBjoern A. Zeeb 				 u32 sset, u8 *data)
1182da8fa4e3SBjoern A. Zeeb {
1183da8fa4e3SBjoern A. Zeeb 	if (sset == ETH_SS_STATS)
1184da8fa4e3SBjoern A. Zeeb 		memcpy(data, *ath10k_gstrings_stats,
1185da8fa4e3SBjoern A. Zeeb 		       sizeof(ath10k_gstrings_stats));
1186da8fa4e3SBjoern A. Zeeb }
1187da8fa4e3SBjoern A. Zeeb 
ath10k_debug_get_et_sset_count(struct ieee80211_hw * hw,struct ieee80211_vif * vif,int sset)1188da8fa4e3SBjoern A. Zeeb int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
1189da8fa4e3SBjoern A. Zeeb 				   struct ieee80211_vif *vif, int sset)
1190da8fa4e3SBjoern A. Zeeb {
1191da8fa4e3SBjoern A. Zeeb 	if (sset == ETH_SS_STATS)
1192da8fa4e3SBjoern A. Zeeb 		return ATH10K_SSTATS_LEN;
1193da8fa4e3SBjoern A. Zeeb 
1194da8fa4e3SBjoern A. Zeeb 	return 0;
1195da8fa4e3SBjoern A. Zeeb }
1196da8fa4e3SBjoern A. Zeeb 
ath10k_debug_get_et_stats(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ethtool_stats * stats,u64 * data)1197da8fa4e3SBjoern A. Zeeb void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
1198da8fa4e3SBjoern A. Zeeb 			       struct ieee80211_vif *vif,
1199da8fa4e3SBjoern A. Zeeb 			       struct ethtool_stats *stats, u64 *data)
1200da8fa4e3SBjoern A. Zeeb {
1201da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = hw->priv;
1202da8fa4e3SBjoern A. Zeeb 	static const struct ath10k_fw_stats_pdev zero_stats = {};
1203da8fa4e3SBjoern A. Zeeb 	const struct ath10k_fw_stats_pdev *pdev_stats;
1204da8fa4e3SBjoern A. Zeeb 	int i = 0, ret;
1205da8fa4e3SBjoern A. Zeeb 
1206da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1207da8fa4e3SBjoern A. Zeeb 
1208da8fa4e3SBjoern A. Zeeb 	if (ar->state == ATH10K_STATE_ON) {
1209da8fa4e3SBjoern A. Zeeb 		ret = ath10k_debug_fw_stats_request(ar);
1210da8fa4e3SBjoern A. Zeeb 		if (ret) {
1211da8fa4e3SBjoern A. Zeeb 			/* just print a warning and try to use older results */
1212da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar,
1213da8fa4e3SBjoern A. Zeeb 				    "failed to get fw stats for ethtool: %d\n",
1214da8fa4e3SBjoern A. Zeeb 				    ret);
1215da8fa4e3SBjoern A. Zeeb 		}
1216da8fa4e3SBjoern A. Zeeb 	}
1217da8fa4e3SBjoern A. Zeeb 
1218da8fa4e3SBjoern A. Zeeb 	pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,
1219da8fa4e3SBjoern A. Zeeb 					      struct ath10k_fw_stats_pdev,
1220da8fa4e3SBjoern A. Zeeb 					      list);
1221da8fa4e3SBjoern A. Zeeb 	if (!pdev_stats) {
1222da8fa4e3SBjoern A. Zeeb 		/* no results available so just return zeroes */
1223da8fa4e3SBjoern A. Zeeb 		pdev_stats = &zero_stats;
1224da8fa4e3SBjoern A. Zeeb 	}
1225da8fa4e3SBjoern A. Zeeb 
1226da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
1227da8fa4e3SBjoern A. Zeeb 
1228da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */
1229da8fa4e3SBjoern A. Zeeb 	data[i++] = 0; /* tx bytes */
1230da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->htt_mpdus;
1231da8fa4e3SBjoern A. Zeeb 	data[i++] = 0; /* rx bytes */
1232da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->ch_noise_floor;
1233da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->cycle_count;
1234da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->phy_err_count;
1235da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->rts_bad;
1236da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->rts_good;
1237da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->chan_tx_power;
1238da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->fcs_bad;
1239da8fa4e3SBjoern A. Zeeb 	data[i++] = ar->stats.rx_crc_err_drop;
1240da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->no_beacons;
1241da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->mpdu_enqued;
1242da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->msdu_enqued;
1243da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->wmm_drop;
1244da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->local_enqued;
1245da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->local_freed;
1246da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->hw_queued;
1247da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->hw_reaped;
1248da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->underrun;
1249da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->tx_abort;
1250da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->mpdus_requeued;
1251da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->tx_ko;
1252da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->data_rc;
1253da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->sw_retry_failure;
1254da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->illgl_rate_phy_err;
1255da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->pdev_cont_xretry;
1256da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->pdev_tx_timeout;
1257da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->txop_ovf;
1258da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->pdev_resets;
1259da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->mid_ppdu_route_change;
1260da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->status_rcvd;
1261da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->r0_frags;
1262da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->r1_frags;
1263da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->r2_frags;
1264da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->r3_frags;
1265da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->htt_msdus;
1266da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->htt_mpdus;
1267da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->loc_msdus;
1268da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->loc_mpdus;
1269da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->phy_errs;
1270da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->phy_err_drop;
1271da8fa4e3SBjoern A. Zeeb 	data[i++] = pdev_stats->mpdu_errs;
1272da8fa4e3SBjoern A. Zeeb 	data[i++] = ar->stats.fw_crash_counter;
1273da8fa4e3SBjoern A. Zeeb 	data[i++] = ar->stats.fw_warm_reset_counter;
1274da8fa4e3SBjoern A. Zeeb 	data[i++] = ar->stats.fw_cold_reset_counter;
1275da8fa4e3SBjoern A. Zeeb 
1276da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
1277da8fa4e3SBjoern A. Zeeb 
1278da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1279da8fa4e3SBjoern A. Zeeb 
1280da8fa4e3SBjoern A. Zeeb 	WARN_ON(i != ATH10K_SSTATS_LEN);
1281da8fa4e3SBjoern A. Zeeb }
1282da8fa4e3SBjoern A. Zeeb 
1283da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_fw_dbglog = {
1284da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_fw_dbglog,
1285da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_fw_dbglog,
1286da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1287da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1288da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1289da8fa4e3SBjoern A. Zeeb };
1290da8fa4e3SBjoern A. Zeeb 
ath10k_debug_cal_data_fetch(struct ath10k * ar)1291da8fa4e3SBjoern A. Zeeb static int ath10k_debug_cal_data_fetch(struct ath10k *ar)
1292da8fa4e3SBjoern A. Zeeb {
1293da8fa4e3SBjoern A. Zeeb 	u32 hi_addr;
1294da8fa4e3SBjoern A. Zeeb 	__le32 addr;
1295da8fa4e3SBjoern A. Zeeb 	int ret;
1296da8fa4e3SBjoern A. Zeeb 
1297da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
1298da8fa4e3SBjoern A. Zeeb 
1299da8fa4e3SBjoern A. Zeeb 	if (WARN_ON(ar->hw_params.cal_data_len > ATH10K_DEBUG_CAL_DATA_LEN))
1300da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1301da8fa4e3SBjoern A. Zeeb 
1302da8fa4e3SBjoern A. Zeeb 	if (ar->hw_params.cal_data_len == 0)
1303da8fa4e3SBjoern A. Zeeb 		return -EOPNOTSUPP;
1304da8fa4e3SBjoern A. Zeeb 
1305da8fa4e3SBjoern A. Zeeb 	hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));
1306da8fa4e3SBjoern A. Zeeb 
1307da8fa4e3SBjoern A. Zeeb 	ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));
1308da8fa4e3SBjoern A. Zeeb 	if (ret) {
1309da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to read hi_board_data address: %d\n",
1310da8fa4e3SBjoern A. Zeeb 			    ret);
1311da8fa4e3SBjoern A. Zeeb 		return ret;
1312da8fa4e3SBjoern A. Zeeb 	}
1313da8fa4e3SBjoern A. Zeeb 
1314da8fa4e3SBjoern A. Zeeb 	ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), ar->debug.cal_data,
1315da8fa4e3SBjoern A. Zeeb 				   ar->hw_params.cal_data_len);
1316da8fa4e3SBjoern A. Zeeb 	if (ret) {
1317da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
1318da8fa4e3SBjoern A. Zeeb 		return ret;
1319da8fa4e3SBjoern A. Zeeb 	}
1320da8fa4e3SBjoern A. Zeeb 
1321da8fa4e3SBjoern A. Zeeb 	return 0;
1322da8fa4e3SBjoern A. Zeeb }
1323da8fa4e3SBjoern A. Zeeb 
ath10k_debug_cal_data_open(struct inode * inode,struct file * file)1324da8fa4e3SBjoern A. Zeeb static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
1325da8fa4e3SBjoern A. Zeeb {
1326da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = inode->i_private;
1327da8fa4e3SBjoern A. Zeeb 
1328da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1329da8fa4e3SBjoern A. Zeeb 
1330da8fa4e3SBjoern A. Zeeb 	if (ar->state == ATH10K_STATE_ON ||
1331da8fa4e3SBjoern A. Zeeb 	    ar->state == ATH10K_STATE_UTF) {
1332da8fa4e3SBjoern A. Zeeb 		ath10k_debug_cal_data_fetch(ar);
1333da8fa4e3SBjoern A. Zeeb 	}
1334da8fa4e3SBjoern A. Zeeb 
1335da8fa4e3SBjoern A. Zeeb 	file->private_data = ar;
1336da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1337da8fa4e3SBjoern A. Zeeb 
1338da8fa4e3SBjoern A. Zeeb 	return 0;
1339da8fa4e3SBjoern A. Zeeb }
1340da8fa4e3SBjoern A. Zeeb 
ath10k_debug_cal_data_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1341da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_debug_cal_data_read(struct file *file,
1342da8fa4e3SBjoern A. Zeeb 					  char __user *user_buf,
1343da8fa4e3SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
1344da8fa4e3SBjoern A. Zeeb {
1345da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1346da8fa4e3SBjoern A. Zeeb 
1347da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1348da8fa4e3SBjoern A. Zeeb 
1349da8fa4e3SBjoern A. Zeeb 	count = simple_read_from_buffer(user_buf, count, ppos,
1350da8fa4e3SBjoern A. Zeeb 					ar->debug.cal_data,
1351da8fa4e3SBjoern A. Zeeb 					ar->hw_params.cal_data_len);
1352da8fa4e3SBjoern A. Zeeb 
1353da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1354da8fa4e3SBjoern A. Zeeb 
1355da8fa4e3SBjoern A. Zeeb 	return count;
1356da8fa4e3SBjoern A. Zeeb }
1357da8fa4e3SBjoern A. Zeeb 
ath10k_write_ani_enable(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1358da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_ani_enable(struct file *file,
1359da8fa4e3SBjoern A. Zeeb 				       const char __user *user_buf,
1360da8fa4e3SBjoern A. Zeeb 				       size_t count, loff_t *ppos)
1361da8fa4e3SBjoern A. Zeeb {
1362da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1363da8fa4e3SBjoern A. Zeeb 	int ret;
1364da8fa4e3SBjoern A. Zeeb 	u8 enable;
1365da8fa4e3SBjoern A. Zeeb 
1366da8fa4e3SBjoern A. Zeeb 	if (kstrtou8_from_user(user_buf, count, 0, &enable))
1367da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1368da8fa4e3SBjoern A. Zeeb 
1369da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1370da8fa4e3SBjoern A. Zeeb 
1371da8fa4e3SBjoern A. Zeeb 	if (ar->ani_enabled == enable) {
1372da8fa4e3SBjoern A. Zeeb 		ret = count;
1373da8fa4e3SBjoern A. Zeeb 		goto exit;
1374da8fa4e3SBjoern A. Zeeb 	}
1375da8fa4e3SBjoern A. Zeeb 
1376da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->ani_enable,
1377da8fa4e3SBjoern A. Zeeb 					enable);
1378da8fa4e3SBjoern A. Zeeb 	if (ret) {
1379da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "ani_enable failed from debugfs: %d\n", ret);
1380da8fa4e3SBjoern A. Zeeb 		goto exit;
1381da8fa4e3SBjoern A. Zeeb 	}
1382da8fa4e3SBjoern A. Zeeb 	ar->ani_enabled = enable;
1383da8fa4e3SBjoern A. Zeeb 
1384da8fa4e3SBjoern A. Zeeb 	ret = count;
1385da8fa4e3SBjoern A. Zeeb 
1386da8fa4e3SBjoern A. Zeeb exit:
1387da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1388da8fa4e3SBjoern A. Zeeb 
1389da8fa4e3SBjoern A. Zeeb 	return ret;
1390da8fa4e3SBjoern A. Zeeb }
1391da8fa4e3SBjoern A. Zeeb 
ath10k_read_ani_enable(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1392da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_ani_enable(struct file *file, char __user *user_buf,
1393da8fa4e3SBjoern A. Zeeb 				      size_t count, loff_t *ppos)
1394da8fa4e3SBjoern A. Zeeb {
1395da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1396da8fa4e3SBjoern A. Zeeb 	size_t len;
1397da8fa4e3SBjoern A. Zeeb 	char buf[32];
1398da8fa4e3SBjoern A. Zeeb 
1399da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "%d\n", ar->ani_enabled);
1400da8fa4e3SBjoern A. Zeeb 
1401da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1402da8fa4e3SBjoern A. Zeeb }
1403da8fa4e3SBjoern A. Zeeb 
1404da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_ani_enable = {
1405da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_ani_enable,
1406da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_ani_enable,
1407da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1408da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1409da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1410da8fa4e3SBjoern A. Zeeb };
1411da8fa4e3SBjoern A. Zeeb 
1412da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_cal_data = {
1413da8fa4e3SBjoern A. Zeeb 	.open = ath10k_debug_cal_data_open,
1414da8fa4e3SBjoern A. Zeeb 	.read = ath10k_debug_cal_data_read,
1415da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1416da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1417da8fa4e3SBjoern A. Zeeb };
1418da8fa4e3SBjoern A. Zeeb 
ath10k_read_nf_cal_period(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1419da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_nf_cal_period(struct file *file,
1420da8fa4e3SBjoern A. Zeeb 					 char __user *user_buf,
1421da8fa4e3SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
1422da8fa4e3SBjoern A. Zeeb {
1423da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1424da8fa4e3SBjoern A. Zeeb 	size_t len;
1425da8fa4e3SBjoern A. Zeeb 	char buf[32];
1426da8fa4e3SBjoern A. Zeeb 
1427da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "%d\n", ar->debug.nf_cal_period);
1428da8fa4e3SBjoern A. Zeeb 
1429da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1430da8fa4e3SBjoern A. Zeeb }
1431da8fa4e3SBjoern A. Zeeb 
ath10k_write_nf_cal_period(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1432da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_nf_cal_period(struct file *file,
1433da8fa4e3SBjoern A. Zeeb 					  const char __user *user_buf,
1434da8fa4e3SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
1435da8fa4e3SBjoern A. Zeeb {
1436da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1437da8fa4e3SBjoern A. Zeeb 	unsigned long period;
1438da8fa4e3SBjoern A. Zeeb 	int ret;
1439da8fa4e3SBjoern A. Zeeb 
1440da8fa4e3SBjoern A. Zeeb 	ret = kstrtoul_from_user(user_buf, count, 0, &period);
1441da8fa4e3SBjoern A. Zeeb 	if (ret)
1442da8fa4e3SBjoern A. Zeeb 		return ret;
1443da8fa4e3SBjoern A. Zeeb 
1444da8fa4e3SBjoern A. Zeeb 	if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX)
1445da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1446da8fa4e3SBjoern A. Zeeb 
1447da8fa4e3SBjoern A. Zeeb 	/* there's no way to switch back to the firmware default */
1448da8fa4e3SBjoern A. Zeeb 	if (period == 0)
1449da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1450da8fa4e3SBjoern A. Zeeb 
1451da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1452da8fa4e3SBjoern A. Zeeb 
1453da8fa4e3SBjoern A. Zeeb 	ar->debug.nf_cal_period = period;
1454da8fa4e3SBjoern A. Zeeb 
1455da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
1456da8fa4e3SBjoern A. Zeeb 		/* firmware is not running, nothing else to do */
1457da8fa4e3SBjoern A. Zeeb 		ret = count;
1458da8fa4e3SBjoern A. Zeeb 		goto exit;
1459da8fa4e3SBjoern A. Zeeb 	}
1460da8fa4e3SBjoern A. Zeeb 
1461da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period,
1462da8fa4e3SBjoern A. Zeeb 					ar->debug.nf_cal_period);
1463da8fa4e3SBjoern A. Zeeb 	if (ret) {
1464da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n",
1465da8fa4e3SBjoern A. Zeeb 			    ret);
1466da8fa4e3SBjoern A. Zeeb 		goto exit;
1467da8fa4e3SBjoern A. Zeeb 	}
1468da8fa4e3SBjoern A. Zeeb 
1469da8fa4e3SBjoern A. Zeeb 	ret = count;
1470da8fa4e3SBjoern A. Zeeb 
1471da8fa4e3SBjoern A. Zeeb exit:
1472da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1473da8fa4e3SBjoern A. Zeeb 
1474da8fa4e3SBjoern A. Zeeb 	return ret;
1475da8fa4e3SBjoern A. Zeeb }
1476da8fa4e3SBjoern A. Zeeb 
1477da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_nf_cal_period = {
1478da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_nf_cal_period,
1479da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_nf_cal_period,
1480da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1481da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1482da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1483da8fa4e3SBjoern A. Zeeb };
1484da8fa4e3SBjoern A. Zeeb 
1485da8fa4e3SBjoern A. Zeeb #define ATH10K_TPC_CONFIG_BUF_SIZE	(1024 * 1024)
1486da8fa4e3SBjoern A. Zeeb 
ath10k_debug_tpc_stats_request(struct ath10k * ar)1487da8fa4e3SBjoern A. Zeeb static int ath10k_debug_tpc_stats_request(struct ath10k *ar)
1488da8fa4e3SBjoern A. Zeeb {
1489da8fa4e3SBjoern A. Zeeb 	int ret;
1490da8fa4e3SBjoern A. Zeeb 	unsigned long time_left;
1491da8fa4e3SBjoern A. Zeeb 
1492da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
1493da8fa4e3SBjoern A. Zeeb 
1494da8fa4e3SBjoern A. Zeeb 	reinit_completion(&ar->debug.tpc_complete);
1495da8fa4e3SBjoern A. Zeeb 
1496da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_get_tpc_config(ar, WMI_TPC_CONFIG_PARAM);
1497da8fa4e3SBjoern A. Zeeb 	if (ret) {
1498da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to request tpc config: %d\n", ret);
1499da8fa4e3SBjoern A. Zeeb 		return ret;
1500da8fa4e3SBjoern A. Zeeb 	}
1501da8fa4e3SBjoern A. Zeeb 
1502da8fa4e3SBjoern A. Zeeb 	time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,
1503da8fa4e3SBjoern A. Zeeb 						1 * HZ);
1504da8fa4e3SBjoern A. Zeeb 	if (time_left == 0)
1505da8fa4e3SBjoern A. Zeeb 		return -ETIMEDOUT;
1506da8fa4e3SBjoern A. Zeeb 
1507da8fa4e3SBjoern A. Zeeb 	return 0;
1508da8fa4e3SBjoern A. Zeeb }
1509da8fa4e3SBjoern A. Zeeb 
ath10k_debug_tpc_stats_process(struct ath10k * ar,struct ath10k_tpc_stats * tpc_stats)1510da8fa4e3SBjoern A. Zeeb void ath10k_debug_tpc_stats_process(struct ath10k *ar,
1511da8fa4e3SBjoern A. Zeeb 				    struct ath10k_tpc_stats *tpc_stats)
1512da8fa4e3SBjoern A. Zeeb {
1513da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
1514da8fa4e3SBjoern A. Zeeb 
1515da8fa4e3SBjoern A. Zeeb 	kfree(ar->debug.tpc_stats);
1516da8fa4e3SBjoern A. Zeeb 	ar->debug.tpc_stats = tpc_stats;
1517da8fa4e3SBjoern A. Zeeb 	complete(&ar->debug.tpc_complete);
1518da8fa4e3SBjoern A. Zeeb 
1519da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
1520da8fa4e3SBjoern A. Zeeb }
1521da8fa4e3SBjoern A. Zeeb 
1522da8fa4e3SBjoern A. Zeeb void
ath10k_debug_tpc_stats_final_process(struct ath10k * ar,struct ath10k_tpc_stats_final * tpc_stats)1523da8fa4e3SBjoern A. Zeeb ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
1524da8fa4e3SBjoern A. Zeeb 				     struct ath10k_tpc_stats_final *tpc_stats)
1525da8fa4e3SBjoern A. Zeeb {
1526da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
1527da8fa4e3SBjoern A. Zeeb 
1528da8fa4e3SBjoern A. Zeeb 	kfree(ar->debug.tpc_stats_final);
1529da8fa4e3SBjoern A. Zeeb 	ar->debug.tpc_stats_final = tpc_stats;
1530da8fa4e3SBjoern A. Zeeb 	complete(&ar->debug.tpc_complete);
1531da8fa4e3SBjoern A. Zeeb 
1532da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
1533da8fa4e3SBjoern A. Zeeb }
1534da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_print(struct ath10k_tpc_stats * tpc_stats,unsigned int j,char * buf,size_t * len)1535da8fa4e3SBjoern A. Zeeb static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,
1536da8fa4e3SBjoern A. Zeeb 				   unsigned int j, char *buf, size_t *len)
1537da8fa4e3SBjoern A. Zeeb {
1538da8fa4e3SBjoern A. Zeeb 	int i;
1539da8fa4e3SBjoern A. Zeeb 	size_t buf_len;
1540da8fa4e3SBjoern A. Zeeb 	static const char table_str[][5] = { "CDD",
1541da8fa4e3SBjoern A. Zeeb 					     "STBC",
1542da8fa4e3SBjoern A. Zeeb 					     "TXBF" };
1543da8fa4e3SBjoern A. Zeeb 	static const char pream_str[][6] = { "CCK",
1544da8fa4e3SBjoern A. Zeeb 					     "OFDM",
1545da8fa4e3SBjoern A. Zeeb 					     "HT20",
1546da8fa4e3SBjoern A. Zeeb 					     "HT40",
1547da8fa4e3SBjoern A. Zeeb 					     "VHT20",
1548da8fa4e3SBjoern A. Zeeb 					     "VHT40",
1549da8fa4e3SBjoern A. Zeeb 					     "VHT80",
1550da8fa4e3SBjoern A. Zeeb 					     "HTCUP" };
1551da8fa4e3SBjoern A. Zeeb 
1552da8fa4e3SBjoern A. Zeeb 	buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;
1553da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len,
1554da8fa4e3SBjoern A. Zeeb 			  "********************************\n");
1555da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len,
1556da8fa4e3SBjoern A. Zeeb 			  "******************* %s POWER TABLE ****************\n",
1557da8fa4e3SBjoern A. Zeeb 			  table_str[j]);
1558da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len,
1559da8fa4e3SBjoern A. Zeeb 			  "********************************\n");
1560da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len,
1561da8fa4e3SBjoern A. Zeeb 			  "No.  Preamble Rate_code ");
1562da8fa4e3SBjoern A. Zeeb 
1563da8fa4e3SBjoern A. Zeeb 	for (i = 0; i < tpc_stats->num_tx_chain; i++)
1564da8fa4e3SBjoern A. Zeeb 		*len += scnprintf(buf + *len, buf_len - *len,
1565da8fa4e3SBjoern A. Zeeb 				  "tpc_value%d ", i);
1566da8fa4e3SBjoern A. Zeeb 
1567da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len, "\n");
1568da8fa4e3SBjoern A. Zeeb 
1569da8fa4e3SBjoern A. Zeeb 	for (i = 0; i < tpc_stats->rate_max; i++) {
1570da8fa4e3SBjoern A. Zeeb 		*len += scnprintf(buf + *len, buf_len - *len,
1571da8fa4e3SBjoern A. Zeeb 				  "%8d %s 0x%2x %s\n", i,
1572da8fa4e3SBjoern A. Zeeb 				  pream_str[tpc_stats->tpc_table[j].pream_idx[i]],
1573da8fa4e3SBjoern A. Zeeb 				  tpc_stats->tpc_table[j].rate_code[i],
1574da8fa4e3SBjoern A. Zeeb 				  tpc_stats->tpc_table[j].tpc_value[i]);
1575da8fa4e3SBjoern A. Zeeb 	}
1576da8fa4e3SBjoern A. Zeeb 
1577da8fa4e3SBjoern A. Zeeb 	*len += scnprintf(buf + *len, buf_len - *len,
1578da8fa4e3SBjoern A. Zeeb 			  "***********************************\n");
1579da8fa4e3SBjoern A. Zeeb }
1580da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_fill(struct ath10k * ar,struct ath10k_tpc_stats * tpc_stats,char * buf)1581da8fa4e3SBjoern A. Zeeb static void ath10k_tpc_stats_fill(struct ath10k *ar,
1582da8fa4e3SBjoern A. Zeeb 				  struct ath10k_tpc_stats *tpc_stats,
1583da8fa4e3SBjoern A. Zeeb 				  char *buf)
1584da8fa4e3SBjoern A. Zeeb {
1585da8fa4e3SBjoern A. Zeeb 	int j;
1586da8fa4e3SBjoern A. Zeeb 	size_t len, buf_len;
1587da8fa4e3SBjoern A. Zeeb 
1588da8fa4e3SBjoern A. Zeeb 	len = 0;
1589da8fa4e3SBjoern A. Zeeb 	buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;
1590da8fa4e3SBjoern A. Zeeb 
1591da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
1592da8fa4e3SBjoern A. Zeeb 
1593da8fa4e3SBjoern A. Zeeb 	if (!tpc_stats) {
1594da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to get tpc stats\n");
1595da8fa4e3SBjoern A. Zeeb 		goto unlock;
1596da8fa4e3SBjoern A. Zeeb 	}
1597da8fa4e3SBjoern A. Zeeb 
1598da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len, "\n");
1599da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1600da8fa4e3SBjoern A. Zeeb 			 "*************************************\n");
1601da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1602da8fa4e3SBjoern A. Zeeb 			 "TPC config for channel %4d mode %d\n",
1603da8fa4e3SBjoern A. Zeeb 			 tpc_stats->chan_freq,
1604da8fa4e3SBjoern A. Zeeb 			 tpc_stats->phy_mode);
1605da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1606da8fa4e3SBjoern A. Zeeb 			 "*************************************\n");
1607da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1608da8fa4e3SBjoern A. Zeeb 			 "CTL		=  0x%2x Reg. Domain		= %2d\n",
1609da8fa4e3SBjoern A. Zeeb 			 tpc_stats->ctl,
1610da8fa4e3SBjoern A. Zeeb 			 tpc_stats->reg_domain);
1611da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1612da8fa4e3SBjoern A. Zeeb 			 "Antenna Gain	= %2d Reg. Max Antenna Gain	=  %2d\n",
1613da8fa4e3SBjoern A. Zeeb 			 tpc_stats->twice_antenna_gain,
1614da8fa4e3SBjoern A. Zeeb 			 tpc_stats->twice_antenna_reduction);
1615da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1616da8fa4e3SBjoern A. Zeeb 			 "Power Limit	= %2d Reg. Max Power		= %2d\n",
1617da8fa4e3SBjoern A. Zeeb 			 tpc_stats->power_limit,
1618da8fa4e3SBjoern A. Zeeb 			 tpc_stats->twice_max_rd_power / 2);
1619da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
1620da8fa4e3SBjoern A. Zeeb 			 "Num tx chains	= %2d Num supported rates	= %2d\n",
1621da8fa4e3SBjoern A. Zeeb 			 tpc_stats->num_tx_chain,
1622da8fa4e3SBjoern A. Zeeb 			 tpc_stats->rate_max);
1623da8fa4e3SBjoern A. Zeeb 
1624da8fa4e3SBjoern A. Zeeb 	for (j = 0; j < WMI_TPC_FLAG; j++) {
1625da8fa4e3SBjoern A. Zeeb 		switch (j) {
1626da8fa4e3SBjoern A. Zeeb 		case WMI_TPC_TABLE_TYPE_CDD:
1627da8fa4e3SBjoern A. Zeeb 			if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
1628da8fa4e3SBjoern A. Zeeb 				len += scnprintf(buf + len, buf_len - len,
1629da8fa4e3SBjoern A. Zeeb 						 "CDD not supported\n");
1630da8fa4e3SBjoern A. Zeeb 				break;
1631da8fa4e3SBjoern A. Zeeb 			}
1632da8fa4e3SBjoern A. Zeeb 
1633da8fa4e3SBjoern A. Zeeb 			ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
1634da8fa4e3SBjoern A. Zeeb 			break;
1635da8fa4e3SBjoern A. Zeeb 		case WMI_TPC_TABLE_TYPE_STBC:
1636da8fa4e3SBjoern A. Zeeb 			if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
1637da8fa4e3SBjoern A. Zeeb 				len += scnprintf(buf + len, buf_len - len,
1638da8fa4e3SBjoern A. Zeeb 						 "STBC not supported\n");
1639da8fa4e3SBjoern A. Zeeb 				break;
1640da8fa4e3SBjoern A. Zeeb 			}
1641da8fa4e3SBjoern A. Zeeb 
1642da8fa4e3SBjoern A. Zeeb 			ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
1643da8fa4e3SBjoern A. Zeeb 			break;
1644da8fa4e3SBjoern A. Zeeb 		case WMI_TPC_TABLE_TYPE_TXBF:
1645da8fa4e3SBjoern A. Zeeb 			if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
1646da8fa4e3SBjoern A. Zeeb 				len += scnprintf(buf + len, buf_len - len,
1647da8fa4e3SBjoern A. Zeeb 						 "TXBF not supported\n***************************\n");
1648da8fa4e3SBjoern A. Zeeb 				break;
1649da8fa4e3SBjoern A. Zeeb 			}
1650da8fa4e3SBjoern A. Zeeb 
1651da8fa4e3SBjoern A. Zeeb 			ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
1652da8fa4e3SBjoern A. Zeeb 			break;
1653da8fa4e3SBjoern A. Zeeb 		default:
1654da8fa4e3SBjoern A. Zeeb 			len += scnprintf(buf + len, buf_len - len,
1655da8fa4e3SBjoern A. Zeeb 					 "Invalid Type\n");
1656da8fa4e3SBjoern A. Zeeb 			break;
1657da8fa4e3SBjoern A. Zeeb 		}
1658da8fa4e3SBjoern A. Zeeb 	}
1659da8fa4e3SBjoern A. Zeeb 
1660da8fa4e3SBjoern A. Zeeb unlock:
1661da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
1662da8fa4e3SBjoern A. Zeeb 
1663da8fa4e3SBjoern A. Zeeb 	if (len >= buf_len)
1664da8fa4e3SBjoern A. Zeeb 		buf[len - 1] = 0;
1665da8fa4e3SBjoern A. Zeeb 	else
1666da8fa4e3SBjoern A. Zeeb 		buf[len] = 0;
1667da8fa4e3SBjoern A. Zeeb }
1668da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_open(struct inode * inode,struct file * file)1669da8fa4e3SBjoern A. Zeeb static int ath10k_tpc_stats_open(struct inode *inode, struct file *file)
1670da8fa4e3SBjoern A. Zeeb {
1671da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = inode->i_private;
1672da8fa4e3SBjoern A. Zeeb 	void *buf = NULL;
1673da8fa4e3SBjoern A. Zeeb 	int ret;
1674da8fa4e3SBjoern A. Zeeb 
1675da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1676da8fa4e3SBjoern A. Zeeb 
1677da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
1678da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
1679da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
1680da8fa4e3SBjoern A. Zeeb 	}
1681da8fa4e3SBjoern A. Zeeb 
1682da8fa4e3SBjoern A. Zeeb 	buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);
1683da8fa4e3SBjoern A. Zeeb 	if (!buf) {
1684da8fa4e3SBjoern A. Zeeb 		ret = -ENOMEM;
1685da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
1686da8fa4e3SBjoern A. Zeeb 	}
1687da8fa4e3SBjoern A. Zeeb 
1688da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_tpc_stats_request(ar);
1689da8fa4e3SBjoern A. Zeeb 	if (ret) {
1690da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to request tpc config stats: %d\n",
1691da8fa4e3SBjoern A. Zeeb 			    ret);
1692da8fa4e3SBjoern A. Zeeb 		goto err_free;
1693da8fa4e3SBjoern A. Zeeb 	}
1694da8fa4e3SBjoern A. Zeeb 
1695da8fa4e3SBjoern A. Zeeb 	ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
1696da8fa4e3SBjoern A. Zeeb 	file->private_data = buf;
1697da8fa4e3SBjoern A. Zeeb 
1698da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1699da8fa4e3SBjoern A. Zeeb 	return 0;
1700da8fa4e3SBjoern A. Zeeb 
1701da8fa4e3SBjoern A. Zeeb err_free:
1702da8fa4e3SBjoern A. Zeeb 	vfree(buf);
1703da8fa4e3SBjoern A. Zeeb 
1704da8fa4e3SBjoern A. Zeeb err_unlock:
1705da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1706da8fa4e3SBjoern A. Zeeb 	return ret;
1707da8fa4e3SBjoern A. Zeeb }
1708da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_release(struct inode * inode,struct file * file)1709da8fa4e3SBjoern A. Zeeb static int ath10k_tpc_stats_release(struct inode *inode, struct file *file)
1710da8fa4e3SBjoern A. Zeeb {
1711da8fa4e3SBjoern A. Zeeb 	vfree(file->private_data);
1712da8fa4e3SBjoern A. Zeeb 
1713da8fa4e3SBjoern A. Zeeb 	return 0;
1714da8fa4e3SBjoern A. Zeeb }
1715da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1716da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_tpc_stats_read(struct file *file, char __user *user_buf,
1717da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
1718da8fa4e3SBjoern A. Zeeb {
1719da8fa4e3SBjoern A. Zeeb 	const char *buf = file->private_data;
1720da8fa4e3SBjoern A. Zeeb 	size_t len = strlen(buf);
1721da8fa4e3SBjoern A. Zeeb 
1722da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1723da8fa4e3SBjoern A. Zeeb }
1724da8fa4e3SBjoern A. Zeeb 
1725da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_tpc_stats = {
1726da8fa4e3SBjoern A. Zeeb 	.open = ath10k_tpc_stats_open,
1727da8fa4e3SBjoern A. Zeeb 	.release = ath10k_tpc_stats_release,
1728da8fa4e3SBjoern A. Zeeb 	.read = ath10k_tpc_stats_read,
1729da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1730da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1731da8fa4e3SBjoern A. Zeeb };
1732da8fa4e3SBjoern A. Zeeb 
ath10k_debug_start(struct ath10k * ar)1733da8fa4e3SBjoern A. Zeeb int ath10k_debug_start(struct ath10k *ar)
1734da8fa4e3SBjoern A. Zeeb {
1735da8fa4e3SBjoern A. Zeeb 	int ret;
1736da8fa4e3SBjoern A. Zeeb 
1737da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
1738da8fa4e3SBjoern A. Zeeb 
1739da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_htt_stats_req(ar);
1740da8fa4e3SBjoern A. Zeeb 	if (ret)
1741da8fa4e3SBjoern A. Zeeb 		/* continue normally anyway, this isn't serious */
1742da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to start htt stats workqueue: %d\n",
1743da8fa4e3SBjoern A. Zeeb 			    ret);
1744da8fa4e3SBjoern A. Zeeb 
1745da8fa4e3SBjoern A. Zeeb 	if (ar->debug.fw_dbglog_mask) {
1746da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
1747da8fa4e3SBjoern A. Zeeb 					    ATH10K_DBGLOG_LEVEL_WARN);
1748da8fa4e3SBjoern A. Zeeb 		if (ret)
1749da8fa4e3SBjoern A. Zeeb 			/* not serious */
1750da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "failed to enable dbglog during start: %d",
1751da8fa4e3SBjoern A. Zeeb 				    ret);
1752da8fa4e3SBjoern A. Zeeb 	}
1753da8fa4e3SBjoern A. Zeeb 
1754da8fa4e3SBjoern A. Zeeb 	if (ar->pktlog_filter) {
1755da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_pktlog_enable(ar,
1756da8fa4e3SBjoern A. Zeeb 						    ar->pktlog_filter);
1757da8fa4e3SBjoern A. Zeeb 		if (ret)
1758da8fa4e3SBjoern A. Zeeb 			/* not serious */
1759da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar,
1760da8fa4e3SBjoern A. Zeeb 				    "failed to enable pktlog filter %x: %d\n",
1761da8fa4e3SBjoern A. Zeeb 				    ar->pktlog_filter, ret);
1762da8fa4e3SBjoern A. Zeeb 	} else {
1763da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_pktlog_disable(ar);
1764da8fa4e3SBjoern A. Zeeb 		if (ret)
1765da8fa4e3SBjoern A. Zeeb 			/* not serious */
1766da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
1767da8fa4e3SBjoern A. Zeeb 	}
1768da8fa4e3SBjoern A. Zeeb 
1769da8fa4e3SBjoern A. Zeeb 	if (ar->debug.nf_cal_period &&
1770da8fa4e3SBjoern A. Zeeb 	    !test_bit(ATH10K_FW_FEATURE_NON_BMI,
1771da8fa4e3SBjoern A. Zeeb 		      ar->normal_mode_fw.fw_file.fw_features)) {
1772da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_set_param(ar,
1773da8fa4e3SBjoern A. Zeeb 						ar->wmi.pdev_param->cal_period,
1774da8fa4e3SBjoern A. Zeeb 						ar->debug.nf_cal_period);
1775da8fa4e3SBjoern A. Zeeb 		if (ret)
1776da8fa4e3SBjoern A. Zeeb 			/* not serious */
1777da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "cal period cfg failed from debug start: %d\n",
1778da8fa4e3SBjoern A. Zeeb 				    ret);
1779da8fa4e3SBjoern A. Zeeb 	}
1780da8fa4e3SBjoern A. Zeeb 
1781da8fa4e3SBjoern A. Zeeb 	return ret;
1782da8fa4e3SBjoern A. Zeeb }
1783da8fa4e3SBjoern A. Zeeb 
ath10k_debug_stop(struct ath10k * ar)1784da8fa4e3SBjoern A. Zeeb void ath10k_debug_stop(struct ath10k *ar)
1785da8fa4e3SBjoern A. Zeeb {
1786da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
1787da8fa4e3SBjoern A. Zeeb 
1788da8fa4e3SBjoern A. Zeeb 	if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
1789da8fa4e3SBjoern A. Zeeb 		      ar->normal_mode_fw.fw_file.fw_features))
1790da8fa4e3SBjoern A. Zeeb 		ath10k_debug_cal_data_fetch(ar);
1791da8fa4e3SBjoern A. Zeeb 
1792da8fa4e3SBjoern A. Zeeb 	/* Must not use _sync to avoid deadlock, we do that in
1793da8fa4e3SBjoern A. Zeeb 	 * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
1794da8fa4e3SBjoern A. Zeeb 	 * warning from del_timer().
1795da8fa4e3SBjoern A. Zeeb 	 */
1796da8fa4e3SBjoern A. Zeeb 	if (ar->debug.htt_stats_mask != 0)
1797da8fa4e3SBjoern A. Zeeb 		cancel_delayed_work(&ar->debug.htt_stats_dwork);
1798da8fa4e3SBjoern A. Zeeb 
1799da8fa4e3SBjoern A. Zeeb 	ath10k_wmi_pdev_pktlog_disable(ar);
1800da8fa4e3SBjoern A. Zeeb }
1801da8fa4e3SBjoern A. Zeeb 
ath10k_write_simulate_radar(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1802da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_simulate_radar(struct file *file,
1803da8fa4e3SBjoern A. Zeeb 					   const char __user *user_buf,
1804da8fa4e3SBjoern A. Zeeb 					   size_t count, loff_t *ppos)
1805da8fa4e3SBjoern A. Zeeb {
1806da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1807da8fa4e3SBjoern A. Zeeb 	struct ath10k_vif *arvif;
1808da8fa4e3SBjoern A. Zeeb 
1809da8fa4e3SBjoern A. Zeeb 	/* Just check for the first vif alone, as all the vifs will be
1810da8fa4e3SBjoern A. Zeeb 	 * sharing the same channel and if the channel is disabled, all the
1811da8fa4e3SBjoern A. Zeeb 	 * vifs will share the same 'is_started' state.
1812da8fa4e3SBjoern A. Zeeb 	 */
1813da8fa4e3SBjoern A. Zeeb 	arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
1814da8fa4e3SBjoern A. Zeeb 	if (!arvif->is_started)
1815da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1816da8fa4e3SBjoern A. Zeeb 
1817da8fa4e3SBjoern A. Zeeb 	ieee80211_radar_detected(ar->hw);
1818da8fa4e3SBjoern A. Zeeb 
1819da8fa4e3SBjoern A. Zeeb 	return count;
1820da8fa4e3SBjoern A. Zeeb }
1821da8fa4e3SBjoern A. Zeeb 
1822da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_simulate_radar = {
1823da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_simulate_radar,
1824da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1825da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1826da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1827da8fa4e3SBjoern A. Zeeb };
1828da8fa4e3SBjoern A. Zeeb 
1829da8fa4e3SBjoern A. Zeeb #define ATH10K_DFS_STAT(s, p) (\
1830da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \
1831da8fa4e3SBjoern A. Zeeb 			 ar->debug.dfs_stats.p))
1832da8fa4e3SBjoern A. Zeeb 
1833da8fa4e3SBjoern A. Zeeb #define ATH10K_DFS_POOL_STAT(s, p) (\
1834da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \
1835da8fa4e3SBjoern A. Zeeb 			 ar->debug.dfs_pool_stats.p))
1836da8fa4e3SBjoern A. Zeeb 
ath10k_read_dfs_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1837da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_dfs_stats(struct file *file, char __user *user_buf,
1838da8fa4e3SBjoern A. Zeeb 				     size_t count, loff_t *ppos)
1839da8fa4e3SBjoern A. Zeeb {
1840da8fa4e3SBjoern A. Zeeb 	int retval = 0, len = 0;
1841da8fa4e3SBjoern A. Zeeb 	const int size = 8000;
1842da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1843da8fa4e3SBjoern A. Zeeb 	char *buf;
1844da8fa4e3SBjoern A. Zeeb 
1845da8fa4e3SBjoern A. Zeeb 	buf = kzalloc(size, GFP_KERNEL);
1846da8fa4e3SBjoern A. Zeeb 	if (buf == NULL)
1847da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
1848da8fa4e3SBjoern A. Zeeb 
1849da8fa4e3SBjoern A. Zeeb 	if (!ar->dfs_detector) {
1850da8fa4e3SBjoern A. Zeeb 		len += scnprintf(buf + len, size - len, "DFS not enabled\n");
1851da8fa4e3SBjoern A. Zeeb 		goto exit;
1852da8fa4e3SBjoern A. Zeeb 	}
1853da8fa4e3SBjoern A. Zeeb 
1854da8fa4e3SBjoern A. Zeeb 	ar->debug.dfs_pool_stats =
1855da8fa4e3SBjoern A. Zeeb 			ar->dfs_detector->get_stats(ar->dfs_detector);
1856da8fa4e3SBjoern A. Zeeb 
1857da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
1858da8fa4e3SBjoern A. Zeeb 
1859da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_STAT("reported phy errors", phy_errors);
1860da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_STAT("pulse events reported", pulses_total);
1861da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_STAT("DFS pulses detected", pulses_detected);
1862da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_STAT("DFS pulses discarded", pulses_discarded);
1863da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_STAT("Radars detected", radar_detected);
1864da8fa4e3SBjoern A. Zeeb 
1865da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");
1866da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Pool references", pool_reference);
1867da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Pulses allocated", pulse_allocated);
1868da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Pulses alloc error", pulse_alloc_error);
1869da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Pulses in use", pulse_used);
1870da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Seqs. allocated", pseq_allocated);
1871da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Seqs. alloc error", pseq_alloc_error);
1872da8fa4e3SBjoern A. Zeeb 	ATH10K_DFS_POOL_STAT("Seqs. in use", pseq_used);
1873da8fa4e3SBjoern A. Zeeb 
1874da8fa4e3SBjoern A. Zeeb exit:
1875da8fa4e3SBjoern A. Zeeb 	if (len > size)
1876da8fa4e3SBjoern A. Zeeb 		len = size;
1877da8fa4e3SBjoern A. Zeeb 
1878da8fa4e3SBjoern A. Zeeb 	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1879da8fa4e3SBjoern A. Zeeb 	kfree(buf);
1880da8fa4e3SBjoern A. Zeeb 
1881da8fa4e3SBjoern A. Zeeb 	return retval;
1882da8fa4e3SBjoern A. Zeeb }
1883da8fa4e3SBjoern A. Zeeb 
1884da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_dfs_stats = {
1885da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_dfs_stats,
1886da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
1887da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
1888da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
1889da8fa4e3SBjoern A. Zeeb };
1890da8fa4e3SBjoern A. Zeeb 
ath10k_write_pktlog_filter(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1891da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_pktlog_filter(struct file *file,
1892da8fa4e3SBjoern A. Zeeb 					  const char __user *ubuf,
1893da8fa4e3SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
1894da8fa4e3SBjoern A. Zeeb {
1895da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1896da8fa4e3SBjoern A. Zeeb 	u32 filter;
1897da8fa4e3SBjoern A. Zeeb 	int ret;
1898da8fa4e3SBjoern A. Zeeb 
1899da8fa4e3SBjoern A. Zeeb 	if (kstrtouint_from_user(ubuf, count, 0, &filter))
1900da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1901da8fa4e3SBjoern A. Zeeb 
1902da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1903da8fa4e3SBjoern A. Zeeb 
1904da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
1905da8fa4e3SBjoern A. Zeeb 		ar->pktlog_filter = filter;
1906da8fa4e3SBjoern A. Zeeb 		ret = count;
1907da8fa4e3SBjoern A. Zeeb 		goto out;
1908da8fa4e3SBjoern A. Zeeb 	}
1909da8fa4e3SBjoern A. Zeeb 
1910da8fa4e3SBjoern A. Zeeb 	if (filter == ar->pktlog_filter) {
1911da8fa4e3SBjoern A. Zeeb 		ret = count;
1912da8fa4e3SBjoern A. Zeeb 		goto out;
1913da8fa4e3SBjoern A. Zeeb 	}
1914da8fa4e3SBjoern A. Zeeb 
1915da8fa4e3SBjoern A. Zeeb 	if (filter) {
1916da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
1917da8fa4e3SBjoern A. Zeeb 		if (ret) {
1918da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
1919da8fa4e3SBjoern A. Zeeb 				    ar->pktlog_filter, ret);
1920da8fa4e3SBjoern A. Zeeb 			goto out;
1921da8fa4e3SBjoern A. Zeeb 		}
1922da8fa4e3SBjoern A. Zeeb 	} else {
1923da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_pktlog_disable(ar);
1924da8fa4e3SBjoern A. Zeeb 		if (ret) {
1925da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
1926da8fa4e3SBjoern A. Zeeb 			goto out;
1927da8fa4e3SBjoern A. Zeeb 		}
1928da8fa4e3SBjoern A. Zeeb 	}
1929da8fa4e3SBjoern A. Zeeb 
1930da8fa4e3SBjoern A. Zeeb 	ar->pktlog_filter = filter;
1931da8fa4e3SBjoern A. Zeeb 	ret = count;
1932da8fa4e3SBjoern A. Zeeb 
1933da8fa4e3SBjoern A. Zeeb out:
1934da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1935da8fa4e3SBjoern A. Zeeb 	return ret;
1936da8fa4e3SBjoern A. Zeeb }
1937da8fa4e3SBjoern A. Zeeb 
ath10k_read_pktlog_filter(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)1938da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
1939da8fa4e3SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
1940da8fa4e3SBjoern A. Zeeb {
1941da8fa4e3SBjoern A. Zeeb 	char buf[32];
1942da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1943da8fa4e3SBjoern A. Zeeb 	int len = 0;
1944da8fa4e3SBjoern A. Zeeb 
1945da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1946da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
1947da8fa4e3SBjoern A. Zeeb 			ar->pktlog_filter);
1948da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1949da8fa4e3SBjoern A. Zeeb 
1950da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
1951da8fa4e3SBjoern A. Zeeb }
1952da8fa4e3SBjoern A. Zeeb 
1953da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_pktlog_filter = {
1954da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_pktlog_filter,
1955da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_pktlog_filter,
1956da8fa4e3SBjoern A. Zeeb 	.open = simple_open
1957da8fa4e3SBjoern A. Zeeb };
1958da8fa4e3SBjoern A. Zeeb 
ath10k_write_quiet_period(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1959da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_quiet_period(struct file *file,
1960da8fa4e3SBjoern A. Zeeb 					 const char __user *ubuf,
1961da8fa4e3SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
1962da8fa4e3SBjoern A. Zeeb {
1963da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1964da8fa4e3SBjoern A. Zeeb 	u32 period;
1965da8fa4e3SBjoern A. Zeeb 
1966da8fa4e3SBjoern A. Zeeb 	if (kstrtouint_from_user(ubuf, count, 0, &period))
1967da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1968da8fa4e3SBjoern A. Zeeb 
1969da8fa4e3SBjoern A. Zeeb 	if (period < ATH10K_QUIET_PERIOD_MIN) {
1970da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "Quiet period %u can not be lesser than 25ms\n",
1971da8fa4e3SBjoern A. Zeeb 			    period);
1972da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
1973da8fa4e3SBjoern A. Zeeb 	}
1974da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1975da8fa4e3SBjoern A. Zeeb 	ar->thermal.quiet_period = period;
1976da8fa4e3SBjoern A. Zeeb 	ath10k_thermal_set_throttling(ar);
1977da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1978da8fa4e3SBjoern A. Zeeb 
1979da8fa4e3SBjoern A. Zeeb 	return count;
1980da8fa4e3SBjoern A. Zeeb }
1981da8fa4e3SBjoern A. Zeeb 
ath10k_read_quiet_period(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)1982da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_quiet_period(struct file *file, char __user *ubuf,
1983da8fa4e3SBjoern A. Zeeb 					size_t count, loff_t *ppos)
1984da8fa4e3SBjoern A. Zeeb {
1985da8fa4e3SBjoern A. Zeeb 	char buf[32];
1986da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
1987da8fa4e3SBjoern A. Zeeb 	int len = 0;
1988da8fa4e3SBjoern A. Zeeb 
1989da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
1990da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
1991da8fa4e3SBjoern A. Zeeb 			ar->thermal.quiet_period);
1992da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
1993da8fa4e3SBjoern A. Zeeb 
1994da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
1995da8fa4e3SBjoern A. Zeeb }
1996da8fa4e3SBjoern A. Zeeb 
1997da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_quiet_period = {
1998da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_quiet_period,
1999da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_quiet_period,
2000da8fa4e3SBjoern A. Zeeb 	.open = simple_open
2001da8fa4e3SBjoern A. Zeeb };
2002da8fa4e3SBjoern A. Zeeb 
ath10k_write_btcoex(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)2003da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_btcoex(struct file *file,
2004da8fa4e3SBjoern A. Zeeb 				   const char __user *ubuf,
2005da8fa4e3SBjoern A. Zeeb 				   size_t count, loff_t *ppos)
2006da8fa4e3SBjoern A. Zeeb {
2007da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2008da8fa4e3SBjoern A. Zeeb 	char buf[32];
2009da8fa4e3SBjoern A. Zeeb 	size_t buf_size;
2010da8fa4e3SBjoern A. Zeeb 	int ret;
2011da8fa4e3SBjoern A. Zeeb 	bool val;
2012da8fa4e3SBjoern A. Zeeb 	u32 pdev_param;
2013da8fa4e3SBjoern A. Zeeb 
2014da8fa4e3SBjoern A. Zeeb 	buf_size = min(count, (sizeof(buf) - 1));
2015da8fa4e3SBjoern A. Zeeb 	if (copy_from_user(buf, ubuf, buf_size))
2016da8fa4e3SBjoern A. Zeeb 		return -EFAULT;
2017da8fa4e3SBjoern A. Zeeb 
2018da8fa4e3SBjoern A. Zeeb 	buf[buf_size] = '\0';
2019da8fa4e3SBjoern A. Zeeb 
2020*07724ba6SBjoern A. Zeeb 	if (kstrtobool(buf, &val) != 0)
2021da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2022da8fa4e3SBjoern A. Zeeb 
2023da8fa4e3SBjoern A. Zeeb 	if (!ar->coex_support)
2024da8fa4e3SBjoern A. Zeeb 		return -EOPNOTSUPP;
2025da8fa4e3SBjoern A. Zeeb 
2026da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2027da8fa4e3SBjoern A. Zeeb 
2028da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
2029da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_RESTARTED) {
2030da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
2031da8fa4e3SBjoern A. Zeeb 		goto exit;
2032da8fa4e3SBjoern A. Zeeb 	}
2033da8fa4e3SBjoern A. Zeeb 
2034da8fa4e3SBjoern A. Zeeb 	if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val)) {
2035da8fa4e3SBjoern A. Zeeb 		ret = count;
2036da8fa4e3SBjoern A. Zeeb 		goto exit;
2037da8fa4e3SBjoern A. Zeeb 	}
2038da8fa4e3SBjoern A. Zeeb 
2039da8fa4e3SBjoern A. Zeeb 	pdev_param = ar->wmi.pdev_param->enable_btcoex;
2040da8fa4e3SBjoern A. Zeeb 	if (test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,
2041da8fa4e3SBjoern A. Zeeb 		     ar->running_fw->fw_file.fw_features)) {
2042da8fa4e3SBjoern A. Zeeb 		ret = ath10k_wmi_pdev_set_param(ar, pdev_param, val);
2043da8fa4e3SBjoern A. Zeeb 		if (ret) {
2044da8fa4e3SBjoern A. Zeeb 			ath10k_warn(ar, "failed to enable btcoex: %d\n", ret);
2045da8fa4e3SBjoern A. Zeeb 			ret = count;
2046da8fa4e3SBjoern A. Zeeb 			goto exit;
2047da8fa4e3SBjoern A. Zeeb 		}
2048da8fa4e3SBjoern A. Zeeb 	} else {
2049da8fa4e3SBjoern A. Zeeb 		ath10k_info(ar, "restarting firmware due to btcoex change");
2050da8fa4e3SBjoern A. Zeeb 		ath10k_core_start_recovery(ar);
2051da8fa4e3SBjoern A. Zeeb 	}
2052da8fa4e3SBjoern A. Zeeb 
2053da8fa4e3SBjoern A. Zeeb 	if (val)
2054da8fa4e3SBjoern A. Zeeb 		set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
2055da8fa4e3SBjoern A. Zeeb 	else
2056da8fa4e3SBjoern A. Zeeb 		clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
2057da8fa4e3SBjoern A. Zeeb 
2058da8fa4e3SBjoern A. Zeeb 	ret = count;
2059da8fa4e3SBjoern A. Zeeb 
2060da8fa4e3SBjoern A. Zeeb exit:
2061da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2062da8fa4e3SBjoern A. Zeeb 
2063da8fa4e3SBjoern A. Zeeb 	return ret;
2064da8fa4e3SBjoern A. Zeeb }
2065da8fa4e3SBjoern A. Zeeb 
ath10k_read_btcoex(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)2066da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,
2067da8fa4e3SBjoern A. Zeeb 				  size_t count, loff_t *ppos)
2068da8fa4e3SBjoern A. Zeeb {
2069da8fa4e3SBjoern A. Zeeb 	char buf[32];
2070da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2071da8fa4e3SBjoern A. Zeeb 	int len = 0;
2072da8fa4e3SBjoern A. Zeeb 
2073da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2074da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
2075da8fa4e3SBjoern A. Zeeb 			test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags));
2076da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2077da8fa4e3SBjoern A. Zeeb 
2078da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
2079da8fa4e3SBjoern A. Zeeb }
2080da8fa4e3SBjoern A. Zeeb 
2081da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_btcoex = {
2082da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_btcoex,
2083da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_btcoex,
2084da8fa4e3SBjoern A. Zeeb 	.open = simple_open
2085da8fa4e3SBjoern A. Zeeb };
2086da8fa4e3SBjoern A. Zeeb 
ath10k_write_enable_extd_tx_stats(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)2087da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_enable_extd_tx_stats(struct file *file,
2088da8fa4e3SBjoern A. Zeeb 						 const char __user *ubuf,
2089da8fa4e3SBjoern A. Zeeb 						 size_t count, loff_t *ppos)
2090da8fa4e3SBjoern A. Zeeb {
2091da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2092da8fa4e3SBjoern A. Zeeb 	u32 filter;
2093da8fa4e3SBjoern A. Zeeb 	int ret;
2094da8fa4e3SBjoern A. Zeeb 
2095da8fa4e3SBjoern A. Zeeb 	if (kstrtouint_from_user(ubuf, count, 0, &filter))
2096da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2097da8fa4e3SBjoern A. Zeeb 
2098da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2099da8fa4e3SBjoern A. Zeeb 
2100da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
2101da8fa4e3SBjoern A. Zeeb 		ar->debug.enable_extd_tx_stats = filter;
2102da8fa4e3SBjoern A. Zeeb 		ret = count;
2103da8fa4e3SBjoern A. Zeeb 		goto out;
2104da8fa4e3SBjoern A. Zeeb 	}
2105da8fa4e3SBjoern A. Zeeb 
2106da8fa4e3SBjoern A. Zeeb 	if (filter == ar->debug.enable_extd_tx_stats) {
2107da8fa4e3SBjoern A. Zeeb 		ret = count;
2108da8fa4e3SBjoern A. Zeeb 		goto out;
2109da8fa4e3SBjoern A. Zeeb 	}
2110da8fa4e3SBjoern A. Zeeb 
2111da8fa4e3SBjoern A. Zeeb 	ar->debug.enable_extd_tx_stats = filter;
2112da8fa4e3SBjoern A. Zeeb 	ret = count;
2113da8fa4e3SBjoern A. Zeeb 
2114da8fa4e3SBjoern A. Zeeb out:
2115da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2116da8fa4e3SBjoern A. Zeeb 	return ret;
2117da8fa4e3SBjoern A. Zeeb }
2118da8fa4e3SBjoern A. Zeeb 
ath10k_read_enable_extd_tx_stats(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)2119da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_enable_extd_tx_stats(struct file *file,
2120da8fa4e3SBjoern A. Zeeb 						char __user *ubuf,
2121da8fa4e3SBjoern A. Zeeb 						size_t count, loff_t *ppos)
2122da8fa4e3SBjoern A. Zeeb 
2123da8fa4e3SBjoern A. Zeeb {
2124da8fa4e3SBjoern A. Zeeb 	char buf[32];
2125da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2126da8fa4e3SBjoern A. Zeeb 	int len = 0;
2127da8fa4e3SBjoern A. Zeeb 
2128da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2129da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
2130da8fa4e3SBjoern A. Zeeb 			ar->debug.enable_extd_tx_stats);
2131da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2132da8fa4e3SBjoern A. Zeeb 
2133da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
2134da8fa4e3SBjoern A. Zeeb }
2135da8fa4e3SBjoern A. Zeeb 
2136da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_enable_extd_tx_stats = {
2137da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_enable_extd_tx_stats,
2138da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_enable_extd_tx_stats,
2139da8fa4e3SBjoern A. Zeeb 	.open = simple_open
2140da8fa4e3SBjoern A. Zeeb };
2141da8fa4e3SBjoern A. Zeeb 
ath10k_write_peer_stats(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)2142da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_peer_stats(struct file *file,
2143da8fa4e3SBjoern A. Zeeb 				       const char __user *ubuf,
2144da8fa4e3SBjoern A. Zeeb 				       size_t count, loff_t *ppos)
2145da8fa4e3SBjoern A. Zeeb {
2146da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2147da8fa4e3SBjoern A. Zeeb 	char buf[32];
2148da8fa4e3SBjoern A. Zeeb 	size_t buf_size;
2149da8fa4e3SBjoern A. Zeeb 	int ret;
2150da8fa4e3SBjoern A. Zeeb 	bool val;
2151da8fa4e3SBjoern A. Zeeb 
2152da8fa4e3SBjoern A. Zeeb 	buf_size = min(count, (sizeof(buf) - 1));
2153da8fa4e3SBjoern A. Zeeb 	if (copy_from_user(buf, ubuf, buf_size))
2154da8fa4e3SBjoern A. Zeeb 		return -EFAULT;
2155da8fa4e3SBjoern A. Zeeb 
2156da8fa4e3SBjoern A. Zeeb 	buf[buf_size] = '\0';
2157da8fa4e3SBjoern A. Zeeb 
2158*07724ba6SBjoern A. Zeeb 	if (kstrtobool(buf, &val) != 0)
2159da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2160da8fa4e3SBjoern A. Zeeb 
2161da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2162da8fa4e3SBjoern A. Zeeb 
2163da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON &&
2164da8fa4e3SBjoern A. Zeeb 	    ar->state != ATH10K_STATE_RESTARTED) {
2165da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
2166da8fa4e3SBjoern A. Zeeb 		goto exit;
2167da8fa4e3SBjoern A. Zeeb 	}
2168da8fa4e3SBjoern A. Zeeb 
2169da8fa4e3SBjoern A. Zeeb 	if (!(test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags) ^ val)) {
2170da8fa4e3SBjoern A. Zeeb 		ret = count;
2171da8fa4e3SBjoern A. Zeeb 		goto exit;
2172da8fa4e3SBjoern A. Zeeb 	}
2173da8fa4e3SBjoern A. Zeeb 
2174da8fa4e3SBjoern A. Zeeb 	if (val)
2175da8fa4e3SBjoern A. Zeeb 		set_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);
2176da8fa4e3SBjoern A. Zeeb 	else
2177da8fa4e3SBjoern A. Zeeb 		clear_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);
2178da8fa4e3SBjoern A. Zeeb 
2179da8fa4e3SBjoern A. Zeeb 	ath10k_info(ar, "restarting firmware due to Peer stats change");
2180da8fa4e3SBjoern A. Zeeb 
2181da8fa4e3SBjoern A. Zeeb 	ath10k_core_start_recovery(ar);
2182da8fa4e3SBjoern A. Zeeb 	ret = count;
2183da8fa4e3SBjoern A. Zeeb 
2184da8fa4e3SBjoern A. Zeeb exit:
2185da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2186da8fa4e3SBjoern A. Zeeb 	return ret;
2187da8fa4e3SBjoern A. Zeeb }
2188da8fa4e3SBjoern A. Zeeb 
ath10k_read_peer_stats(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)2189da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_peer_stats(struct file *file, char __user *ubuf,
2190da8fa4e3SBjoern A. Zeeb 				      size_t count, loff_t *ppos)
2191da8fa4e3SBjoern A. Zeeb 
2192da8fa4e3SBjoern A. Zeeb {
2193da8fa4e3SBjoern A. Zeeb 	char buf[32];
2194da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2195da8fa4e3SBjoern A. Zeeb 	int len = 0;
2196da8fa4e3SBjoern A. Zeeb 
2197da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2198da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
2199da8fa4e3SBjoern A. Zeeb 			test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags));
2200da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2201da8fa4e3SBjoern A. Zeeb 
2202da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
2203da8fa4e3SBjoern A. Zeeb }
2204da8fa4e3SBjoern A. Zeeb 
2205da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_peer_stats = {
2206da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_peer_stats,
2207da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_peer_stats,
2208da8fa4e3SBjoern A. Zeeb 	.open = simple_open
2209da8fa4e3SBjoern A. Zeeb };
2210da8fa4e3SBjoern A. Zeeb 
ath10k_debug_fw_checksums_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2211da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_debug_fw_checksums_read(struct file *file,
2212da8fa4e3SBjoern A. Zeeb 					      char __user *user_buf,
2213da8fa4e3SBjoern A. Zeeb 					      size_t count, loff_t *ppos)
2214da8fa4e3SBjoern A. Zeeb {
2215da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2216da8fa4e3SBjoern A. Zeeb 	size_t len = 0, buf_len = 4096;
2217da8fa4e3SBjoern A. Zeeb 	ssize_t ret_cnt;
2218da8fa4e3SBjoern A. Zeeb 	char *buf;
2219da8fa4e3SBjoern A. Zeeb 
2220da8fa4e3SBjoern A. Zeeb 	buf = kzalloc(buf_len, GFP_KERNEL);
2221da8fa4e3SBjoern A. Zeeb 	if (!buf)
2222da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
2223da8fa4e3SBjoern A. Zeeb 
2224da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2225da8fa4e3SBjoern A. Zeeb 
2226da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2227da8fa4e3SBjoern A. Zeeb 			 "firmware-N.bin\t\t%08x\n",
2228da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.fw_file.firmware->data,
2229da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.fw_file.firmware->size));
2230da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2231da8fa4e3SBjoern A. Zeeb 			 "athwlan\t\t\t%08x\n",
2232da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.fw_file.firmware_data,
2233da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.fw_file.firmware_len));
2234da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2235da8fa4e3SBjoern A. Zeeb 			 "otp\t\t\t%08x\n",
2236da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.fw_file.otp_data,
2237da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.fw_file.otp_len));
2238da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2239da8fa4e3SBjoern A. Zeeb 			 "codeswap\t\t%08x\n",
2240da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.fw_file.codeswap_data,
2241da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.fw_file.codeswap_len));
2242da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2243da8fa4e3SBjoern A. Zeeb 			 "board-N.bin\t\t%08x\n",
2244da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.board->data,
2245da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.board->size));
2246da8fa4e3SBjoern A. Zeeb 	len += scnprintf(buf + len, buf_len - len,
2247da8fa4e3SBjoern A. Zeeb 			 "board\t\t\t%08x\n",
2248da8fa4e3SBjoern A. Zeeb 			 crc32_le(0, ar->normal_mode_fw.board_data,
2249da8fa4e3SBjoern A. Zeeb 				  ar->normal_mode_fw.board_len));
2250da8fa4e3SBjoern A. Zeeb 
2251da8fa4e3SBjoern A. Zeeb 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
2252da8fa4e3SBjoern A. Zeeb 
2253da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2254da8fa4e3SBjoern A. Zeeb 
2255da8fa4e3SBjoern A. Zeeb 	kfree(buf);
2256da8fa4e3SBjoern A. Zeeb 	return ret_cnt;
2257da8fa4e3SBjoern A. Zeeb }
2258da8fa4e3SBjoern A. Zeeb 
2259da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_fw_checksums = {
2260da8fa4e3SBjoern A. Zeeb 	.read = ath10k_debug_fw_checksums_read,
2261da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
2262da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2263da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2264da8fa4e3SBjoern A. Zeeb };
2265da8fa4e3SBjoern A. Zeeb 
ath10k_sta_tid_stats_mask_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2266da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file,
2267da8fa4e3SBjoern A. Zeeb 					      char __user *user_buf,
2268da8fa4e3SBjoern A. Zeeb 					      size_t count, loff_t *ppos)
2269da8fa4e3SBjoern A. Zeeb {
2270da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2271da8fa4e3SBjoern A. Zeeb 	char buf[32];
2272da8fa4e3SBjoern A. Zeeb 	size_t len;
2273da8fa4e3SBjoern A. Zeeb 
2274da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask);
2275da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
2276da8fa4e3SBjoern A. Zeeb }
2277da8fa4e3SBjoern A. Zeeb 
ath10k_sta_tid_stats_mask_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)2278da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file,
2279da8fa4e3SBjoern A. Zeeb 					       const char __user *user_buf,
2280da8fa4e3SBjoern A. Zeeb 					       size_t count, loff_t *ppos)
2281da8fa4e3SBjoern A. Zeeb {
2282da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2283da8fa4e3SBjoern A. Zeeb 	char buf[32];
2284da8fa4e3SBjoern A. Zeeb 	ssize_t len;
2285da8fa4e3SBjoern A. Zeeb 	u32 mask;
2286da8fa4e3SBjoern A. Zeeb 
2287da8fa4e3SBjoern A. Zeeb 	len = min(count, sizeof(buf) - 1);
2288da8fa4e3SBjoern A. Zeeb 	if (copy_from_user(buf, user_buf, len))
2289da8fa4e3SBjoern A. Zeeb 		return -EFAULT;
2290da8fa4e3SBjoern A. Zeeb 
2291da8fa4e3SBjoern A. Zeeb 	buf[len] = '\0';
2292da8fa4e3SBjoern A. Zeeb 	if (kstrtoint(buf, 0, &mask))
2293da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2294da8fa4e3SBjoern A. Zeeb 
2295da8fa4e3SBjoern A. Zeeb 	ar->sta_tid_stats_mask = mask;
2296da8fa4e3SBjoern A. Zeeb 
2297da8fa4e3SBjoern A. Zeeb 	return len;
2298da8fa4e3SBjoern A. Zeeb }
2299da8fa4e3SBjoern A. Zeeb 
2300da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_sta_tid_stats_mask = {
2301da8fa4e3SBjoern A. Zeeb 	.read = ath10k_sta_tid_stats_mask_read,
2302da8fa4e3SBjoern A. Zeeb 	.write = ath10k_sta_tid_stats_mask_write,
2303da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
2304da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2305da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2306da8fa4e3SBjoern A. Zeeb };
2307da8fa4e3SBjoern A. Zeeb 
ath10k_debug_tpc_stats_final_request(struct ath10k * ar)2308da8fa4e3SBjoern A. Zeeb static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar)
2309da8fa4e3SBjoern A. Zeeb {
2310da8fa4e3SBjoern A. Zeeb 	int ret;
2311da8fa4e3SBjoern A. Zeeb 	unsigned long time_left;
2312da8fa4e3SBjoern A. Zeeb 
2313da8fa4e3SBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
2314da8fa4e3SBjoern A. Zeeb 
2315da8fa4e3SBjoern A. Zeeb 	reinit_completion(&ar->debug.tpc_complete);
2316da8fa4e3SBjoern A. Zeeb 
2317da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM);
2318da8fa4e3SBjoern A. Zeeb 	if (ret) {
2319da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret);
2320da8fa4e3SBjoern A. Zeeb 		return ret;
2321da8fa4e3SBjoern A. Zeeb 	}
2322da8fa4e3SBjoern A. Zeeb 
2323da8fa4e3SBjoern A. Zeeb 	time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,
2324da8fa4e3SBjoern A. Zeeb 						1 * HZ);
2325da8fa4e3SBjoern A. Zeeb 	if (time_left == 0)
2326da8fa4e3SBjoern A. Zeeb 		return -ETIMEDOUT;
2327da8fa4e3SBjoern A. Zeeb 
2328da8fa4e3SBjoern A. Zeeb 	return 0;
2329da8fa4e3SBjoern A. Zeeb }
2330da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_final_open(struct inode * inode,struct file * file)2331da8fa4e3SBjoern A. Zeeb static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file)
2332da8fa4e3SBjoern A. Zeeb {
2333da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = inode->i_private;
2334da8fa4e3SBjoern A. Zeeb 	void *buf;
2335da8fa4e3SBjoern A. Zeeb 	int ret;
2336da8fa4e3SBjoern A. Zeeb 
2337da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2338da8fa4e3SBjoern A. Zeeb 
2339da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
2340da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
2341da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
2342da8fa4e3SBjoern A. Zeeb 	}
2343da8fa4e3SBjoern A. Zeeb 
2344da8fa4e3SBjoern A. Zeeb 	buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);
2345da8fa4e3SBjoern A. Zeeb 	if (!buf) {
2346da8fa4e3SBjoern A. Zeeb 		ret = -ENOMEM;
2347da8fa4e3SBjoern A. Zeeb 		goto err_unlock;
2348da8fa4e3SBjoern A. Zeeb 	}
2349da8fa4e3SBjoern A. Zeeb 
2350da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_tpc_stats_final_request(ar);
2351da8fa4e3SBjoern A. Zeeb 	if (ret) {
2352da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to request tpc stats final: %d\n",
2353da8fa4e3SBjoern A. Zeeb 			    ret);
2354da8fa4e3SBjoern A. Zeeb 		goto err_free;
2355da8fa4e3SBjoern A. Zeeb 	}
2356da8fa4e3SBjoern A. Zeeb 
2357da8fa4e3SBjoern A. Zeeb 	ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
2358da8fa4e3SBjoern A. Zeeb 	file->private_data = buf;
2359da8fa4e3SBjoern A. Zeeb 
2360da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2361da8fa4e3SBjoern A. Zeeb 	return 0;
2362da8fa4e3SBjoern A. Zeeb 
2363da8fa4e3SBjoern A. Zeeb err_free:
2364da8fa4e3SBjoern A. Zeeb 	vfree(buf);
2365da8fa4e3SBjoern A. Zeeb 
2366da8fa4e3SBjoern A. Zeeb err_unlock:
2367da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2368da8fa4e3SBjoern A. Zeeb 	return ret;
2369da8fa4e3SBjoern A. Zeeb }
2370da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_final_release(struct inode * inode,struct file * file)2371da8fa4e3SBjoern A. Zeeb static int ath10k_tpc_stats_final_release(struct inode *inode,
2372da8fa4e3SBjoern A. Zeeb 					  struct file *file)
2373da8fa4e3SBjoern A. Zeeb {
2374da8fa4e3SBjoern A. Zeeb 	vfree(file->private_data);
2375da8fa4e3SBjoern A. Zeeb 
2376da8fa4e3SBjoern A. Zeeb 	return 0;
2377da8fa4e3SBjoern A. Zeeb }
2378da8fa4e3SBjoern A. Zeeb 
ath10k_tpc_stats_final_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2379da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_tpc_stats_final_read(struct file *file,
2380da8fa4e3SBjoern A. Zeeb 					   char __user *user_buf,
2381da8fa4e3SBjoern A. Zeeb 					   size_t count, loff_t *ppos)
2382da8fa4e3SBjoern A. Zeeb {
2383da8fa4e3SBjoern A. Zeeb 	const char *buf = file->private_data;
2384da8fa4e3SBjoern A. Zeeb 	unsigned int len = strlen(buf);
2385da8fa4e3SBjoern A. Zeeb 
2386da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
2387da8fa4e3SBjoern A. Zeeb }
2388da8fa4e3SBjoern A. Zeeb 
2389da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_tpc_stats_final = {
2390da8fa4e3SBjoern A. Zeeb 	.open = ath10k_tpc_stats_final_open,
2391da8fa4e3SBjoern A. Zeeb 	.release = ath10k_tpc_stats_final_release,
2392da8fa4e3SBjoern A. Zeeb 	.read = ath10k_tpc_stats_final_read,
2393da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2394da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2395da8fa4e3SBjoern A. Zeeb };
2396da8fa4e3SBjoern A. Zeeb 
ath10k_write_warm_hw_reset(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)2397da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_warm_hw_reset(struct file *file,
2398da8fa4e3SBjoern A. Zeeb 					  const char __user *user_buf,
2399da8fa4e3SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
2400da8fa4e3SBjoern A. Zeeb {
2401da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2402da8fa4e3SBjoern A. Zeeb 	int ret;
2403da8fa4e3SBjoern A. Zeeb 	bool val;
2404da8fa4e3SBjoern A. Zeeb 
2405da8fa4e3SBjoern A. Zeeb 	if (kstrtobool_from_user(user_buf, count, &val))
2406da8fa4e3SBjoern A. Zeeb 		return -EFAULT;
2407da8fa4e3SBjoern A. Zeeb 
2408da8fa4e3SBjoern A. Zeeb 	if (!val)
2409da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2410da8fa4e3SBjoern A. Zeeb 
2411da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2412da8fa4e3SBjoern A. Zeeb 
2413da8fa4e3SBjoern A. Zeeb 	if (ar->state != ATH10K_STATE_ON) {
2414da8fa4e3SBjoern A. Zeeb 		ret = -ENETDOWN;
2415da8fa4e3SBjoern A. Zeeb 		goto exit;
2416da8fa4e3SBjoern A. Zeeb 	}
2417da8fa4e3SBjoern A. Zeeb 
2418da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pdev_reset,
2419da8fa4e3SBjoern A. Zeeb 					WMI_RST_MODE_WARM_RESET);
2420da8fa4e3SBjoern A. Zeeb 
2421da8fa4e3SBjoern A. Zeeb 	if (ret) {
2422da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to enable warm hw reset: %d\n", ret);
2423da8fa4e3SBjoern A. Zeeb 		goto exit;
2424da8fa4e3SBjoern A. Zeeb 	}
2425da8fa4e3SBjoern A. Zeeb 
2426da8fa4e3SBjoern A. Zeeb 	ret = count;
2427da8fa4e3SBjoern A. Zeeb 
2428da8fa4e3SBjoern A. Zeeb exit:
2429da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2430da8fa4e3SBjoern A. Zeeb 	return ret;
2431da8fa4e3SBjoern A. Zeeb }
2432da8fa4e3SBjoern A. Zeeb 
2433da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_warm_hw_reset = {
2434da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_warm_hw_reset,
2435da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
2436da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2437da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2438da8fa4e3SBjoern A. Zeeb };
2439da8fa4e3SBjoern A. Zeeb 
ath10k_peer_ps_state_disable(void * data,struct ieee80211_sta * sta)2440da8fa4e3SBjoern A. Zeeb static void ath10k_peer_ps_state_disable(void *data,
2441da8fa4e3SBjoern A. Zeeb 					 struct ieee80211_sta *sta)
2442da8fa4e3SBjoern A. Zeeb {
2443da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = data;
2444da8fa4e3SBjoern A. Zeeb 	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
2445da8fa4e3SBjoern A. Zeeb 
2446da8fa4e3SBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
2447da8fa4e3SBjoern A. Zeeb 	arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
2448da8fa4e3SBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
2449da8fa4e3SBjoern A. Zeeb }
2450da8fa4e3SBjoern A. Zeeb 
ath10k_write_ps_state_enable(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)2451da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_ps_state_enable(struct file *file,
2452da8fa4e3SBjoern A. Zeeb 					    const char __user *user_buf,
2453da8fa4e3SBjoern A. Zeeb 					    size_t count, loff_t *ppos)
2454da8fa4e3SBjoern A. Zeeb {
2455da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2456da8fa4e3SBjoern A. Zeeb 	int ret;
2457da8fa4e3SBjoern A. Zeeb 	u32 param;
2458da8fa4e3SBjoern A. Zeeb 	u8 ps_state_enable;
2459da8fa4e3SBjoern A. Zeeb 
2460da8fa4e3SBjoern A. Zeeb 	if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))
2461da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2462da8fa4e3SBjoern A. Zeeb 
2463da8fa4e3SBjoern A. Zeeb 	if (ps_state_enable > 1)
2464da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2465da8fa4e3SBjoern A. Zeeb 
2466da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2467da8fa4e3SBjoern A. Zeeb 
2468da8fa4e3SBjoern A. Zeeb 	if (ar->ps_state_enable == ps_state_enable) {
2469da8fa4e3SBjoern A. Zeeb 		ret = count;
2470da8fa4e3SBjoern A. Zeeb 		goto exit;
2471da8fa4e3SBjoern A. Zeeb 	}
2472da8fa4e3SBjoern A. Zeeb 
2473da8fa4e3SBjoern A. Zeeb 	param = ar->wmi.pdev_param->peer_sta_ps_statechg_enable;
2474da8fa4e3SBjoern A. Zeeb 	ret = ath10k_wmi_pdev_set_param(ar, param, ps_state_enable);
2475da8fa4e3SBjoern A. Zeeb 	if (ret) {
2476da8fa4e3SBjoern A. Zeeb 		ath10k_warn(ar, "failed to enable ps_state_enable: %d\n",
2477da8fa4e3SBjoern A. Zeeb 			    ret);
2478da8fa4e3SBjoern A. Zeeb 		goto exit;
2479da8fa4e3SBjoern A. Zeeb 	}
2480da8fa4e3SBjoern A. Zeeb 	ar->ps_state_enable = ps_state_enable;
2481da8fa4e3SBjoern A. Zeeb 
2482da8fa4e3SBjoern A. Zeeb 	if (!ar->ps_state_enable)
2483da8fa4e3SBjoern A. Zeeb 		ieee80211_iterate_stations_atomic(ar->hw,
2484da8fa4e3SBjoern A. Zeeb 						  ath10k_peer_ps_state_disable,
2485da8fa4e3SBjoern A. Zeeb 						  ar);
2486da8fa4e3SBjoern A. Zeeb 
2487da8fa4e3SBjoern A. Zeeb 	ret = count;
2488da8fa4e3SBjoern A. Zeeb 
2489da8fa4e3SBjoern A. Zeeb exit:
2490da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2491da8fa4e3SBjoern A. Zeeb 
2492da8fa4e3SBjoern A. Zeeb 	return ret;
2493da8fa4e3SBjoern A. Zeeb }
2494da8fa4e3SBjoern A. Zeeb 
ath10k_read_ps_state_enable(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2495da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_read_ps_state_enable(struct file *file,
2496da8fa4e3SBjoern A. Zeeb 					   char __user *user_buf,
2497da8fa4e3SBjoern A. Zeeb 					   size_t count, loff_t *ppos)
2498da8fa4e3SBjoern A. Zeeb {
2499da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2500da8fa4e3SBjoern A. Zeeb 	int len = 0;
2501da8fa4e3SBjoern A. Zeeb 	char buf[32];
2502da8fa4e3SBjoern A. Zeeb 
2503da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2504da8fa4e3SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
2505da8fa4e3SBjoern A. Zeeb 			ar->ps_state_enable);
2506da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2507da8fa4e3SBjoern A. Zeeb 
2508da8fa4e3SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
2509da8fa4e3SBjoern A. Zeeb }
2510da8fa4e3SBjoern A. Zeeb 
2511da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_ps_state_enable = {
2512da8fa4e3SBjoern A. Zeeb 	.read = ath10k_read_ps_state_enable,
2513da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_ps_state_enable,
2514da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
2515da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2516da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2517da8fa4e3SBjoern A. Zeeb };
2518da8fa4e3SBjoern A. Zeeb 
ath10k_write_reset_htt_stats(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)2519da8fa4e3SBjoern A. Zeeb static ssize_t ath10k_write_reset_htt_stats(struct file *file,
2520da8fa4e3SBjoern A. Zeeb 					    const char __user *user_buf,
2521da8fa4e3SBjoern A. Zeeb 					    size_t count, loff_t *ppos)
2522da8fa4e3SBjoern A. Zeeb {
2523da8fa4e3SBjoern A. Zeeb 	struct ath10k *ar = file->private_data;
2524da8fa4e3SBjoern A. Zeeb 	unsigned long reset;
2525da8fa4e3SBjoern A. Zeeb 	int ret;
2526da8fa4e3SBjoern A. Zeeb 
2527da8fa4e3SBjoern A. Zeeb 	ret = kstrtoul_from_user(user_buf, count, 0, &reset);
2528da8fa4e3SBjoern A. Zeeb 	if (ret)
2529da8fa4e3SBjoern A. Zeeb 		return ret;
2530da8fa4e3SBjoern A. Zeeb 
2531da8fa4e3SBjoern A. Zeeb 	if (reset == 0 || reset > 0x1ffff)
2532da8fa4e3SBjoern A. Zeeb 		return -EINVAL;
2533da8fa4e3SBjoern A. Zeeb 
2534da8fa4e3SBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
2535da8fa4e3SBjoern A. Zeeb 
2536da8fa4e3SBjoern A. Zeeb 	ar->debug.reset_htt_stats = reset;
2537da8fa4e3SBjoern A. Zeeb 
2538da8fa4e3SBjoern A. Zeeb 	ret = ath10k_debug_htt_stats_req(ar);
2539da8fa4e3SBjoern A. Zeeb 	if (ret)
2540da8fa4e3SBjoern A. Zeeb 		goto out;
2541da8fa4e3SBjoern A. Zeeb 
2542da8fa4e3SBjoern A. Zeeb 	ar->debug.reset_htt_stats = 0;
2543da8fa4e3SBjoern A. Zeeb 	ret = count;
2544da8fa4e3SBjoern A. Zeeb 
2545da8fa4e3SBjoern A. Zeeb out:
2546da8fa4e3SBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
2547da8fa4e3SBjoern A. Zeeb 	return ret;
2548da8fa4e3SBjoern A. Zeeb }
2549da8fa4e3SBjoern A. Zeeb 
2550da8fa4e3SBjoern A. Zeeb static const struct file_operations fops_reset_htt_stats = {
2551da8fa4e3SBjoern A. Zeeb 	.write = ath10k_write_reset_htt_stats,
2552da8fa4e3SBjoern A. Zeeb 	.owner = THIS_MODULE,
2553da8fa4e3SBjoern A. Zeeb 	.open = simple_open,
2554da8fa4e3SBjoern A. Zeeb 	.llseek = default_llseek,
2555da8fa4e3SBjoern A. Zeeb };
2556da8fa4e3SBjoern A. Zeeb 
ath10k_debug_create(struct ath10k * ar)2557da8fa4e3SBjoern A. Zeeb int ath10k_debug_create(struct ath10k *ar)
2558da8fa4e3SBjoern A. Zeeb {
2559da8fa4e3SBjoern A. Zeeb 	ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
2560da8fa4e3SBjoern A. Zeeb 	if (!ar->debug.cal_data)
2561da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
2562da8fa4e3SBjoern A. Zeeb 
2563da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
2564da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
2565da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
2566da8fa4e3SBjoern A. Zeeb 	INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);
2567da8fa4e3SBjoern A. Zeeb 
2568da8fa4e3SBjoern A. Zeeb 	return 0;
2569da8fa4e3SBjoern A. Zeeb }
2570da8fa4e3SBjoern A. Zeeb 
ath10k_debug_destroy(struct ath10k * ar)2571da8fa4e3SBjoern A. Zeeb void ath10k_debug_destroy(struct ath10k *ar)
2572da8fa4e3SBjoern A. Zeeb {
2573da8fa4e3SBjoern A. Zeeb 	vfree(ar->debug.cal_data);
2574da8fa4e3SBjoern A. Zeeb 	ar->debug.cal_data = NULL;
2575da8fa4e3SBjoern A. Zeeb 
2576da8fa4e3SBjoern A. Zeeb 	ath10k_debug_fw_stats_reset(ar);
2577da8fa4e3SBjoern A. Zeeb 
2578da8fa4e3SBjoern A. Zeeb 	kfree(ar->debug.tpc_stats);
2579da8fa4e3SBjoern A. Zeeb 	kfree(ar->debug.tpc_stats_final);
2580da8fa4e3SBjoern A. Zeeb }
2581da8fa4e3SBjoern A. Zeeb 
ath10k_debug_register(struct ath10k * ar)2582da8fa4e3SBjoern A. Zeeb int ath10k_debug_register(struct ath10k *ar)
2583da8fa4e3SBjoern A. Zeeb {
2584da8fa4e3SBjoern A. Zeeb 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
2585da8fa4e3SBjoern A. Zeeb 						   ar->hw->wiphy->debugfsdir);
2586da8fa4e3SBjoern A. Zeeb 	if (IS_ERR_OR_NULL(ar->debug.debugfs_phy)) {
2587da8fa4e3SBjoern A. Zeeb 		if (IS_ERR(ar->debug.debugfs_phy))
2588da8fa4e3SBjoern A. Zeeb 			return PTR_ERR(ar->debug.debugfs_phy);
2589da8fa4e3SBjoern A. Zeeb 
2590da8fa4e3SBjoern A. Zeeb 		return -ENOMEM;
2591da8fa4e3SBjoern A. Zeeb 	}
2592da8fa4e3SBjoern A. Zeeb 
2593da8fa4e3SBjoern A. Zeeb 	INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
2594da8fa4e3SBjoern A. Zeeb 			  ath10k_debug_htt_stats_dwork);
2595da8fa4e3SBjoern A. Zeeb 
2596da8fa4e3SBjoern A. Zeeb 	init_completion(&ar->debug.tpc_complete);
2597da8fa4e3SBjoern A. Zeeb 	init_completion(&ar->debug.fw_stats_complete);
2598da8fa4e3SBjoern A. Zeeb 
2599da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("fw_stats", 0400, ar->debug.debugfs_phy, ar,
2600da8fa4e3SBjoern A. Zeeb 			    &fops_fw_stats);
2601da8fa4e3SBjoern A. Zeeb 
2602da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("fw_reset_stats", 0400, ar->debug.debugfs_phy, ar,
2603da8fa4e3SBjoern A. Zeeb 			    &fops_fw_reset_stats);
2604da8fa4e3SBjoern A. Zeeb 
2605da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("wmi_services", 0400, ar->debug.debugfs_phy, ar,
2606da8fa4e3SBjoern A. Zeeb 			    &fops_wmi_services);
2607da8fa4e3SBjoern A. Zeeb 
2608da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("simulate_fw_crash", 0600, ar->debug.debugfs_phy, ar,
2609da8fa4e3SBjoern A. Zeeb 			    &fops_simulate_fw_crash);
2610da8fa4e3SBjoern A. Zeeb 
2611da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("reg_addr", 0600, ar->debug.debugfs_phy, ar,
2612da8fa4e3SBjoern A. Zeeb 			    &fops_reg_addr);
2613da8fa4e3SBjoern A. Zeeb 
2614da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("reg_value", 0600, ar->debug.debugfs_phy, ar,
2615da8fa4e3SBjoern A. Zeeb 			    &fops_reg_value);
2616da8fa4e3SBjoern A. Zeeb 
2617da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("mem_value", 0600, ar->debug.debugfs_phy, ar,
2618da8fa4e3SBjoern A. Zeeb 			    &fops_mem_value);
2619da8fa4e3SBjoern A. Zeeb 
2620da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("chip_id", 0400, ar->debug.debugfs_phy, ar,
2621da8fa4e3SBjoern A. Zeeb 			    &fops_chip_id);
2622da8fa4e3SBjoern A. Zeeb 
2623da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("htt_stats_mask", 0600, ar->debug.debugfs_phy, ar,
2624da8fa4e3SBjoern A. Zeeb 			    &fops_htt_stats_mask);
2625da8fa4e3SBjoern A. Zeeb 
2626da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("htt_max_amsdu_ampdu", 0600, ar->debug.debugfs_phy, ar,
2627da8fa4e3SBjoern A. Zeeb 			    &fops_htt_max_amsdu_ampdu);
2628da8fa4e3SBjoern A. Zeeb 
2629da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("fw_dbglog", 0600, ar->debug.debugfs_phy, ar,
2630da8fa4e3SBjoern A. Zeeb 			    &fops_fw_dbglog);
2631da8fa4e3SBjoern A. Zeeb 
2632da8fa4e3SBjoern A. Zeeb 	if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
2633da8fa4e3SBjoern A. Zeeb 		      ar->normal_mode_fw.fw_file.fw_features)) {
2634da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("cal_data", 0400, ar->debug.debugfs_phy, ar,
2635da8fa4e3SBjoern A. Zeeb 				    &fops_cal_data);
2636da8fa4e3SBjoern A. Zeeb 
2637da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("nf_cal_period", 0600, ar->debug.debugfs_phy, ar,
2638da8fa4e3SBjoern A. Zeeb 				    &fops_nf_cal_period);
2639da8fa4e3SBjoern A. Zeeb 	}
2640da8fa4e3SBjoern A. Zeeb 
2641da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("ani_enable", 0600, ar->debug.debugfs_phy, ar,
2642da8fa4e3SBjoern A. Zeeb 			    &fops_ani_enable);
2643da8fa4e3SBjoern A. Zeeb 
2644da8fa4e3SBjoern A. Zeeb 	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {
2645da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("dfs_simulate_radar", 0200, ar->debug.debugfs_phy,
2646da8fa4e3SBjoern A. Zeeb 				    ar, &fops_simulate_radar);
2647da8fa4e3SBjoern A. Zeeb 
2648da8fa4e3SBjoern A. Zeeb 		debugfs_create_bool("dfs_block_radar_events", 0200,
2649da8fa4e3SBjoern A. Zeeb 				    ar->debug.debugfs_phy,
2650da8fa4e3SBjoern A. Zeeb 				    &ar->dfs_block_radar_events);
2651da8fa4e3SBjoern A. Zeeb 
2652da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("dfs_stats", 0400, ar->debug.debugfs_phy, ar,
2653da8fa4e3SBjoern A. Zeeb 				    &fops_dfs_stats);
2654da8fa4e3SBjoern A. Zeeb 	}
2655da8fa4e3SBjoern A. Zeeb 
2656da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_phy, ar,
2657da8fa4e3SBjoern A. Zeeb 			    &fops_pktlog_filter);
2658da8fa4e3SBjoern A. Zeeb 
2659da8fa4e3SBjoern A. Zeeb 	if (test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
2660da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar,
2661da8fa4e3SBjoern A. Zeeb 				    &fops_quiet_period);
2662da8fa4e3SBjoern A. Zeeb 
2663da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_phy, ar,
2664da8fa4e3SBjoern A. Zeeb 			    &fops_tpc_stats);
2665da8fa4e3SBjoern A. Zeeb 
2666da8fa4e3SBjoern A. Zeeb 	if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
2667da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("btcoex", 0644, ar->debug.debugfs_phy, ar,
2668da8fa4e3SBjoern A. Zeeb 				    &fops_btcoex);
2669da8fa4e3SBjoern A. Zeeb 
2670da8fa4e3SBjoern A. Zeeb 	if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
2671da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("peer_stats", 0644, ar->debug.debugfs_phy, ar,
2672da8fa4e3SBjoern A. Zeeb 				    &fops_peer_stats);
2673da8fa4e3SBjoern A. Zeeb 
2674da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("enable_extd_tx_stats", 0644,
2675da8fa4e3SBjoern A. Zeeb 				    ar->debug.debugfs_phy, ar,
2676da8fa4e3SBjoern A. Zeeb 				    &fops_enable_extd_tx_stats);
2677da8fa4e3SBjoern A. Zeeb 	}
2678da8fa4e3SBjoern A. Zeeb 
2679da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar,
2680da8fa4e3SBjoern A. Zeeb 			    &fops_fw_checksums);
2681da8fa4e3SBjoern A. Zeeb 
2682da8fa4e3SBjoern A. Zeeb 	if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS))
2683da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("sta_tid_stats_mask", 0600,
2684da8fa4e3SBjoern A. Zeeb 				    ar->debug.debugfs_phy,
2685da8fa4e3SBjoern A. Zeeb 				    ar, &fops_sta_tid_stats_mask);
2686da8fa4e3SBjoern A. Zeeb 
2687da8fa4e3SBjoern A. Zeeb 	if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map))
2688da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("tpc_stats_final", 0400,
2689da8fa4e3SBjoern A. Zeeb 				    ar->debug.debugfs_phy, ar,
2690da8fa4e3SBjoern A. Zeeb 				    &fops_tpc_stats_final);
2691da8fa4e3SBjoern A. Zeeb 
2692da8fa4e3SBjoern A. Zeeb 	if (test_bit(WMI_SERVICE_RESET_CHIP, ar->wmi.svc_map))
2693da8fa4e3SBjoern A. Zeeb 		debugfs_create_file("warm_hw_reset", 0600,
2694da8fa4e3SBjoern A. Zeeb 				    ar->debug.debugfs_phy, ar,
2695da8fa4e3SBjoern A. Zeeb 				    &fops_warm_hw_reset);
2696da8fa4e3SBjoern A. Zeeb 
2697da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_phy, ar,
2698da8fa4e3SBjoern A. Zeeb 			    &fops_ps_state_enable);
2699da8fa4e3SBjoern A. Zeeb 
2700da8fa4e3SBjoern A. Zeeb 	debugfs_create_file("reset_htt_stats", 0200, ar->debug.debugfs_phy, ar,
2701da8fa4e3SBjoern A. Zeeb 			    &fops_reset_htt_stats);
2702da8fa4e3SBjoern A. Zeeb 
2703da8fa4e3SBjoern A. Zeeb 	return 0;
2704da8fa4e3SBjoern A. Zeeb }
2705da8fa4e3SBjoern A. Zeeb 
ath10k_debug_unregister(struct ath10k * ar)2706da8fa4e3SBjoern A. Zeeb void ath10k_debug_unregister(struct ath10k *ar)
2707da8fa4e3SBjoern A. Zeeb {
2708da8fa4e3SBjoern A. Zeeb 	cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
2709da8fa4e3SBjoern A. Zeeb }
2710da8fa4e3SBjoern A. Zeeb 
2711da8fa4e3SBjoern A. Zeeb #endif /* CONFIG_ATH10K_DEBUGFS */
2712da8fa4e3SBjoern A. Zeeb 
2713da8fa4e3SBjoern A. Zeeb #ifdef CONFIG_ATH10K_DEBUG
__ath10k_dbg(struct ath10k * ar,enum ath10k_debug_mask mask,const char * fmt,...)2714da8fa4e3SBjoern A. Zeeb void __ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask,
2715da8fa4e3SBjoern A. Zeeb 		  const char *fmt, ...)
2716da8fa4e3SBjoern A. Zeeb {
2717da8fa4e3SBjoern A. Zeeb 	struct va_format vaf;
2718da8fa4e3SBjoern A. Zeeb 	va_list args;
2719da8fa4e3SBjoern A. Zeeb 
2720da8fa4e3SBjoern A. Zeeb 	va_start(args, fmt);
2721da8fa4e3SBjoern A. Zeeb 
2722da8fa4e3SBjoern A. Zeeb 	vaf.fmt = fmt;
2723da8fa4e3SBjoern A. Zeeb 	vaf.va = &args;
2724da8fa4e3SBjoern A. Zeeb 
2725da8fa4e3SBjoern A. Zeeb 	if (ath10k_debug_mask & mask)
2726da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
2727da8fa4e3SBjoern A. Zeeb 		dev_printk(KERN_DEBUG, ar->dev, "%pV", &vaf);
2728da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
2729da8fa4e3SBjoern A. Zeeb 	{
2730da8fa4e3SBjoern A. Zeeb 		char *str;
2731da8fa4e3SBjoern A. Zeeb 		vasprintf(&str, M_KMALLOC, fmt, args);
2732da8fa4e3SBjoern A. Zeeb 		dev_printk(KERN_DEBUG, ar->dev, "%s", str);
2733da8fa4e3SBjoern A. Zeeb 		free(str, M_KMALLOC);
2734da8fa4e3SBjoern A. Zeeb 	}
2735da8fa4e3SBjoern A. Zeeb #endif
2736da8fa4e3SBjoern A. Zeeb 
2737da8fa4e3SBjoern A. Zeeb 	trace_ath10k_log_dbg(ar, mask, &vaf);
2738da8fa4e3SBjoern A. Zeeb 
2739da8fa4e3SBjoern A. Zeeb 	va_end(args);
2740da8fa4e3SBjoern A. Zeeb }
2741da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(__ath10k_dbg);
2742da8fa4e3SBjoern A. Zeeb 
ath10k_dbg_dump(struct ath10k * ar,enum ath10k_debug_mask mask,const char * msg,const char * prefix,const void * buf,size_t len)2743da8fa4e3SBjoern A. Zeeb void ath10k_dbg_dump(struct ath10k *ar,
2744da8fa4e3SBjoern A. Zeeb 		     enum ath10k_debug_mask mask,
2745da8fa4e3SBjoern A. Zeeb 		     const char *msg, const char *prefix,
2746da8fa4e3SBjoern A. Zeeb 		     const void *buf, size_t len)
2747da8fa4e3SBjoern A. Zeeb {
2748da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
2749da8fa4e3SBjoern A. Zeeb 	char linebuf[256];
2750da8fa4e3SBjoern A. Zeeb 	size_t linebuflen;
2751da8fa4e3SBjoern A. Zeeb 	const void *ptr;
2752da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
2753da8fa4e3SBjoern A. Zeeb         struct sbuf *sb;
2754da8fa4e3SBjoern A. Zeeb         int rc;
2755da8fa4e3SBjoern A. Zeeb #endif
2756da8fa4e3SBjoern A. Zeeb 
2757da8fa4e3SBjoern A. Zeeb 	if (ath10k_debug_mask & mask) {
2758da8fa4e3SBjoern A. Zeeb 		if (msg)
2759da8fa4e3SBjoern A. Zeeb 			__ath10k_dbg(ar, mask, "%s\n", msg);
2760da8fa4e3SBjoern A. Zeeb 
2761da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
2762da8fa4e3SBjoern A. Zeeb 		for (ptr = buf; (ptr - buf) < len; ptr += 16) {
2763da8fa4e3SBjoern A. Zeeb 			linebuflen = 0;
2764da8fa4e3SBjoern A. Zeeb 			linebuflen += scnprintf(linebuf + linebuflen,
2765da8fa4e3SBjoern A. Zeeb 						sizeof(linebuf) - linebuflen,
2766da8fa4e3SBjoern A. Zeeb 						"%s%08x: ",
2767da8fa4e3SBjoern A. Zeeb 						(prefix ? prefix : ""),
2768da8fa4e3SBjoern A. Zeeb 						(unsigned int)(ptr - buf));
2769da8fa4e3SBjoern A. Zeeb 			hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,
2770da8fa4e3SBjoern A. Zeeb 					   linebuf + linebuflen,
2771da8fa4e3SBjoern A. Zeeb 					   sizeof(linebuf) - linebuflen, true);
2772da8fa4e3SBjoern A. Zeeb 			dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);
2773da8fa4e3SBjoern A. Zeeb 		}
2774da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
2775da8fa4e3SBjoern A. Zeeb 		sb = sbuf_new_auto();
2776da8fa4e3SBjoern A. Zeeb 		if (sb == NULL)
2777da8fa4e3SBjoern A. Zeeb 			goto trace;
2778da8fa4e3SBjoern A. Zeeb 
2779da8fa4e3SBjoern A. Zeeb 		sbuf_hexdump(sb, buf, len, prefix, 0);
2780da8fa4e3SBjoern A. Zeeb 		sbuf_trim(sb);
2781da8fa4e3SBjoern A. Zeeb 		rc = sbuf_finish(sb);
2782da8fa4e3SBjoern A. Zeeb 		if (rc == 0)
2783da8fa4e3SBjoern A. Zeeb 			dev_printk(KERN_DEBUG, ar->dev, "%s\n", sbuf_data(sb));
2784da8fa4e3SBjoern A. Zeeb 		sbuf_delete(sb);
2785da8fa4e3SBjoern A. Zeeb trace: ;
2786da8fa4e3SBjoern A. Zeeb #endif
2787da8fa4e3SBjoern A. Zeeb 	}
2788da8fa4e3SBjoern A. Zeeb 
2789da8fa4e3SBjoern A. Zeeb 	/* tracing code doesn't like null strings :/ */
2790da8fa4e3SBjoern A. Zeeb 	trace_ath10k_log_dbg_dump(ar, msg ? msg : "", prefix ? prefix : "",
2791da8fa4e3SBjoern A. Zeeb 				  buf, len);
2792da8fa4e3SBjoern A. Zeeb }
2793da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_dbg_dump);
2794da8fa4e3SBjoern A. Zeeb 
2795da8fa4e3SBjoern A. Zeeb #endif /* CONFIG_ATH10K_DEBUG */
2796