xref: /freebsd/sys/contrib/dev/athk/ath11k/spectral.c (revision dd4f32ae62426a10a84b4322756d82c06c202c4e)
1*dd4f32aeSBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
2*dd4f32aeSBjoern A. Zeeb /*
3*dd4f32aeSBjoern A. Zeeb  * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
4*dd4f32aeSBjoern A. Zeeb  */
5*dd4f32aeSBjoern A. Zeeb 
6*dd4f32aeSBjoern A. Zeeb #include <linux/relay.h>
7*dd4f32aeSBjoern A. Zeeb #include "core.h"
8*dd4f32aeSBjoern A. Zeeb #include "debug.h"
9*dd4f32aeSBjoern A. Zeeb 
10*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_NUM_RESP_PER_EVENT	2
11*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS	1
12*dd4f32aeSBjoern A. Zeeb 
13*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_DWORD_SIZE		4
14*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_MIN_BINS		32
15*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_MIN_IB_BINS		(ATH11K_SPECTRAL_MIN_BINS >> 1)
16*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_MAX_IB_BINS(x)	((x)->hw_params.spectral.max_fft_bins >> 1)
17*dd4f32aeSBjoern A. Zeeb 
18*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_SCAN_COUNT_MAX		4095
19*dd4f32aeSBjoern A. Zeeb 
20*dd4f32aeSBjoern A. Zeeb /* Max channel computed by sum of 2g and 5g band channels */
21*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TOTAL_CHANNEL		41
22*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL	70
23*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x)	(sizeof(struct fft_sample_ath11k) + \
24*dd4f32aeSBjoern A. Zeeb 						 ATH11K_SPECTRAL_MAX_IB_BINS(x))
25*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TOTAL_SAMPLE		(ATH11K_SPECTRAL_TOTAL_CHANNEL * \
26*dd4f32aeSBjoern A. Zeeb 						 ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL)
27*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_SUB_BUFF_SIZE(x)	ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x)
28*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_NUM_SUB_BUF		ATH11K_SPECTRAL_TOTAL_SAMPLE
29*dd4f32aeSBjoern A. Zeeb 
30*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_20MHZ			20
31*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_40MHZ			40
32*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_80MHZ			80
33*dd4f32aeSBjoern A. Zeeb 
34*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_SIGNATURE		0xFA
35*dd4f32aeSBjoern A. Zeeb 
36*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TAG_RADAR_SUMMARY	0x0
37*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TAG_RADAR_FFT		0x1
38*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TAG_SCAN_SUMMARY	0x2
39*dd4f32aeSBjoern A. Zeeb #define ATH11K_SPECTRAL_TAG_SCAN_SEARCH		0x3
40*dd4f32aeSBjoern A. Zeeb 
41*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_TLV_HDR_LEN				GENMASK(15, 0)
42*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_TLV_HDR_TAG				GENMASK(23, 16)
43*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_TLV_HDR_SIGN				GENMASK(31, 24)
44*dd4f32aeSBjoern A. Zeeb 
45*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN		GENMASK(7, 0)
46*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_OB_FLAG			BIT(8)
47*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_GRP_IDX			GENMASK(16, 9)
48*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT		BIT(17)
49*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB		GENMASK(27, 18)
50*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_FALSE_SCAN		BIT(28)
51*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_DETECTOR_ID		GENMASK(30, 29)
52*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO0_PRI80			BIT(31)
53*dd4f32aeSBjoern A. Zeeb 
54*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX		GENMASK(11, 0)
55*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE		GENMASK(21, 12)
56*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO2_NARROWBAND_MASK		GENMASK(29, 22)
57*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE		BIT(30)
58*dd4f32aeSBjoern A. Zeeb 
59*dd4f32aeSBjoern A. Zeeb struct spectral_tlv {
60*dd4f32aeSBjoern A. Zeeb 	__le32 timestamp;
61*dd4f32aeSBjoern A. Zeeb 	__le32 header;
62*dd4f32aeSBjoern A. Zeeb } __packed;
63*dd4f32aeSBjoern A. Zeeb 
64*dd4f32aeSBjoern A. Zeeb struct spectral_summary_fft_report {
65*dd4f32aeSBjoern A. Zeeb 	__le32 timestamp;
66*dd4f32aeSBjoern A. Zeeb 	__le32 tlv_header;
67*dd4f32aeSBjoern A. Zeeb 	__le32 info0;
68*dd4f32aeSBjoern A. Zeeb 	__le32 reserve0;
69*dd4f32aeSBjoern A. Zeeb 	__le32 info2;
70*dd4f32aeSBjoern A. Zeeb 	__le32 reserve1;
71*dd4f32aeSBjoern A. Zeeb } __packed;
72*dd4f32aeSBjoern A. Zeeb 
73*dd4f32aeSBjoern A. Zeeb struct ath11k_spectral_summary_report {
74*dd4f32aeSBjoern A. Zeeb 	struct wmi_dma_buf_release_meta_data meta;
75*dd4f32aeSBjoern A. Zeeb 	u32 timestamp;
76*dd4f32aeSBjoern A. Zeeb 	u8 agc_total_gain;
77*dd4f32aeSBjoern A. Zeeb 	u8 grp_idx;
78*dd4f32aeSBjoern A. Zeeb 	u16 inb_pwr_db;
79*dd4f32aeSBjoern A. Zeeb 	s16 peak_idx;
80*dd4f32aeSBjoern A. Zeeb 	u16 peak_mag;
81*dd4f32aeSBjoern A. Zeeb 	u8 detector_id;
82*dd4f32aeSBjoern A. Zeeb 	bool out_of_band_flag;
83*dd4f32aeSBjoern A. Zeeb 	bool rf_saturation;
84*dd4f32aeSBjoern A. Zeeb 	bool primary80;
85*dd4f32aeSBjoern A. Zeeb 	bool gain_change;
86*dd4f32aeSBjoern A. Zeeb 	bool false_scan;
87*dd4f32aeSBjoern A. Zeeb };
88*dd4f32aeSBjoern A. Zeeb 
89*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID		GENMASK(1, 0)
90*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO0_FFT_NUM		GENMASK(4, 2)
91*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK		GENMASK(16, 5)
92*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX	GENMASK(27, 17)
93*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX		GENMASK(30, 28)
94*dd4f32aeSBjoern A. Zeeb 
95*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB		GENMASK(8, 0)
96*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB		GENMASK(16, 9)
97*dd4f32aeSBjoern A. Zeeb 
98*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS	GENMASK(7, 0)
99*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE	GENMASK(17, 8)
100*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB		GENMASK(24, 18)
101*dd4f32aeSBjoern A. Zeeb #define SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB		GENMASK(31, 25)
102*dd4f32aeSBjoern A. Zeeb 
103*dd4f32aeSBjoern A. Zeeb struct spectral_search_fft_report {
104*dd4f32aeSBjoern A. Zeeb 	__le32 timestamp;
105*dd4f32aeSBjoern A. Zeeb 	__le32 tlv_header;
106*dd4f32aeSBjoern A. Zeeb 	__le32 info0;
107*dd4f32aeSBjoern A. Zeeb 	__le32 info1;
108*dd4f32aeSBjoern A. Zeeb 	__le32 info2;
109*dd4f32aeSBjoern A. Zeeb 	__le32 reserve0;
110*dd4f32aeSBjoern A. Zeeb 	u8 bins[0];
111*dd4f32aeSBjoern A. Zeeb } __packed;
112*dd4f32aeSBjoern A. Zeeb 
113*dd4f32aeSBjoern A. Zeeb struct ath11k_spectral_search_report {
114*dd4f32aeSBjoern A. Zeeb 	u32 timestamp;
115*dd4f32aeSBjoern A. Zeeb 	u8 detector_id;
116*dd4f32aeSBjoern A. Zeeb 	u8 fft_count;
117*dd4f32aeSBjoern A. Zeeb 	u16 radar_check;
118*dd4f32aeSBjoern A. Zeeb 	s16 peak_idx;
119*dd4f32aeSBjoern A. Zeeb 	u8 chain_idx;
120*dd4f32aeSBjoern A. Zeeb 	u16 base_pwr_db;
121*dd4f32aeSBjoern A. Zeeb 	u8 total_gain_db;
122*dd4f32aeSBjoern A. Zeeb 	u8 strong_bin_count;
123*dd4f32aeSBjoern A. Zeeb 	u16 peak_mag;
124*dd4f32aeSBjoern A. Zeeb 	u8 avg_pwr_db;
125*dd4f32aeSBjoern A. Zeeb 	u8 rel_pwr_db;
126*dd4f32aeSBjoern A. Zeeb };
127*dd4f32aeSBjoern A. Zeeb 
128*dd4f32aeSBjoern A. Zeeb static struct dentry *create_buf_file_handler(const char *filename,
129*dd4f32aeSBjoern A. Zeeb 					      struct dentry *parent,
130*dd4f32aeSBjoern A. Zeeb 					      umode_t mode,
131*dd4f32aeSBjoern A. Zeeb 					      struct rchan_buf *buf,
132*dd4f32aeSBjoern A. Zeeb 					      int *is_global)
133*dd4f32aeSBjoern A. Zeeb {
134*dd4f32aeSBjoern A. Zeeb 	struct dentry *buf_file;
135*dd4f32aeSBjoern A. Zeeb 
136*dd4f32aeSBjoern A. Zeeb 	buf_file = debugfs_create_file(filename, mode, parent, buf,
137*dd4f32aeSBjoern A. Zeeb 				       &relay_file_operations);
138*dd4f32aeSBjoern A. Zeeb 	*is_global = 1;
139*dd4f32aeSBjoern A. Zeeb 	return buf_file;
140*dd4f32aeSBjoern A. Zeeb }
141*dd4f32aeSBjoern A. Zeeb 
142*dd4f32aeSBjoern A. Zeeb static int remove_buf_file_handler(struct dentry *dentry)
143*dd4f32aeSBjoern A. Zeeb {
144*dd4f32aeSBjoern A. Zeeb 	debugfs_remove(dentry);
145*dd4f32aeSBjoern A. Zeeb 
146*dd4f32aeSBjoern A. Zeeb 	return 0;
147*dd4f32aeSBjoern A. Zeeb }
148*dd4f32aeSBjoern A. Zeeb 
149*dd4f32aeSBjoern A. Zeeb static const struct rchan_callbacks rfs_scan_cb = {
150*dd4f32aeSBjoern A. Zeeb 	.create_buf_file = create_buf_file_handler,
151*dd4f32aeSBjoern A. Zeeb 	.remove_buf_file = remove_buf_file_handler,
152*dd4f32aeSBjoern A. Zeeb };
153*dd4f32aeSBjoern A. Zeeb 
154*dd4f32aeSBjoern A. Zeeb static struct ath11k_vif *ath11k_spectral_get_vdev(struct ath11k *ar)
155*dd4f32aeSBjoern A. Zeeb {
156*dd4f32aeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
157*dd4f32aeSBjoern A. Zeeb 
158*dd4f32aeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
159*dd4f32aeSBjoern A. Zeeb 
160*dd4f32aeSBjoern A. Zeeb 	if (list_empty(&ar->arvifs))
161*dd4f32aeSBjoern A. Zeeb 		return NULL;
162*dd4f32aeSBjoern A. Zeeb 
163*dd4f32aeSBjoern A. Zeeb 	/* if there already is a vif doing spectral, return that. */
164*dd4f32aeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list)
165*dd4f32aeSBjoern A. Zeeb 		if (arvif->spectral_enabled)
166*dd4f32aeSBjoern A. Zeeb 			return arvif;
167*dd4f32aeSBjoern A. Zeeb 
168*dd4f32aeSBjoern A. Zeeb 	/* otherwise, return the first vif. */
169*dd4f32aeSBjoern A. Zeeb 	return list_first_entry(&ar->arvifs, typeof(*arvif), list);
170*dd4f32aeSBjoern A. Zeeb }
171*dd4f32aeSBjoern A. Zeeb 
172*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_scan_trigger(struct ath11k *ar)
173*dd4f32aeSBjoern A. Zeeb {
174*dd4f32aeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
175*dd4f32aeSBjoern A. Zeeb 	int ret;
176*dd4f32aeSBjoern A. Zeeb 
177*dd4f32aeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
178*dd4f32aeSBjoern A. Zeeb 
179*dd4f32aeSBjoern A. Zeeb 	arvif = ath11k_spectral_get_vdev(ar);
180*dd4f32aeSBjoern A. Zeeb 	if (!arvif)
181*dd4f32aeSBjoern A. Zeeb 		return -ENODEV;
182*dd4f32aeSBjoern A. Zeeb 
183*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.mode == ATH11K_SPECTRAL_DISABLED)
184*dd4f32aeSBjoern A. Zeeb 		return 0;
185*dd4f32aeSBjoern A. Zeeb 
186*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
187*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
188*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
189*dd4f32aeSBjoern A. Zeeb 	if (ret)
190*dd4f32aeSBjoern A. Zeeb 		return ret;
191*dd4f32aeSBjoern A. Zeeb 
192*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
193*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
194*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
195*dd4f32aeSBjoern A. Zeeb 	if (ret)
196*dd4f32aeSBjoern A. Zeeb 		return ret;
197*dd4f32aeSBjoern A. Zeeb 
198*dd4f32aeSBjoern A. Zeeb 	return 0;
199*dd4f32aeSBjoern A. Zeeb }
200*dd4f32aeSBjoern A. Zeeb 
201*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_scan_config(struct ath11k *ar,
202*dd4f32aeSBjoern A. Zeeb 				       enum ath11k_spectral_mode mode)
203*dd4f32aeSBjoern A. Zeeb {
204*dd4f32aeSBjoern A. Zeeb 	struct ath11k_wmi_vdev_spectral_conf_param param = { 0 };
205*dd4f32aeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
206*dd4f32aeSBjoern A. Zeeb 	int ret, count;
207*dd4f32aeSBjoern A. Zeeb 
208*dd4f32aeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
209*dd4f32aeSBjoern A. Zeeb 
210*dd4f32aeSBjoern A. Zeeb 	arvif = ath11k_spectral_get_vdev(ar);
211*dd4f32aeSBjoern A. Zeeb 	if (!arvif)
212*dd4f32aeSBjoern A. Zeeb 		return -ENODEV;
213*dd4f32aeSBjoern A. Zeeb 
214*dd4f32aeSBjoern A. Zeeb 	arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED);
215*dd4f32aeSBjoern A. Zeeb 	ar->spectral.mode = mode;
216*dd4f32aeSBjoern A. Zeeb 
217*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
218*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
219*dd4f32aeSBjoern A. Zeeb 					      ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE);
220*dd4f32aeSBjoern A. Zeeb 	if (ret) {
221*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to enable spectral scan: %d\n", ret);
222*dd4f32aeSBjoern A. Zeeb 		return ret;
223*dd4f32aeSBjoern A. Zeeb 	}
224*dd4f32aeSBjoern A. Zeeb 
225*dd4f32aeSBjoern A. Zeeb 	if (mode == ATH11K_SPECTRAL_DISABLED)
226*dd4f32aeSBjoern A. Zeeb 		return 0;
227*dd4f32aeSBjoern A. Zeeb 
228*dd4f32aeSBjoern A. Zeeb 	if (mode == ATH11K_SPECTRAL_BACKGROUND)
229*dd4f32aeSBjoern A. Zeeb 		count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
230*dd4f32aeSBjoern A. Zeeb 	else
231*dd4f32aeSBjoern A. Zeeb 		count = max_t(u16, 1, ar->spectral.count);
232*dd4f32aeSBjoern A. Zeeb 
233*dd4f32aeSBjoern A. Zeeb 	param.vdev_id = arvif->vdev_id;
234*dd4f32aeSBjoern A. Zeeb 	param.scan_count = count;
235*dd4f32aeSBjoern A. Zeeb 	param.scan_fft_size = ar->spectral.fft_size;
236*dd4f32aeSBjoern A. Zeeb 	param.scan_period = ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT;
237*dd4f32aeSBjoern A. Zeeb 	param.scan_priority = ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT;
238*dd4f32aeSBjoern A. Zeeb 	param.scan_gc_ena = ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT;
239*dd4f32aeSBjoern A. Zeeb 	param.scan_restart_ena = ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT;
240*dd4f32aeSBjoern A. Zeeb 	param.scan_noise_floor_ref = ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
241*dd4f32aeSBjoern A. Zeeb 	param.scan_init_delay = ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT;
242*dd4f32aeSBjoern A. Zeeb 	param.scan_nb_tone_thr = ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
243*dd4f32aeSBjoern A. Zeeb 	param.scan_str_bin_thr = ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
244*dd4f32aeSBjoern A. Zeeb 	param.scan_wb_rpt_mode = ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
245*dd4f32aeSBjoern A. Zeeb 	param.scan_rssi_rpt_mode = ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
246*dd4f32aeSBjoern A. Zeeb 	param.scan_rssi_thr = ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT;
247*dd4f32aeSBjoern A. Zeeb 	param.scan_pwr_format = ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
248*dd4f32aeSBjoern A. Zeeb 	param.scan_rpt_mode = ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT;
249*dd4f32aeSBjoern A. Zeeb 	param.scan_bin_scale = ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT;
250*dd4f32aeSBjoern A. Zeeb 	param.scan_dbm_adj = ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT;
251*dd4f32aeSBjoern A. Zeeb 	param.scan_chn_mask = ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT;
252*dd4f32aeSBjoern A. Zeeb 
253*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_vdev_spectral_conf(ar, &param);
254*dd4f32aeSBjoern A. Zeeb 	if (ret) {
255*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to configure spectral scan: %d\n", ret);
256*dd4f32aeSBjoern A. Zeeb 		return ret;
257*dd4f32aeSBjoern A. Zeeb 	}
258*dd4f32aeSBjoern A. Zeeb 
259*dd4f32aeSBjoern A. Zeeb 	return 0;
260*dd4f32aeSBjoern A. Zeeb }
261*dd4f32aeSBjoern A. Zeeb 
262*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_read_file_spec_scan_ctl(struct file *file,
263*dd4f32aeSBjoern A. Zeeb 					      char __user *user_buf,
264*dd4f32aeSBjoern A. Zeeb 					      size_t count, loff_t *ppos)
265*dd4f32aeSBjoern A. Zeeb {
266*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
267*dd4f32aeSBjoern A. Zeeb 	char *mode = "";
268*dd4f32aeSBjoern A. Zeeb 	size_t len;
269*dd4f32aeSBjoern A. Zeeb 	enum ath11k_spectral_mode spectral_mode;
270*dd4f32aeSBjoern A. Zeeb 
271*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
272*dd4f32aeSBjoern A. Zeeb 	spectral_mode = ar->spectral.mode;
273*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
274*dd4f32aeSBjoern A. Zeeb 
275*dd4f32aeSBjoern A. Zeeb 	switch (spectral_mode) {
276*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_DISABLED:
277*dd4f32aeSBjoern A. Zeeb 		mode = "disable";
278*dd4f32aeSBjoern A. Zeeb 		break;
279*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_BACKGROUND:
280*dd4f32aeSBjoern A. Zeeb 		mode = "background";
281*dd4f32aeSBjoern A. Zeeb 		break;
282*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_MANUAL:
283*dd4f32aeSBjoern A. Zeeb 		mode = "manual";
284*dd4f32aeSBjoern A. Zeeb 		break;
285*dd4f32aeSBjoern A. Zeeb 	}
286*dd4f32aeSBjoern A. Zeeb 
287*dd4f32aeSBjoern A. Zeeb 	len = strlen(mode);
288*dd4f32aeSBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, mode, len);
289*dd4f32aeSBjoern A. Zeeb }
290*dd4f32aeSBjoern A. Zeeb 
291*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_write_file_spec_scan_ctl(struct file *file,
292*dd4f32aeSBjoern A. Zeeb 					       const char __user *user_buf,
293*dd4f32aeSBjoern A. Zeeb 					       size_t count, loff_t *ppos)
294*dd4f32aeSBjoern A. Zeeb {
295*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
296*dd4f32aeSBjoern A. Zeeb 	char buf[32];
297*dd4f32aeSBjoern A. Zeeb 	ssize_t len;
298*dd4f32aeSBjoern A. Zeeb 	int ret;
299*dd4f32aeSBjoern A. Zeeb 
300*dd4f32aeSBjoern A. Zeeb 	len = min(count, sizeof(buf) - 1);
301*dd4f32aeSBjoern A. Zeeb 	if (copy_from_user(buf, user_buf, len))
302*dd4f32aeSBjoern A. Zeeb 		return -EFAULT;
303*dd4f32aeSBjoern A. Zeeb 
304*dd4f32aeSBjoern A. Zeeb 	buf[len] = '\0';
305*dd4f32aeSBjoern A. Zeeb 
306*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
307*dd4f32aeSBjoern A. Zeeb 
308*dd4f32aeSBjoern A. Zeeb 	if (strncmp("trigger", buf, 7) == 0) {
309*dd4f32aeSBjoern A. Zeeb 		if (ar->spectral.mode == ATH11K_SPECTRAL_MANUAL ||
310*dd4f32aeSBjoern A. Zeeb 		    ar->spectral.mode == ATH11K_SPECTRAL_BACKGROUND) {
311*dd4f32aeSBjoern A. Zeeb 			/* reset the configuration to adopt possibly changed
312*dd4f32aeSBjoern A. Zeeb 			 * debugfs parameters
313*dd4f32aeSBjoern A. Zeeb 			 */
314*dd4f32aeSBjoern A. Zeeb 			ret = ath11k_spectral_scan_config(ar, ar->spectral.mode);
315*dd4f32aeSBjoern A. Zeeb 			if (ret) {
316*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ar->ab, "failed to reconfigure spectral scan: %d\n",
317*dd4f32aeSBjoern A. Zeeb 					    ret);
318*dd4f32aeSBjoern A. Zeeb 				goto unlock;
319*dd4f32aeSBjoern A. Zeeb 			}
320*dd4f32aeSBjoern A. Zeeb 
321*dd4f32aeSBjoern A. Zeeb 			ret = ath11k_spectral_scan_trigger(ar);
322*dd4f32aeSBjoern A. Zeeb 			if (ret) {
323*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ar->ab, "failed to trigger spectral scan: %d\n",
324*dd4f32aeSBjoern A. Zeeb 					    ret);
325*dd4f32aeSBjoern A. Zeeb 			}
326*dd4f32aeSBjoern A. Zeeb 		} else {
327*dd4f32aeSBjoern A. Zeeb 			ret = -EINVAL;
328*dd4f32aeSBjoern A. Zeeb 		}
329*dd4f32aeSBjoern A. Zeeb 	} else if (strncmp("background", buf, 10) == 0) {
330*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_BACKGROUND);
331*dd4f32aeSBjoern A. Zeeb 	} else if (strncmp("manual", buf, 6) == 0) {
332*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_MANUAL);
333*dd4f32aeSBjoern A. Zeeb 	} else if (strncmp("disable", buf, 7) == 0) {
334*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);
335*dd4f32aeSBjoern A. Zeeb 	} else {
336*dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
337*dd4f32aeSBjoern A. Zeeb 	}
338*dd4f32aeSBjoern A. Zeeb 
339*dd4f32aeSBjoern A. Zeeb unlock:
340*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
341*dd4f32aeSBjoern A. Zeeb 
342*dd4f32aeSBjoern A. Zeeb 	if (ret)
343*dd4f32aeSBjoern A. Zeeb 		return ret;
344*dd4f32aeSBjoern A. Zeeb 
345*dd4f32aeSBjoern A. Zeeb 	return count;
346*dd4f32aeSBjoern A. Zeeb }
347*dd4f32aeSBjoern A. Zeeb 
348*dd4f32aeSBjoern A. Zeeb static const struct file_operations fops_scan_ctl = {
349*dd4f32aeSBjoern A. Zeeb 	.read = ath11k_read_file_spec_scan_ctl,
350*dd4f32aeSBjoern A. Zeeb 	.write = ath11k_write_file_spec_scan_ctl,
351*dd4f32aeSBjoern A. Zeeb 	.open = simple_open,
352*dd4f32aeSBjoern A. Zeeb 	.owner = THIS_MODULE,
353*dd4f32aeSBjoern A. Zeeb 	.llseek = default_llseek,
354*dd4f32aeSBjoern A. Zeeb };
355*dd4f32aeSBjoern A. Zeeb 
356*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_read_file_spectral_count(struct file *file,
357*dd4f32aeSBjoern A. Zeeb 					       char __user *user_buf,
358*dd4f32aeSBjoern A. Zeeb 					       size_t count, loff_t *ppos)
359*dd4f32aeSBjoern A. Zeeb {
360*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
361*dd4f32aeSBjoern A. Zeeb 	char buf[32];
362*dd4f32aeSBjoern A. Zeeb 	size_t len;
363*dd4f32aeSBjoern A. Zeeb 	u16 spectral_count;
364*dd4f32aeSBjoern A. Zeeb 
365*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
366*dd4f32aeSBjoern A. Zeeb 	spectral_count = ar->spectral.count;
367*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
368*dd4f32aeSBjoern A. Zeeb 
369*dd4f32aeSBjoern A. Zeeb 	len = sprintf(buf, "%d\n", spectral_count);
370*dd4f32aeSBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
371*dd4f32aeSBjoern A. Zeeb }
372*dd4f32aeSBjoern A. Zeeb 
373*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_write_file_spectral_count(struct file *file,
374*dd4f32aeSBjoern A. Zeeb 						const char __user *user_buf,
375*dd4f32aeSBjoern A. Zeeb 						size_t count, loff_t *ppos)
376*dd4f32aeSBjoern A. Zeeb {
377*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
378*dd4f32aeSBjoern A. Zeeb 	unsigned long val;
379*dd4f32aeSBjoern A. Zeeb 	char buf[32];
380*dd4f32aeSBjoern A. Zeeb 	ssize_t len;
381*dd4f32aeSBjoern A. Zeeb 
382*dd4f32aeSBjoern A. Zeeb 	len = min(count, sizeof(buf) - 1);
383*dd4f32aeSBjoern A. Zeeb 	if (copy_from_user(buf, user_buf, len))
384*dd4f32aeSBjoern A. Zeeb 		return -EFAULT;
385*dd4f32aeSBjoern A. Zeeb 
386*dd4f32aeSBjoern A. Zeeb 	buf[len] = '\0';
387*dd4f32aeSBjoern A. Zeeb 	if (kstrtoul(buf, 0, &val))
388*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
389*dd4f32aeSBjoern A. Zeeb 
390*dd4f32aeSBjoern A. Zeeb 	if (val > ATH11K_SPECTRAL_SCAN_COUNT_MAX)
391*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
392*dd4f32aeSBjoern A. Zeeb 
393*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
394*dd4f32aeSBjoern A. Zeeb 	ar->spectral.count = val;
395*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
396*dd4f32aeSBjoern A. Zeeb 
397*dd4f32aeSBjoern A. Zeeb 	return count;
398*dd4f32aeSBjoern A. Zeeb }
399*dd4f32aeSBjoern A. Zeeb 
400*dd4f32aeSBjoern A. Zeeb static const struct file_operations fops_scan_count = {
401*dd4f32aeSBjoern A. Zeeb 	.read = ath11k_read_file_spectral_count,
402*dd4f32aeSBjoern A. Zeeb 	.write = ath11k_write_file_spectral_count,
403*dd4f32aeSBjoern A. Zeeb 	.open = simple_open,
404*dd4f32aeSBjoern A. Zeeb 	.owner = THIS_MODULE,
405*dd4f32aeSBjoern A. Zeeb 	.llseek = default_llseek,
406*dd4f32aeSBjoern A. Zeeb };
407*dd4f32aeSBjoern A. Zeeb 
408*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_read_file_spectral_bins(struct file *file,
409*dd4f32aeSBjoern A. Zeeb 					      char __user *user_buf,
410*dd4f32aeSBjoern A. Zeeb 					      size_t count, loff_t *ppos)
411*dd4f32aeSBjoern A. Zeeb {
412*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
413*dd4f32aeSBjoern A. Zeeb 	char buf[32];
414*dd4f32aeSBjoern A. Zeeb 	unsigned int bins, fft_size;
415*dd4f32aeSBjoern A. Zeeb 	size_t len;
416*dd4f32aeSBjoern A. Zeeb 
417*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
418*dd4f32aeSBjoern A. Zeeb 
419*dd4f32aeSBjoern A. Zeeb 	fft_size = ar->spectral.fft_size;
420*dd4f32aeSBjoern A. Zeeb 	bins = 1 << fft_size;
421*dd4f32aeSBjoern A. Zeeb 
422*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
423*dd4f32aeSBjoern A. Zeeb 
424*dd4f32aeSBjoern A. Zeeb 	len = sprintf(buf, "%d\n", bins);
425*dd4f32aeSBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
426*dd4f32aeSBjoern A. Zeeb }
427*dd4f32aeSBjoern A. Zeeb 
428*dd4f32aeSBjoern A. Zeeb static ssize_t ath11k_write_file_spectral_bins(struct file *file,
429*dd4f32aeSBjoern A. Zeeb 					       const char __user *user_buf,
430*dd4f32aeSBjoern A. Zeeb 					       size_t count, loff_t *ppos)
431*dd4f32aeSBjoern A. Zeeb {
432*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = file->private_data;
433*dd4f32aeSBjoern A. Zeeb 	unsigned long val;
434*dd4f32aeSBjoern A. Zeeb 	char buf[32];
435*dd4f32aeSBjoern A. Zeeb 	ssize_t len;
436*dd4f32aeSBjoern A. Zeeb 
437*dd4f32aeSBjoern A. Zeeb 	len = min(count, sizeof(buf) - 1);
438*dd4f32aeSBjoern A. Zeeb 	if (copy_from_user(buf, user_buf, len))
439*dd4f32aeSBjoern A. Zeeb 		return -EFAULT;
440*dd4f32aeSBjoern A. Zeeb 
441*dd4f32aeSBjoern A. Zeeb 	buf[len] = '\0';
442*dd4f32aeSBjoern A. Zeeb 	if (kstrtoul(buf, 0, &val))
443*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
444*dd4f32aeSBjoern A. Zeeb 
445*dd4f32aeSBjoern A. Zeeb 	if (val < ATH11K_SPECTRAL_MIN_BINS ||
446*dd4f32aeSBjoern A. Zeeb 	    val > ar->ab->hw_params.spectral.max_fft_bins)
447*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
448*dd4f32aeSBjoern A. Zeeb 
449*dd4f32aeSBjoern A. Zeeb 	if (!is_power_of_2(val))
450*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
451*dd4f32aeSBjoern A. Zeeb 
452*dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
453*dd4f32aeSBjoern A. Zeeb 	ar->spectral.fft_size = ilog2(val);
454*dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
455*dd4f32aeSBjoern A. Zeeb 
456*dd4f32aeSBjoern A. Zeeb 	return count;
457*dd4f32aeSBjoern A. Zeeb }
458*dd4f32aeSBjoern A. Zeeb 
459*dd4f32aeSBjoern A. Zeeb static const struct file_operations fops_scan_bins = {
460*dd4f32aeSBjoern A. Zeeb 	.read = ath11k_read_file_spectral_bins,
461*dd4f32aeSBjoern A. Zeeb 	.write = ath11k_write_file_spectral_bins,
462*dd4f32aeSBjoern A. Zeeb 	.open = simple_open,
463*dd4f32aeSBjoern A. Zeeb 	.owner = THIS_MODULE,
464*dd4f32aeSBjoern A. Zeeb 	.llseek = default_llseek,
465*dd4f32aeSBjoern A. Zeeb };
466*dd4f32aeSBjoern A. Zeeb 
467*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_pull_summary(struct ath11k *ar,
468*dd4f32aeSBjoern A. Zeeb 					struct wmi_dma_buf_release_meta_data *meta,
469*dd4f32aeSBjoern A. Zeeb 					struct spectral_summary_fft_report *summary,
470*dd4f32aeSBjoern A. Zeeb 					struct ath11k_spectral_summary_report *report)
471*dd4f32aeSBjoern A. Zeeb {
472*dd4f32aeSBjoern A. Zeeb 	report->timestamp = __le32_to_cpu(summary->timestamp);
473*dd4f32aeSBjoern A. Zeeb 	report->agc_total_gain = FIELD_GET(SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN,
474*dd4f32aeSBjoern A. Zeeb 					   __le32_to_cpu(summary->info0));
475*dd4f32aeSBjoern A. Zeeb 	report->out_of_band_flag = FIELD_GET(SPECTRAL_SUMMARY_INFO0_OB_FLAG,
476*dd4f32aeSBjoern A. Zeeb 					     __le32_to_cpu(summary->info0));
477*dd4f32aeSBjoern A. Zeeb 	report->grp_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO0_GRP_IDX,
478*dd4f32aeSBjoern A. Zeeb 				    __le32_to_cpu(summary->info0));
479*dd4f32aeSBjoern A. Zeeb 	report->rf_saturation = FIELD_GET(SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT,
480*dd4f32aeSBjoern A. Zeeb 					  __le32_to_cpu(summary->info0));
481*dd4f32aeSBjoern A. Zeeb 	report->inb_pwr_db = FIELD_GET(SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB,
482*dd4f32aeSBjoern A. Zeeb 				       __le32_to_cpu(summary->info0));
483*dd4f32aeSBjoern A. Zeeb 	report->false_scan = FIELD_GET(SPECTRAL_SUMMARY_INFO0_FALSE_SCAN,
484*dd4f32aeSBjoern A. Zeeb 				       __le32_to_cpu(summary->info0));
485*dd4f32aeSBjoern A. Zeeb 	report->detector_id = FIELD_GET(SPECTRAL_SUMMARY_INFO0_DETECTOR_ID,
486*dd4f32aeSBjoern A. Zeeb 					__le32_to_cpu(summary->info0));
487*dd4f32aeSBjoern A. Zeeb 	report->primary80 = FIELD_GET(SPECTRAL_SUMMARY_INFO0_PRI80,
488*dd4f32aeSBjoern A. Zeeb 				      __le32_to_cpu(summary->info0));
489*dd4f32aeSBjoern A. Zeeb 	report->peak_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX,
490*dd4f32aeSBjoern A. Zeeb 				     __le32_to_cpu(summary->info2));
491*dd4f32aeSBjoern A. Zeeb 	report->peak_mag = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE,
492*dd4f32aeSBjoern A. Zeeb 				     __le32_to_cpu(summary->info2));
493*dd4f32aeSBjoern A. Zeeb 	report->gain_change = FIELD_GET(SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE,
494*dd4f32aeSBjoern A. Zeeb 					__le32_to_cpu(summary->info2));
495*dd4f32aeSBjoern A. Zeeb 
496*dd4f32aeSBjoern A. Zeeb 	memcpy(&report->meta, meta, sizeof(*meta));
497*dd4f32aeSBjoern A. Zeeb 
498*dd4f32aeSBjoern A. Zeeb 	return 0;
499*dd4f32aeSBjoern A. Zeeb }
500*dd4f32aeSBjoern A. Zeeb 
501*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_pull_search(struct ath11k *ar,
502*dd4f32aeSBjoern A. Zeeb 				       struct spectral_search_fft_report *search,
503*dd4f32aeSBjoern A. Zeeb 				       struct ath11k_spectral_search_report *report)
504*dd4f32aeSBjoern A. Zeeb {
505*dd4f32aeSBjoern A. Zeeb 	report->timestamp = __le32_to_cpu(search->timestamp);
506*dd4f32aeSBjoern A. Zeeb 	report->detector_id = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID,
507*dd4f32aeSBjoern A. Zeeb 					__le32_to_cpu(search->info0));
508*dd4f32aeSBjoern A. Zeeb 	report->fft_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_FFT_NUM,
509*dd4f32aeSBjoern A. Zeeb 				      __le32_to_cpu(search->info0));
510*dd4f32aeSBjoern A. Zeeb 	report->radar_check = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK,
511*dd4f32aeSBjoern A. Zeeb 					__le32_to_cpu(search->info0));
512*dd4f32aeSBjoern A. Zeeb 	report->peak_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
513*dd4f32aeSBjoern A. Zeeb 				     __le32_to_cpu(search->info0));
514*dd4f32aeSBjoern A. Zeeb 	report->chain_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX,
515*dd4f32aeSBjoern A. Zeeb 				      __le32_to_cpu(search->info0));
516*dd4f32aeSBjoern A. Zeeb 	report->base_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB,
517*dd4f32aeSBjoern A. Zeeb 					__le32_to_cpu(search->info1));
518*dd4f32aeSBjoern A. Zeeb 	report->total_gain_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB,
519*dd4f32aeSBjoern A. Zeeb 					  __le32_to_cpu(search->info1));
520*dd4f32aeSBjoern A. Zeeb 	report->strong_bin_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS,
521*dd4f32aeSBjoern A. Zeeb 					     __le32_to_cpu(search->info2));
522*dd4f32aeSBjoern A. Zeeb 	report->peak_mag = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE,
523*dd4f32aeSBjoern A. Zeeb 				     __le32_to_cpu(search->info2));
524*dd4f32aeSBjoern A. Zeeb 	report->avg_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB,
525*dd4f32aeSBjoern A. Zeeb 				       __le32_to_cpu(search->info2));
526*dd4f32aeSBjoern A. Zeeb 	report->rel_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB,
527*dd4f32aeSBjoern A. Zeeb 				       __le32_to_cpu(search->info2));
528*dd4f32aeSBjoern A. Zeeb 
529*dd4f32aeSBjoern A. Zeeb 	return 0;
530*dd4f32aeSBjoern A. Zeeb }
531*dd4f32aeSBjoern A. Zeeb 
532*dd4f32aeSBjoern A. Zeeb static u8 ath11k_spectral_get_max_exp(s8 max_index, u8 max_magnitude,
533*dd4f32aeSBjoern A. Zeeb 				      int bin_len, u8 *bins)
534*dd4f32aeSBjoern A. Zeeb {
535*dd4f32aeSBjoern A. Zeeb 	int dc_pos;
536*dd4f32aeSBjoern A. Zeeb 	u8 max_exp;
537*dd4f32aeSBjoern A. Zeeb 
538*dd4f32aeSBjoern A. Zeeb 	dc_pos = bin_len / 2;
539*dd4f32aeSBjoern A. Zeeb 
540*dd4f32aeSBjoern A. Zeeb 	/* peak index outside of bins */
541*dd4f32aeSBjoern A. Zeeb 	if (dc_pos <= max_index || -dc_pos >= max_index)
542*dd4f32aeSBjoern A. Zeeb 		return 0;
543*dd4f32aeSBjoern A. Zeeb 
544*dd4f32aeSBjoern A. Zeeb 	for (max_exp = 0; max_exp < 8; max_exp++) {
545*dd4f32aeSBjoern A. Zeeb 		if (bins[dc_pos + max_index] == (max_magnitude >> max_exp))
546*dd4f32aeSBjoern A. Zeeb 			break;
547*dd4f32aeSBjoern A. Zeeb 	}
548*dd4f32aeSBjoern A. Zeeb 
549*dd4f32aeSBjoern A. Zeeb 	/* max_exp not found */
550*dd4f32aeSBjoern A. Zeeb 	if (bins[dc_pos + max_index] != (max_magnitude >> max_exp))
551*dd4f32aeSBjoern A. Zeeb 		return 0;
552*dd4f32aeSBjoern A. Zeeb 
553*dd4f32aeSBjoern A. Zeeb 	return max_exp;
554*dd4f32aeSBjoern A. Zeeb }
555*dd4f32aeSBjoern A. Zeeb 
556*dd4f32aeSBjoern A. Zeeb static void ath11k_spectral_parse_fft(u8 *outbins, u8 *inbins, int num_bins, u8 fft_sz)
557*dd4f32aeSBjoern A. Zeeb {
558*dd4f32aeSBjoern A. Zeeb 	int i, j;
559*dd4f32aeSBjoern A. Zeeb 
560*dd4f32aeSBjoern A. Zeeb 	i = 0;
561*dd4f32aeSBjoern A. Zeeb 	j = 0;
562*dd4f32aeSBjoern A. Zeeb 	while (i < num_bins) {
563*dd4f32aeSBjoern A. Zeeb 		outbins[i] = inbins[j];
564*dd4f32aeSBjoern A. Zeeb 		i++;
565*dd4f32aeSBjoern A. Zeeb 		j += fft_sz;
566*dd4f32aeSBjoern A. Zeeb 	}
567*dd4f32aeSBjoern A. Zeeb }
568*dd4f32aeSBjoern A. Zeeb 
569*dd4f32aeSBjoern A. Zeeb static
570*dd4f32aeSBjoern A. Zeeb int ath11k_spectral_process_fft(struct ath11k *ar,
571*dd4f32aeSBjoern A. Zeeb 				struct ath11k_spectral_summary_report *summary,
572*dd4f32aeSBjoern A. Zeeb 				void *data,
573*dd4f32aeSBjoern A. Zeeb 				struct fft_sample_ath11k *fft_sample,
574*dd4f32aeSBjoern A. Zeeb 				u32 data_len)
575*dd4f32aeSBjoern A. Zeeb {
576*dd4f32aeSBjoern A. Zeeb 	struct ath11k_base *ab = ar->ab;
577*dd4f32aeSBjoern A. Zeeb 	struct spectral_search_fft_report *fft_report = data;
578*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral_search_report search;
579*dd4f32aeSBjoern A. Zeeb 	struct spectral_tlv *tlv;
580*dd4f32aeSBjoern A. Zeeb 	int tlv_len, bin_len, num_bins;
581*dd4f32aeSBjoern A. Zeeb 	u16 length, freq;
582*dd4f32aeSBjoern A. Zeeb 	u8 chan_width_mhz, bin_sz;
583*dd4f32aeSBjoern A. Zeeb 	int ret;
584*dd4f32aeSBjoern A. Zeeb 	u32 check_length;
585*dd4f32aeSBjoern A. Zeeb 
586*dd4f32aeSBjoern A. Zeeb 	lockdep_assert_held(&ar->spectral.lock);
587*dd4f32aeSBjoern A. Zeeb 
588*dd4f32aeSBjoern A. Zeeb 	if (!ab->hw_params.spectral.fft_sz) {
589*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "invalid bin size type for hw rev %d\n",
590*dd4f32aeSBjoern A. Zeeb 			    ab->hw_rev);
591*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
592*dd4f32aeSBjoern A. Zeeb 	}
593*dd4f32aeSBjoern A. Zeeb 
594*dd4f32aeSBjoern A. Zeeb 	tlv = (struct spectral_tlv *)data;
595*dd4f32aeSBjoern A. Zeeb 	tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header));
596*dd4f32aeSBjoern A. Zeeb 	/* convert Dword into bytes */
597*dd4f32aeSBjoern A. Zeeb 	tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
598*dd4f32aeSBjoern A. Zeeb 	bin_len = tlv_len - ab->hw_params.spectral.fft_hdr_len;
599*dd4f32aeSBjoern A. Zeeb 
600*dd4f32aeSBjoern A. Zeeb 	if (data_len < (bin_len + sizeof(*fft_report))) {
601*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n",
602*dd4f32aeSBjoern A. Zeeb 			    bin_len, data_len);
603*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
604*dd4f32aeSBjoern A. Zeeb 	}
605*dd4f32aeSBjoern A. Zeeb 
606*dd4f32aeSBjoern A. Zeeb 	bin_sz = ab->hw_params.spectral.fft_sz + ab->hw_params.spectral.fft_pad_sz;
607*dd4f32aeSBjoern A. Zeeb 	num_bins = bin_len / bin_sz;
608*dd4f32aeSBjoern A. Zeeb 	/* Only In-band bins are useful to user for visualize */
609*dd4f32aeSBjoern A. Zeeb 	num_bins >>= 1;
610*dd4f32aeSBjoern A. Zeeb 
611*dd4f32aeSBjoern A. Zeeb 	if (num_bins < ATH11K_SPECTRAL_MIN_IB_BINS ||
612*dd4f32aeSBjoern A. Zeeb 	    num_bins > ATH11K_SPECTRAL_MAX_IB_BINS(ab) ||
613*dd4f32aeSBjoern A. Zeeb 	    !is_power_of_2(num_bins)) {
614*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "Invalid num of bins %d\n", num_bins);
615*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
616*dd4f32aeSBjoern A. Zeeb 	}
617*dd4f32aeSBjoern A. Zeeb 
618*dd4f32aeSBjoern A. Zeeb 	check_length = sizeof(*fft_report) + (num_bins * ab->hw_params.spectral.fft_sz);
619*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_dbring_validate_buffer(ar, data, check_length);
620*dd4f32aeSBjoern A. Zeeb 	if (ret) {
621*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "found magic value in fft data, dropping\n");
622*dd4f32aeSBjoern A. Zeeb 		return ret;
623*dd4f32aeSBjoern A. Zeeb 	}
624*dd4f32aeSBjoern A. Zeeb 
625*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_spectral_pull_search(ar, data, &search);
626*dd4f32aeSBjoern A. Zeeb 	if (ret) {
627*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to pull search report %d\n", ret);
628*dd4f32aeSBjoern A. Zeeb 		return ret;
629*dd4f32aeSBjoern A. Zeeb 	}
630*dd4f32aeSBjoern A. Zeeb 
631*dd4f32aeSBjoern A. Zeeb 	chan_width_mhz = summary->meta.ch_width;
632*dd4f32aeSBjoern A. Zeeb 
633*dd4f32aeSBjoern A. Zeeb 	switch (chan_width_mhz) {
634*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_20MHZ:
635*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_40MHZ:
636*dd4f32aeSBjoern A. Zeeb 	case ATH11K_SPECTRAL_80MHZ:
637*dd4f32aeSBjoern A. Zeeb 		fft_sample->chan_width_mhz = chan_width_mhz;
638*dd4f32aeSBjoern A. Zeeb 		break;
639*dd4f32aeSBjoern A. Zeeb 	default:
640*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "invalid channel width %d\n", chan_width_mhz);
641*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
642*dd4f32aeSBjoern A. Zeeb 	}
643*dd4f32aeSBjoern A. Zeeb 
644*dd4f32aeSBjoern A. Zeeb 	length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + num_bins;
645*dd4f32aeSBjoern A. Zeeb 	fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH11K;
646*dd4f32aeSBjoern A. Zeeb 	fft_sample->tlv.length = __cpu_to_be16(length);
647*dd4f32aeSBjoern A. Zeeb 
648*dd4f32aeSBjoern A. Zeeb 	fft_sample->tsf = __cpu_to_be32(search.timestamp);
649*dd4f32aeSBjoern A. Zeeb 	fft_sample->max_magnitude = __cpu_to_be16(search.peak_mag);
650*dd4f32aeSBjoern A. Zeeb 	fft_sample->max_index = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
651*dd4f32aeSBjoern A. Zeeb 					  __le32_to_cpu(fft_report->info0));
652*dd4f32aeSBjoern A. Zeeb 
653*dd4f32aeSBjoern A. Zeeb 	summary->inb_pwr_db >>= 1;
654*dd4f32aeSBjoern A. Zeeb 	fft_sample->rssi = __cpu_to_be16(summary->inb_pwr_db);
655*dd4f32aeSBjoern A. Zeeb 	fft_sample->noise = __cpu_to_be32(summary->meta.noise_floor[search.chain_idx]);
656*dd4f32aeSBjoern A. Zeeb 
657*dd4f32aeSBjoern A. Zeeb 	freq = summary->meta.freq1;
658*dd4f32aeSBjoern A. Zeeb 	fft_sample->freq1 = __cpu_to_be16(freq);
659*dd4f32aeSBjoern A. Zeeb 
660*dd4f32aeSBjoern A. Zeeb 	freq = summary->meta.freq2;
661*dd4f32aeSBjoern A. Zeeb 	fft_sample->freq2 = __cpu_to_be16(freq);
662*dd4f32aeSBjoern A. Zeeb 
663*dd4f32aeSBjoern A. Zeeb 	ath11k_spectral_parse_fft(fft_sample->data, fft_report->bins, num_bins,
664*dd4f32aeSBjoern A. Zeeb 				  ab->hw_params.spectral.fft_sz);
665*dd4f32aeSBjoern A. Zeeb 
666*dd4f32aeSBjoern A. Zeeb 	fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index,
667*dd4f32aeSBjoern A. Zeeb 							  search.peak_mag,
668*dd4f32aeSBjoern A. Zeeb 							  num_bins,
669*dd4f32aeSBjoern A. Zeeb 							  fft_sample->data);
670*dd4f32aeSBjoern A. Zeeb 
671*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.rfs_scan)
672*dd4f32aeSBjoern A. Zeeb 		relay_write(ar->spectral.rfs_scan, fft_sample,
673*dd4f32aeSBjoern A. Zeeb 			    length + sizeof(struct fft_sample_tlv));
674*dd4f32aeSBjoern A. Zeeb 
675*dd4f32aeSBjoern A. Zeeb 	return 0;
676*dd4f32aeSBjoern A. Zeeb }
677*dd4f32aeSBjoern A. Zeeb 
678*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_process_data(struct ath11k *ar,
679*dd4f32aeSBjoern A. Zeeb 					struct ath11k_dbring_data *param)
680*dd4f32aeSBjoern A. Zeeb {
681*dd4f32aeSBjoern A. Zeeb 	struct ath11k_base *ab = ar->ab;
682*dd4f32aeSBjoern A. Zeeb 	struct spectral_tlv *tlv;
683*dd4f32aeSBjoern A. Zeeb 	struct spectral_summary_fft_report *summary = NULL;
684*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral_summary_report summ_rpt;
685*dd4f32aeSBjoern A. Zeeb 	struct fft_sample_ath11k *fft_sample = NULL;
686*dd4f32aeSBjoern A. Zeeb 	u8 *data;
687*dd4f32aeSBjoern A. Zeeb 	u32 data_len, i;
688*dd4f32aeSBjoern A. Zeeb 	u8 sign, tag;
689*dd4f32aeSBjoern A. Zeeb 	int tlv_len, sample_sz;
690*dd4f32aeSBjoern A. Zeeb 	int ret;
691*dd4f32aeSBjoern A. Zeeb 	bool quit = false;
692*dd4f32aeSBjoern A. Zeeb 
693*dd4f32aeSBjoern A. Zeeb 	spin_lock_bh(&ar->spectral.lock);
694*dd4f32aeSBjoern A. Zeeb 
695*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.enabled) {
696*dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
697*dd4f32aeSBjoern A. Zeeb 		goto unlock;
698*dd4f32aeSBjoern A. Zeeb 	}
699*dd4f32aeSBjoern A. Zeeb 
700*dd4f32aeSBjoern A. Zeeb 	sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_MAX_IB_BINS(ab);
701*dd4f32aeSBjoern A. Zeeb 	fft_sample = kmalloc(sample_sz, GFP_ATOMIC);
702*dd4f32aeSBjoern A. Zeeb 	if (!fft_sample) {
703*dd4f32aeSBjoern A. Zeeb 		ret = -ENOBUFS;
704*dd4f32aeSBjoern A. Zeeb 		goto unlock;
705*dd4f32aeSBjoern A. Zeeb 	}
706*dd4f32aeSBjoern A. Zeeb 
707*dd4f32aeSBjoern A. Zeeb 	data = param->data;
708*dd4f32aeSBjoern A. Zeeb 	data_len = param->data_sz;
709*dd4f32aeSBjoern A. Zeeb 	i = 0;
710*dd4f32aeSBjoern A. Zeeb 	while (!quit && (i < data_len)) {
711*dd4f32aeSBjoern A. Zeeb 		if ((i + sizeof(*tlv)) > data_len) {
712*dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "failed to parse spectral tlv hdr at bytes %d\n",
713*dd4f32aeSBjoern A. Zeeb 				    i);
714*dd4f32aeSBjoern A. Zeeb 			ret = -EINVAL;
715*dd4f32aeSBjoern A. Zeeb 			goto err;
716*dd4f32aeSBjoern A. Zeeb 		}
717*dd4f32aeSBjoern A. Zeeb 
718*dd4f32aeSBjoern A. Zeeb 		tlv = (struct spectral_tlv *)&data[i];
719*dd4f32aeSBjoern A. Zeeb 		sign = FIELD_GET(SPECTRAL_TLV_HDR_SIGN,
720*dd4f32aeSBjoern A. Zeeb 				 __le32_to_cpu(tlv->header));
721*dd4f32aeSBjoern A. Zeeb 		if (sign != ATH11K_SPECTRAL_SIGNATURE) {
722*dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "Invalid sign 0x%x at bytes %d\n",
723*dd4f32aeSBjoern A. Zeeb 				    sign, i);
724*dd4f32aeSBjoern A. Zeeb 			ret = -EINVAL;
725*dd4f32aeSBjoern A. Zeeb 			goto err;
726*dd4f32aeSBjoern A. Zeeb 		}
727*dd4f32aeSBjoern A. Zeeb 
728*dd4f32aeSBjoern A. Zeeb 		tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN,
729*dd4f32aeSBjoern A. Zeeb 				    __le32_to_cpu(tlv->header));
730*dd4f32aeSBjoern A. Zeeb 		/* convert Dword into bytes */
731*dd4f32aeSBjoern A. Zeeb 		tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
732*dd4f32aeSBjoern A. Zeeb 		if ((i + sizeof(*tlv) + tlv_len) > data_len) {
733*dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "failed to parse spectral tlv payload at bytes %d tlv_len:%d data_len:%d\n",
734*dd4f32aeSBjoern A. Zeeb 				    i, tlv_len, data_len);
735*dd4f32aeSBjoern A. Zeeb 			ret = -EINVAL;
736*dd4f32aeSBjoern A. Zeeb 			goto err;
737*dd4f32aeSBjoern A. Zeeb 		}
738*dd4f32aeSBjoern A. Zeeb 
739*dd4f32aeSBjoern A. Zeeb 		tag = FIELD_GET(SPECTRAL_TLV_HDR_TAG,
740*dd4f32aeSBjoern A. Zeeb 				__le32_to_cpu(tlv->header));
741*dd4f32aeSBjoern A. Zeeb 		switch (tag) {
742*dd4f32aeSBjoern A. Zeeb 		case ATH11K_SPECTRAL_TAG_SCAN_SUMMARY:
743*dd4f32aeSBjoern A. Zeeb 			/* HW bug in tlv length of summary report,
744*dd4f32aeSBjoern A. Zeeb 			 * HW report 3 DWORD size but the data payload
745*dd4f32aeSBjoern A. Zeeb 			 * is 4 DWORD size (16 bytes).
746*dd4f32aeSBjoern A. Zeeb 			 * Need to remove this workaround once HW bug fixed
747*dd4f32aeSBjoern A. Zeeb 			 */
748*dd4f32aeSBjoern A. Zeeb 			tlv_len = sizeof(*summary) - sizeof(*tlv) +
749*dd4f32aeSBjoern A. Zeeb 				  ab->hw_params.spectral.summary_pad_sz;
750*dd4f32aeSBjoern A. Zeeb 
751*dd4f32aeSBjoern A. Zeeb 			if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) {
752*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n",
753*dd4f32aeSBjoern A. Zeeb 					    i, tlv_len);
754*dd4f32aeSBjoern A. Zeeb 				ret = -EINVAL;
755*dd4f32aeSBjoern A. Zeeb 				goto err;
756*dd4f32aeSBjoern A. Zeeb 			}
757*dd4f32aeSBjoern A. Zeeb 
758*dd4f32aeSBjoern A. Zeeb 			ret = ath11k_dbring_validate_buffer(ar, data, tlv_len);
759*dd4f32aeSBjoern A. Zeeb 			if (ret) {
760*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ar->ab, "found magic value in spectral summary, dropping\n");
761*dd4f32aeSBjoern A. Zeeb 				goto err;
762*dd4f32aeSBjoern A. Zeeb 			}
763*dd4f32aeSBjoern A. Zeeb 
764*dd4f32aeSBjoern A. Zeeb 			summary = (struct spectral_summary_fft_report *)tlv;
765*dd4f32aeSBjoern A. Zeeb 			ath11k_spectral_pull_summary(ar, &param->meta,
766*dd4f32aeSBjoern A. Zeeb 						     summary, &summ_rpt);
767*dd4f32aeSBjoern A. Zeeb 			break;
768*dd4f32aeSBjoern A. Zeeb 		case ATH11K_SPECTRAL_TAG_SCAN_SEARCH:
769*dd4f32aeSBjoern A. Zeeb 			if (tlv_len < (sizeof(struct spectral_search_fft_report) -
770*dd4f32aeSBjoern A. Zeeb 				       sizeof(*tlv))) {
771*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ab, "failed to parse spectral search fft at bytes %d\n",
772*dd4f32aeSBjoern A. Zeeb 					    i);
773*dd4f32aeSBjoern A. Zeeb 				ret = -EINVAL;
774*dd4f32aeSBjoern A. Zeeb 				goto err;
775*dd4f32aeSBjoern A. Zeeb 			}
776*dd4f32aeSBjoern A. Zeeb 
777*dd4f32aeSBjoern A. Zeeb 			memset(fft_sample, 0, sample_sz);
778*dd4f32aeSBjoern A. Zeeb 			ret = ath11k_spectral_process_fft(ar, &summ_rpt, tlv,
779*dd4f32aeSBjoern A. Zeeb 							  fft_sample,
780*dd4f32aeSBjoern A. Zeeb 							  data_len - i);
781*dd4f32aeSBjoern A. Zeeb 			if (ret) {
782*dd4f32aeSBjoern A. Zeeb 				ath11k_warn(ab, "failed to process spectral fft at bytes %d\n",
783*dd4f32aeSBjoern A. Zeeb 					    i);
784*dd4f32aeSBjoern A. Zeeb 				goto err;
785*dd4f32aeSBjoern A. Zeeb 			}
786*dd4f32aeSBjoern A. Zeeb 			quit = true;
787*dd4f32aeSBjoern A. Zeeb 			break;
788*dd4f32aeSBjoern A. Zeeb 		}
789*dd4f32aeSBjoern A. Zeeb 
790*dd4f32aeSBjoern A. Zeeb 		i += sizeof(*tlv) + tlv_len;
791*dd4f32aeSBjoern A. Zeeb 	}
792*dd4f32aeSBjoern A. Zeeb 
793*dd4f32aeSBjoern A. Zeeb 	ret = 0;
794*dd4f32aeSBjoern A. Zeeb 
795*dd4f32aeSBjoern A. Zeeb err:
796*dd4f32aeSBjoern A. Zeeb 	kfree(fft_sample);
797*dd4f32aeSBjoern A. Zeeb unlock:
798*dd4f32aeSBjoern A. Zeeb 	spin_unlock_bh(&ar->spectral.lock);
799*dd4f32aeSBjoern A. Zeeb 	return ret;
800*dd4f32aeSBjoern A. Zeeb }
801*dd4f32aeSBjoern A. Zeeb 
802*dd4f32aeSBjoern A. Zeeb static int ath11k_spectral_ring_alloc(struct ath11k *ar,
803*dd4f32aeSBjoern A. Zeeb 				      struct ath11k_dbring_cap *db_cap)
804*dd4f32aeSBjoern A. Zeeb {
805*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral *sp = &ar->spectral;
806*dd4f32aeSBjoern A. Zeeb 	int ret;
807*dd4f32aeSBjoern A. Zeeb 
808*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_dbring_srng_setup(ar, &sp->rx_ring,
809*dd4f32aeSBjoern A. Zeeb 				       0, db_cap->min_elem);
810*dd4f32aeSBjoern A. Zeeb 	if (ret) {
811*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to setup db ring\n");
812*dd4f32aeSBjoern A. Zeeb 		return ret;
813*dd4f32aeSBjoern A. Zeeb 	}
814*dd4f32aeSBjoern A. Zeeb 
815*dd4f32aeSBjoern A. Zeeb 	ath11k_dbring_set_cfg(ar, &sp->rx_ring,
816*dd4f32aeSBjoern A. Zeeb 			      ATH11K_SPECTRAL_NUM_RESP_PER_EVENT,
817*dd4f32aeSBjoern A. Zeeb 			      ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,
818*dd4f32aeSBjoern A. Zeeb 			      ath11k_spectral_process_data);
819*dd4f32aeSBjoern A. Zeeb 
820*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap);
821*dd4f32aeSBjoern A. Zeeb 	if (ret) {
822*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to setup db ring buffer\n");
823*dd4f32aeSBjoern A. Zeeb 		goto srng_cleanup;
824*dd4f32aeSBjoern A. Zeeb 	}
825*dd4f32aeSBjoern A. Zeeb 
826*dd4f32aeSBjoern A. Zeeb 	ret = ath11k_dbring_wmi_cfg_setup(ar, &sp->rx_ring,
827*dd4f32aeSBjoern A. Zeeb 					  WMI_DIRECT_BUF_SPECTRAL);
828*dd4f32aeSBjoern A. Zeeb 	if (ret) {
829*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
830*dd4f32aeSBjoern A. Zeeb 		goto buffer_cleanup;
831*dd4f32aeSBjoern A. Zeeb 	}
832*dd4f32aeSBjoern A. Zeeb 
833*dd4f32aeSBjoern A. Zeeb 	return 0;
834*dd4f32aeSBjoern A. Zeeb 
835*dd4f32aeSBjoern A. Zeeb buffer_cleanup:
836*dd4f32aeSBjoern A. Zeeb 	ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
837*dd4f32aeSBjoern A. Zeeb srng_cleanup:
838*dd4f32aeSBjoern A. Zeeb 	ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
839*dd4f32aeSBjoern A. Zeeb 	return ret;
840*dd4f32aeSBjoern A. Zeeb }
841*dd4f32aeSBjoern A. Zeeb 
842*dd4f32aeSBjoern A. Zeeb static inline void ath11k_spectral_ring_free(struct ath11k *ar)
843*dd4f32aeSBjoern A. Zeeb {
844*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral *sp = &ar->spectral;
845*dd4f32aeSBjoern A. Zeeb 
846*dd4f32aeSBjoern A. Zeeb 	if (!sp->enabled)
847*dd4f32aeSBjoern A. Zeeb 		return;
848*dd4f32aeSBjoern A. Zeeb 
849*dd4f32aeSBjoern A. Zeeb 	ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
850*dd4f32aeSBjoern A. Zeeb 	ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
851*dd4f32aeSBjoern A. Zeeb }
852*dd4f32aeSBjoern A. Zeeb 
853*dd4f32aeSBjoern A. Zeeb static inline void ath11k_spectral_debug_unregister(struct ath11k *ar)
854*dd4f32aeSBjoern A. Zeeb {
855*dd4f32aeSBjoern A. Zeeb 	debugfs_remove(ar->spectral.scan_bins);
856*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_bins = NULL;
857*dd4f32aeSBjoern A. Zeeb 
858*dd4f32aeSBjoern A. Zeeb 	debugfs_remove(ar->spectral.scan_count);
859*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_count = NULL;
860*dd4f32aeSBjoern A. Zeeb 
861*dd4f32aeSBjoern A. Zeeb 	debugfs_remove(ar->spectral.scan_ctl);
862*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_ctl = NULL;
863*dd4f32aeSBjoern A. Zeeb 
864*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.rfs_scan) {
865*dd4f32aeSBjoern A. Zeeb 		relay_close(ar->spectral.rfs_scan);
866*dd4f32aeSBjoern A. Zeeb 		ar->spectral.rfs_scan = NULL;
867*dd4f32aeSBjoern A. Zeeb 	}
868*dd4f32aeSBjoern A. Zeeb }
869*dd4f32aeSBjoern A. Zeeb 
870*dd4f32aeSBjoern A. Zeeb int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
871*dd4f32aeSBjoern A. Zeeb {
872*dd4f32aeSBjoern A. Zeeb 	if (!arvif->spectral_enabled)
873*dd4f32aeSBjoern A. Zeeb 		return 0;
874*dd4f32aeSBjoern A. Zeeb 
875*dd4f32aeSBjoern A. Zeeb 	return ath11k_spectral_scan_config(arvif->ar, ATH11K_SPECTRAL_DISABLED);
876*dd4f32aeSBjoern A. Zeeb }
877*dd4f32aeSBjoern A. Zeeb 
878*dd4f32aeSBjoern A. Zeeb void ath11k_spectral_reset_buffer(struct ath11k *ar)
879*dd4f32aeSBjoern A. Zeeb {
880*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.enabled)
881*dd4f32aeSBjoern A. Zeeb 		return;
882*dd4f32aeSBjoern A. Zeeb 
883*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.rfs_scan)
884*dd4f32aeSBjoern A. Zeeb 		relay_reset(ar->spectral.rfs_scan);
885*dd4f32aeSBjoern A. Zeeb }
886*dd4f32aeSBjoern A. Zeeb 
887*dd4f32aeSBjoern A. Zeeb void ath11k_spectral_deinit(struct ath11k_base *ab)
888*dd4f32aeSBjoern A. Zeeb {
889*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar;
890*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral *sp;
891*dd4f32aeSBjoern A. Zeeb 	int i;
892*dd4f32aeSBjoern A. Zeeb 
893*dd4f32aeSBjoern A. Zeeb 	for (i = 0; i <  ab->num_radios; i++) {
894*dd4f32aeSBjoern A. Zeeb 		ar = ab->pdevs[i].ar;
895*dd4f32aeSBjoern A. Zeeb 		sp = &ar->spectral;
896*dd4f32aeSBjoern A. Zeeb 
897*dd4f32aeSBjoern A. Zeeb 		if (!sp->enabled)
898*dd4f32aeSBjoern A. Zeeb 			continue;
899*dd4f32aeSBjoern A. Zeeb 
900*dd4f32aeSBjoern A. Zeeb 		ath11k_spectral_debug_unregister(ar);
901*dd4f32aeSBjoern A. Zeeb 		ath11k_spectral_ring_free(ar);
902*dd4f32aeSBjoern A. Zeeb 
903*dd4f32aeSBjoern A. Zeeb 		spin_lock_bh(&sp->lock);
904*dd4f32aeSBjoern A. Zeeb 
905*dd4f32aeSBjoern A. Zeeb 		sp->mode = ATH11K_SPECTRAL_DISABLED;
906*dd4f32aeSBjoern A. Zeeb 		sp->enabled = false;
907*dd4f32aeSBjoern A. Zeeb 
908*dd4f32aeSBjoern A. Zeeb 		spin_unlock_bh(&sp->lock);
909*dd4f32aeSBjoern A. Zeeb 	}
910*dd4f32aeSBjoern A. Zeeb }
911*dd4f32aeSBjoern A. Zeeb 
912*dd4f32aeSBjoern A. Zeeb static inline int ath11k_spectral_debug_register(struct ath11k *ar)
913*dd4f32aeSBjoern A. Zeeb {
914*dd4f32aeSBjoern A. Zeeb 	int ret;
915*dd4f32aeSBjoern A. Zeeb 
916*dd4f32aeSBjoern A. Zeeb 	ar->spectral.rfs_scan = relay_open("spectral_scan",
917*dd4f32aeSBjoern A. Zeeb 					   ar->debug.debugfs_pdev,
918*dd4f32aeSBjoern A. Zeeb 					   ATH11K_SPECTRAL_SUB_BUFF_SIZE(ar->ab),
919*dd4f32aeSBjoern A. Zeeb 					   ATH11K_SPECTRAL_NUM_SUB_BUF,
920*dd4f32aeSBjoern A. Zeeb 					   &rfs_scan_cb, NULL);
921*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.rfs_scan) {
922*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to open relay in pdev %d\n",
923*dd4f32aeSBjoern A. Zeeb 			    ar->pdev_idx);
924*dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
925*dd4f32aeSBjoern A. Zeeb 	}
926*dd4f32aeSBjoern A. Zeeb 
927*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_ctl = debugfs_create_file("spectral_scan_ctl",
928*dd4f32aeSBjoern A. Zeeb 						    0600,
929*dd4f32aeSBjoern A. Zeeb 						    ar->debug.debugfs_pdev, ar,
930*dd4f32aeSBjoern A. Zeeb 						    &fops_scan_ctl);
931*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.scan_ctl) {
932*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
933*dd4f32aeSBjoern A. Zeeb 			    ar->pdev_idx);
934*dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
935*dd4f32aeSBjoern A. Zeeb 		goto debug_unregister;
936*dd4f32aeSBjoern A. Zeeb 	}
937*dd4f32aeSBjoern A. Zeeb 
938*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_count = debugfs_create_file("spectral_count",
939*dd4f32aeSBjoern A. Zeeb 						      0600,
940*dd4f32aeSBjoern A. Zeeb 						      ar->debug.debugfs_pdev, ar,
941*dd4f32aeSBjoern A. Zeeb 						      &fops_scan_count);
942*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.scan_count) {
943*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
944*dd4f32aeSBjoern A. Zeeb 			    ar->pdev_idx);
945*dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
946*dd4f32aeSBjoern A. Zeeb 		goto debug_unregister;
947*dd4f32aeSBjoern A. Zeeb 	}
948*dd4f32aeSBjoern A. Zeeb 
949*dd4f32aeSBjoern A. Zeeb 	ar->spectral.scan_bins = debugfs_create_file("spectral_bins",
950*dd4f32aeSBjoern A. Zeeb 						     0600,
951*dd4f32aeSBjoern A. Zeeb 						     ar->debug.debugfs_pdev, ar,
952*dd4f32aeSBjoern A. Zeeb 						     &fops_scan_bins);
953*dd4f32aeSBjoern A. Zeeb 	if (!ar->spectral.scan_bins) {
954*dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
955*dd4f32aeSBjoern A. Zeeb 			    ar->pdev_idx);
956*dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
957*dd4f32aeSBjoern A. Zeeb 		goto debug_unregister;
958*dd4f32aeSBjoern A. Zeeb 	}
959*dd4f32aeSBjoern A. Zeeb 
960*dd4f32aeSBjoern A. Zeeb 	return 0;
961*dd4f32aeSBjoern A. Zeeb 
962*dd4f32aeSBjoern A. Zeeb debug_unregister:
963*dd4f32aeSBjoern A. Zeeb 	ath11k_spectral_debug_unregister(ar);
964*dd4f32aeSBjoern A. Zeeb 	return ret;
965*dd4f32aeSBjoern A. Zeeb }
966*dd4f32aeSBjoern A. Zeeb 
967*dd4f32aeSBjoern A. Zeeb int ath11k_spectral_init(struct ath11k_base *ab)
968*dd4f32aeSBjoern A. Zeeb {
969*dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar;
970*dd4f32aeSBjoern A. Zeeb 	struct ath11k_spectral *sp;
971*dd4f32aeSBjoern A. Zeeb 	struct ath11k_dbring_cap db_cap;
972*dd4f32aeSBjoern A. Zeeb 	int ret;
973*dd4f32aeSBjoern A. Zeeb 	int i;
974*dd4f32aeSBjoern A. Zeeb 
975*dd4f32aeSBjoern A. Zeeb 	if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA,
976*dd4f32aeSBjoern A. Zeeb 		      ab->wmi_ab.svc_map))
977*dd4f32aeSBjoern A. Zeeb 		return 0;
978*dd4f32aeSBjoern A. Zeeb 
979*dd4f32aeSBjoern A. Zeeb 	if (!ab->hw_params.spectral.fft_sz)
980*dd4f32aeSBjoern A. Zeeb 		return 0;
981*dd4f32aeSBjoern A. Zeeb 
982*dd4f32aeSBjoern A. Zeeb 	for (i = 0; i < ab->num_radios; i++) {
983*dd4f32aeSBjoern A. Zeeb 		ar = ab->pdevs[i].ar;
984*dd4f32aeSBjoern A. Zeeb 		sp = &ar->spectral;
985*dd4f32aeSBjoern A. Zeeb 
986*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
987*dd4f32aeSBjoern A. Zeeb 					    WMI_DIRECT_BUF_SPECTRAL,
988*dd4f32aeSBjoern A. Zeeb 					    &db_cap);
989*dd4f32aeSBjoern A. Zeeb 		if (ret)
990*dd4f32aeSBjoern A. Zeeb 			continue;
991*dd4f32aeSBjoern A. Zeeb 
992*dd4f32aeSBjoern A. Zeeb 		idr_init(&sp->rx_ring.bufs_idr);
993*dd4f32aeSBjoern A. Zeeb 		spin_lock_init(&sp->rx_ring.idr_lock);
994*dd4f32aeSBjoern A. Zeeb 		spin_lock_init(&sp->lock);
995*dd4f32aeSBjoern A. Zeeb 
996*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_spectral_ring_alloc(ar, &db_cap);
997*dd4f32aeSBjoern A. Zeeb 		if (ret) {
998*dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "failed to init spectral ring for pdev %d\n",
999*dd4f32aeSBjoern A. Zeeb 				    i);
1000*dd4f32aeSBjoern A. Zeeb 			goto deinit;
1001*dd4f32aeSBjoern A. Zeeb 		}
1002*dd4f32aeSBjoern A. Zeeb 
1003*dd4f32aeSBjoern A. Zeeb 		spin_lock_bh(&sp->lock);
1004*dd4f32aeSBjoern A. Zeeb 
1005*dd4f32aeSBjoern A. Zeeb 		sp->mode = ATH11K_SPECTRAL_DISABLED;
1006*dd4f32aeSBjoern A. Zeeb 		sp->count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
1007*dd4f32aeSBjoern A. Zeeb 		sp->fft_size = ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT;
1008*dd4f32aeSBjoern A. Zeeb 		sp->enabled = true;
1009*dd4f32aeSBjoern A. Zeeb 
1010*dd4f32aeSBjoern A. Zeeb 		spin_unlock_bh(&sp->lock);
1011*dd4f32aeSBjoern A. Zeeb 
1012*dd4f32aeSBjoern A. Zeeb 		ret = ath11k_spectral_debug_register(ar);
1013*dd4f32aeSBjoern A. Zeeb 		if (ret) {
1014*dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "failed to register spectral for pdev %d\n",
1015*dd4f32aeSBjoern A. Zeeb 				    i);
1016*dd4f32aeSBjoern A. Zeeb 			goto deinit;
1017*dd4f32aeSBjoern A. Zeeb 		}
1018*dd4f32aeSBjoern A. Zeeb 	}
1019*dd4f32aeSBjoern A. Zeeb 
1020*dd4f32aeSBjoern A. Zeeb 	return 0;
1021*dd4f32aeSBjoern A. Zeeb 
1022*dd4f32aeSBjoern A. Zeeb deinit:
1023*dd4f32aeSBjoern A. Zeeb 	ath11k_spectral_deinit(ab);
1024*dd4f32aeSBjoern A. Zeeb 	return ret;
1025*dd4f32aeSBjoern A. Zeeb }
1026*dd4f32aeSBjoern A. Zeeb 
1027*dd4f32aeSBjoern A. Zeeb enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
1028*dd4f32aeSBjoern A. Zeeb {
1029*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.enabled)
1030*dd4f32aeSBjoern A. Zeeb 		return ar->spectral.mode;
1031*dd4f32aeSBjoern A. Zeeb 	else
1032*dd4f32aeSBjoern A. Zeeb 		return ATH11K_SPECTRAL_DISABLED;
1033*dd4f32aeSBjoern A. Zeeb }
1034*dd4f32aeSBjoern A. Zeeb 
1035*dd4f32aeSBjoern A. Zeeb struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
1036*dd4f32aeSBjoern A. Zeeb {
1037*dd4f32aeSBjoern A. Zeeb 	if (ar->spectral.enabled)
1038*dd4f32aeSBjoern A. Zeeb 		return &ar->spectral.rx_ring;
1039*dd4f32aeSBjoern A. Zeeb 	else
1040*dd4f32aeSBjoern A. Zeeb 		return NULL;
1041*dd4f32aeSBjoern A. Zeeb }
1042