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, ¶m); 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, ¶m->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