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