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