1*0e3d6777SRyder Lee // SPDX-License-Identifier: ISC 2e6cb3291SLorenzo Bianconi /* 3e6cb3291SLorenzo Bianconi * Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 4e6cb3291SLorenzo Bianconi */ 5e6cb3291SLorenzo Bianconi 6e6cb3291SLorenzo Bianconi #include "mt76x02.h" 7e6cb3291SLorenzo Bianconi 8e6cb3291SLorenzo Bianconi #define RADAR_SPEC(m, len, el, eh, wl, wh, \ 9e6cb3291SLorenzo Bianconi w_tolerance, tl, th, t_tolerance, \ 10e6cb3291SLorenzo Bianconi bl, bh, event_exp, power_jmp) \ 11e6cb3291SLorenzo Bianconi { \ 12e6cb3291SLorenzo Bianconi .mode = m, \ 13e6cb3291SLorenzo Bianconi .avg_len = len, \ 14e6cb3291SLorenzo Bianconi .e_low = el, \ 15e6cb3291SLorenzo Bianconi .e_high = eh, \ 16e6cb3291SLorenzo Bianconi .w_low = wl, \ 17e6cb3291SLorenzo Bianconi .w_high = wh, \ 18e6cb3291SLorenzo Bianconi .w_margin = w_tolerance, \ 19e6cb3291SLorenzo Bianconi .t_low = tl, \ 20e6cb3291SLorenzo Bianconi .t_high = th, \ 21e6cb3291SLorenzo Bianconi .t_margin = t_tolerance, \ 22e6cb3291SLorenzo Bianconi .b_low = bl, \ 23e6cb3291SLorenzo Bianconi .b_high = bh, \ 24e6cb3291SLorenzo Bianconi .event_expiration = event_exp, \ 25e6cb3291SLorenzo Bianconi .pwr_jmp = power_jmp \ 26e6cb3291SLorenzo Bianconi } 27e6cb3291SLorenzo Bianconi 28e6cb3291SLorenzo Bianconi static const struct mt76x02_radar_specs etsi_radar_specs[] = { 29e6cb3291SLorenzo Bianconi /* 20MHz */ 30e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, 31e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 32e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, 33e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 34e6cb3291SLorenzo Bianconi RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, 35e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19dd), 36e6cb3291SLorenzo Bianconi RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, 37e6cb3291SLorenzo Bianconi 0x7fffffff, 0x2191c0, 0x15cc), 38e6cb3291SLorenzo Bianconi /* 40MHz */ 39e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, 40e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 41e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, 42e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 43e6cb3291SLorenzo Bianconi RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, 44e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19dd), 45e6cb3291SLorenzo Bianconi RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, 46e6cb3291SLorenzo Bianconi 0x7fffffff, 0x2191c0, 0x15cc), 47e6cb3291SLorenzo Bianconi /* 80MHz */ 48e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, 49e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 50e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, 51e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19cc), 52e6cb3291SLorenzo Bianconi RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, 53e6cb3291SLorenzo Bianconi 0x7fffffff, 0x155cc0, 0x19dd), 54e6cb3291SLorenzo Bianconi RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, 55e6cb3291SLorenzo Bianconi 0x7fffffff, 0x2191c0, 0x15cc) 56e6cb3291SLorenzo Bianconi }; 57e6cb3291SLorenzo Bianconi 58e6cb3291SLorenzo Bianconi static const struct mt76x02_radar_specs fcc_radar_specs[] = { 59e6cb3291SLorenzo Bianconi /* 20MHz */ 60e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0, 61e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x13dc), 62e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 63e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x19dd), 64e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, 65e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x12cc), 66e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, 67e6cb3291SLorenzo Bianconi 0x3938700, 0x57bcf00, 0x1289), 68e6cb3291SLorenzo Bianconi /* 40MHz */ 69e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0, 70e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x13dc), 71e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 72e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x19dd), 73e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, 74e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x12cc), 75e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, 76e6cb3291SLorenzo Bianconi 0x3938700, 0x57bcf00, 0x1289), 77e6cb3291SLorenzo Bianconi /* 80MHz */ 78e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0, 79e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x16cc), 80e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 81e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x19dd), 82e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, 83e6cb3291SLorenzo Bianconi 0x7fffffff, 0xfe808, 0x12cc), 84e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, 85e6cb3291SLorenzo Bianconi 0x3938700, 0x57bcf00, 0x1289) 86e6cb3291SLorenzo Bianconi }; 87e6cb3291SLorenzo Bianconi 88e6cb3291SLorenzo Bianconi static const struct mt76x02_radar_specs jp_w56_radar_specs[] = { 89e6cb3291SLorenzo Bianconi /* 20MHz */ 90e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0, 91e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x13dc), 92e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 93e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x19dd), 94e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, 95e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x12cc), 96e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, 97e6cb3291SLorenzo Bianconi 0x3938700, 0X57bcf00, 0x1289), 98e6cb3291SLorenzo Bianconi /* 40MHz */ 99e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0, 100e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x13dc), 101e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 102e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x19dd), 103e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, 104e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x12cc), 105e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, 106e6cb3291SLorenzo Bianconi 0x3938700, 0X57bcf00, 0x1289), 107e6cb3291SLorenzo Bianconi /* 80MHz */ 108e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0, 109e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 110e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, 111e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x19dd), 112e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, 113e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x12cc), 114e6cb3291SLorenzo Bianconi RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, 115e6cb3291SLorenzo Bianconi 0x3938700, 0X57bcf00, 0x1289) 116e6cb3291SLorenzo Bianconi }; 117e6cb3291SLorenzo Bianconi 118e6cb3291SLorenzo Bianconi static const struct mt76x02_radar_specs jp_w53_radar_specs[] = { 119e6cb3291SLorenzo Bianconi /* 20MHz */ 120e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, 121e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 122e6cb3291SLorenzo Bianconi { 0 }, 123e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, 124e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 125e6cb3291SLorenzo Bianconi { 0 }, 126e6cb3291SLorenzo Bianconi /* 40MHz */ 127e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, 128e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 129e6cb3291SLorenzo Bianconi { 0 }, 130e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, 131e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 132e6cb3291SLorenzo Bianconi { 0 }, 133e6cb3291SLorenzo Bianconi /* 80MHz */ 134e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, 135e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 136e6cb3291SLorenzo Bianconi { 0 }, 137e6cb3291SLorenzo Bianconi RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, 138e6cb3291SLorenzo Bianconi 0x7fffffff, 0x14c080, 0x16cc), 139e6cb3291SLorenzo Bianconi { 0 } 140e6cb3291SLorenzo Bianconi }; 141e6cb3291SLorenzo Bianconi 142e6cb3291SLorenzo Bianconi static void 143e6cb3291SLorenzo Bianconi mt76x02_dfs_set_capture_mode_ctrl(struct mt76x02_dev *dev, u8 enable) 144e6cb3291SLorenzo Bianconi { 145e6cb3291SLorenzo Bianconi u32 data; 146e6cb3291SLorenzo Bianconi 147e6cb3291SLorenzo Bianconi data = (1 << 1) | enable; 148e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 36), data); 149e6cb3291SLorenzo Bianconi } 150e6cb3291SLorenzo Bianconi 151e6cb3291SLorenzo Bianconi static void mt76x02_dfs_seq_pool_put(struct mt76x02_dev *dev, 152e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence *seq) 153e6cb3291SLorenzo Bianconi { 154e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 155e6cb3291SLorenzo Bianconi 156e6cb3291SLorenzo Bianconi list_add(&seq->head, &dfs_pd->seq_pool); 157e6cb3291SLorenzo Bianconi 158e6cb3291SLorenzo Bianconi dfs_pd->seq_stats.seq_pool_len++; 159e6cb3291SLorenzo Bianconi dfs_pd->seq_stats.seq_len--; 160e6cb3291SLorenzo Bianconi } 161e6cb3291SLorenzo Bianconi 162e6cb3291SLorenzo Bianconi static struct mt76x02_dfs_sequence * 163e6cb3291SLorenzo Bianconi mt76x02_dfs_seq_pool_get(struct mt76x02_dev *dev) 164e6cb3291SLorenzo Bianconi { 165e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 166e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence *seq; 167e6cb3291SLorenzo Bianconi 168e6cb3291SLorenzo Bianconi if (list_empty(&dfs_pd->seq_pool)) { 169e6cb3291SLorenzo Bianconi seq = devm_kzalloc(dev->mt76.dev, sizeof(*seq), GFP_ATOMIC); 170e6cb3291SLorenzo Bianconi } else { 171e6cb3291SLorenzo Bianconi seq = list_first_entry(&dfs_pd->seq_pool, 172e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence, 173e6cb3291SLorenzo Bianconi head); 174e6cb3291SLorenzo Bianconi list_del(&seq->head); 175e6cb3291SLorenzo Bianconi dfs_pd->seq_stats.seq_pool_len--; 176e6cb3291SLorenzo Bianconi } 177e6cb3291SLorenzo Bianconi if (seq) 178e6cb3291SLorenzo Bianconi dfs_pd->seq_stats.seq_len++; 179e6cb3291SLorenzo Bianconi 180e6cb3291SLorenzo Bianconi return seq; 181e6cb3291SLorenzo Bianconi } 182e6cb3291SLorenzo Bianconi 183e6cb3291SLorenzo Bianconi static int mt76x02_dfs_get_multiple(int val, int frac, int margin) 184e6cb3291SLorenzo Bianconi { 185e6cb3291SLorenzo Bianconi int remainder, factor; 186e6cb3291SLorenzo Bianconi 187e6cb3291SLorenzo Bianconi if (!frac) 188e6cb3291SLorenzo Bianconi return 0; 189e6cb3291SLorenzo Bianconi 190e6cb3291SLorenzo Bianconi if (abs(val - frac) <= margin) 191e6cb3291SLorenzo Bianconi return 1; 192e6cb3291SLorenzo Bianconi 193e6cb3291SLorenzo Bianconi factor = val / frac; 194e6cb3291SLorenzo Bianconi remainder = val % frac; 195e6cb3291SLorenzo Bianconi 196e6cb3291SLorenzo Bianconi if (remainder > margin) { 197e6cb3291SLorenzo Bianconi if ((frac - remainder) <= margin) 198e6cb3291SLorenzo Bianconi factor++; 199e6cb3291SLorenzo Bianconi else 200e6cb3291SLorenzo Bianconi factor = 0; 201e6cb3291SLorenzo Bianconi } 202e6cb3291SLorenzo Bianconi return factor; 203e6cb3291SLorenzo Bianconi } 204e6cb3291SLorenzo Bianconi 205e6cb3291SLorenzo Bianconi static void mt76x02_dfs_detector_reset(struct mt76x02_dev *dev) 206e6cb3291SLorenzo Bianconi { 207e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 208e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence *seq, *tmp_seq; 209e6cb3291SLorenzo Bianconi int i; 210e6cb3291SLorenzo Bianconi 211e6cb3291SLorenzo Bianconi /* reset hw detector */ 212e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 1), 0xf); 213e6cb3291SLorenzo Bianconi 214e6cb3291SLorenzo Bianconi /* reset sw detector */ 215e6cb3291SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) { 216e6cb3291SLorenzo Bianconi dfs_pd->event_rb[i].h_rb = 0; 217e6cb3291SLorenzo Bianconi dfs_pd->event_rb[i].t_rb = 0; 218e6cb3291SLorenzo Bianconi } 219e6cb3291SLorenzo Bianconi 220e6cb3291SLorenzo Bianconi list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) { 221e6cb3291SLorenzo Bianconi list_del_init(&seq->head); 222e6cb3291SLorenzo Bianconi mt76x02_dfs_seq_pool_put(dev, seq); 223e6cb3291SLorenzo Bianconi } 224e6cb3291SLorenzo Bianconi } 225e6cb3291SLorenzo Bianconi 226e6cb3291SLorenzo Bianconi static bool mt76x02_dfs_check_chirp(struct mt76x02_dev *dev) 227e6cb3291SLorenzo Bianconi { 228e6cb3291SLorenzo Bianconi bool ret = false; 229e6cb3291SLorenzo Bianconi u32 current_ts, delta_ts; 230e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 231e6cb3291SLorenzo Bianconi 232e6cb3291SLorenzo Bianconi current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER); 233e6cb3291SLorenzo Bianconi delta_ts = current_ts - dfs_pd->chirp_pulse_ts; 234e6cb3291SLorenzo Bianconi dfs_pd->chirp_pulse_ts = current_ts; 235e6cb3291SLorenzo Bianconi 236e6cb3291SLorenzo Bianconi /* 12 sec */ 237e6cb3291SLorenzo Bianconi if (delta_ts <= (12 * (1 << 20))) { 238e6cb3291SLorenzo Bianconi if (++dfs_pd->chirp_pulse_cnt > 8) 239e6cb3291SLorenzo Bianconi ret = true; 240e6cb3291SLorenzo Bianconi } else { 241e6cb3291SLorenzo Bianconi dfs_pd->chirp_pulse_cnt = 1; 242e6cb3291SLorenzo Bianconi } 243e6cb3291SLorenzo Bianconi 244e6cb3291SLorenzo Bianconi return ret; 245e6cb3291SLorenzo Bianconi } 246e6cb3291SLorenzo Bianconi 247e6cb3291SLorenzo Bianconi static void mt76x02_dfs_get_hw_pulse(struct mt76x02_dev *dev, 248e6cb3291SLorenzo Bianconi struct mt76x02_dfs_hw_pulse *pulse) 249e6cb3291SLorenzo Bianconi { 250e6cb3291SLorenzo Bianconi u32 data; 251e6cb3291SLorenzo Bianconi 252e6cb3291SLorenzo Bianconi /* select channel */ 253e6cb3291SLorenzo Bianconi data = (MT_DFS_CH_EN << 16) | pulse->engine; 254e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), data); 255e6cb3291SLorenzo Bianconi 256e6cb3291SLorenzo Bianconi /* reported period */ 257e6cb3291SLorenzo Bianconi pulse->period = mt76_rr(dev, MT_BBP(DFS, 19)); 258e6cb3291SLorenzo Bianconi 259e6cb3291SLorenzo Bianconi /* reported width */ 260e6cb3291SLorenzo Bianconi pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20)); 261e6cb3291SLorenzo Bianconi pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23)); 262e6cb3291SLorenzo Bianconi 263e6cb3291SLorenzo Bianconi /* reported burst number */ 264e6cb3291SLorenzo Bianconi pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22)); 265e6cb3291SLorenzo Bianconi } 266e6cb3291SLorenzo Bianconi 267e6cb3291SLorenzo Bianconi static bool mt76x02_dfs_check_hw_pulse(struct mt76x02_dev *dev, 268e6cb3291SLorenzo Bianconi struct mt76x02_dfs_hw_pulse *pulse) 269e6cb3291SLorenzo Bianconi { 270e6cb3291SLorenzo Bianconi bool ret = false; 271e6cb3291SLorenzo Bianconi 272e6cb3291SLorenzo Bianconi if (!pulse->period || !pulse->w1) 273e6cb3291SLorenzo Bianconi return false; 274e6cb3291SLorenzo Bianconi 275d8b8890dSLorenzo Bianconi switch (dev->mt76.region) { 276e6cb3291SLorenzo Bianconi case NL80211_DFS_FCC: 277e6cb3291SLorenzo Bianconi if (pulse->engine > 3) 278e6cb3291SLorenzo Bianconi break; 279e6cb3291SLorenzo Bianconi 280e6cb3291SLorenzo Bianconi if (pulse->engine == 3) { 281e6cb3291SLorenzo Bianconi ret = mt76x02_dfs_check_chirp(dev); 282e6cb3291SLorenzo Bianconi break; 283e6cb3291SLorenzo Bianconi } 284e6cb3291SLorenzo Bianconi 285e6cb3291SLorenzo Bianconi /* check short pulse*/ 286e6cb3291SLorenzo Bianconi if (pulse->w1 < 120) 287e6cb3291SLorenzo Bianconi ret = (pulse->period >= 2900 && 288e6cb3291SLorenzo Bianconi (pulse->period <= 4700 || 289e6cb3291SLorenzo Bianconi pulse->period >= 6400) && 290e6cb3291SLorenzo Bianconi (pulse->period <= 6800 || 291e6cb3291SLorenzo Bianconi pulse->period >= 10200) && 292e6cb3291SLorenzo Bianconi pulse->period <= 61600); 293e6cb3291SLorenzo Bianconi else if (pulse->w1 < 130) /* 120 - 130 */ 294e6cb3291SLorenzo Bianconi ret = (pulse->period >= 2900 && 295e6cb3291SLorenzo Bianconi pulse->period <= 61600); 296e6cb3291SLorenzo Bianconi else 297e6cb3291SLorenzo Bianconi ret = (pulse->period >= 3500 && 298e6cb3291SLorenzo Bianconi pulse->period <= 10100); 299e6cb3291SLorenzo Bianconi break; 300e6cb3291SLorenzo Bianconi case NL80211_DFS_ETSI: 301e6cb3291SLorenzo Bianconi if (pulse->engine >= 3) 302e6cb3291SLorenzo Bianconi break; 303e6cb3291SLorenzo Bianconi 304e6cb3291SLorenzo Bianconi ret = (pulse->period >= 4900 && 305e6cb3291SLorenzo Bianconi (pulse->period <= 10200 || 306e6cb3291SLorenzo Bianconi pulse->period >= 12400) && 307e6cb3291SLorenzo Bianconi pulse->period <= 100100); 308e6cb3291SLorenzo Bianconi break; 309e6cb3291SLorenzo Bianconi case NL80211_DFS_JP: 310e6cb3291SLorenzo Bianconi if (dev->mt76.chandef.chan->center_freq >= 5250 && 311e6cb3291SLorenzo Bianconi dev->mt76.chandef.chan->center_freq <= 5350) { 312e6cb3291SLorenzo Bianconi /* JPW53 */ 313e6cb3291SLorenzo Bianconi if (pulse->w1 <= 130) 314e6cb3291SLorenzo Bianconi ret = (pulse->period >= 28360 && 315e6cb3291SLorenzo Bianconi (pulse->period <= 28700 || 316e6cb3291SLorenzo Bianconi pulse->period >= 76900) && 317e6cb3291SLorenzo Bianconi pulse->period <= 76940); 318e6cb3291SLorenzo Bianconi break; 319e6cb3291SLorenzo Bianconi } 320e6cb3291SLorenzo Bianconi 321e6cb3291SLorenzo Bianconi if (pulse->engine > 3) 322e6cb3291SLorenzo Bianconi break; 323e6cb3291SLorenzo Bianconi 324e6cb3291SLorenzo Bianconi if (pulse->engine == 3) { 325e6cb3291SLorenzo Bianconi ret = mt76x02_dfs_check_chirp(dev); 326e6cb3291SLorenzo Bianconi break; 327e6cb3291SLorenzo Bianconi } 328e6cb3291SLorenzo Bianconi 329e6cb3291SLorenzo Bianconi /* check short pulse*/ 330e6cb3291SLorenzo Bianconi if (pulse->w1 < 120) 331e6cb3291SLorenzo Bianconi ret = (pulse->period >= 2900 && 332e6cb3291SLorenzo Bianconi (pulse->period <= 4700 || 333e6cb3291SLorenzo Bianconi pulse->period >= 6400) && 334e6cb3291SLorenzo Bianconi (pulse->period <= 6800 || 335e6cb3291SLorenzo Bianconi pulse->period >= 27560) && 336e6cb3291SLorenzo Bianconi (pulse->period <= 27960 || 337e6cb3291SLorenzo Bianconi pulse->period >= 28360) && 338e6cb3291SLorenzo Bianconi (pulse->period <= 28700 || 339e6cb3291SLorenzo Bianconi pulse->period >= 79900) && 340e6cb3291SLorenzo Bianconi pulse->period <= 80100); 341e6cb3291SLorenzo Bianconi else if (pulse->w1 < 130) /* 120 - 130 */ 342e6cb3291SLorenzo Bianconi ret = (pulse->period >= 2900 && 343e6cb3291SLorenzo Bianconi (pulse->period <= 10100 || 344e6cb3291SLorenzo Bianconi pulse->period >= 27560) && 345e6cb3291SLorenzo Bianconi (pulse->period <= 27960 || 346e6cb3291SLorenzo Bianconi pulse->period >= 28360) && 347e6cb3291SLorenzo Bianconi (pulse->period <= 28700 || 348e6cb3291SLorenzo Bianconi pulse->period >= 79900) && 349e6cb3291SLorenzo Bianconi pulse->period <= 80100); 350e6cb3291SLorenzo Bianconi else 351e6cb3291SLorenzo Bianconi ret = (pulse->period >= 3900 && 352e6cb3291SLorenzo Bianconi pulse->period <= 10100); 353e6cb3291SLorenzo Bianconi break; 354e6cb3291SLorenzo Bianconi case NL80211_DFS_UNSET: 355e6cb3291SLorenzo Bianconi default: 356e6cb3291SLorenzo Bianconi return false; 357e6cb3291SLorenzo Bianconi } 358e6cb3291SLorenzo Bianconi 359e6cb3291SLorenzo Bianconi return ret; 360e6cb3291SLorenzo Bianconi } 361e6cb3291SLorenzo Bianconi 362e6cb3291SLorenzo Bianconi static bool mt76x02_dfs_fetch_event(struct mt76x02_dev *dev, 363e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event) 364e6cb3291SLorenzo Bianconi { 365e6cb3291SLorenzo Bianconi u32 data; 366e6cb3291SLorenzo Bianconi 367e6cb3291SLorenzo Bianconi /* 1st: DFS_R37[31]: 0 (engine 0) - 1 (engine 2) 368e6cb3291SLorenzo Bianconi * 2nd: DFS_R37[21:0]: pulse time 369e6cb3291SLorenzo Bianconi * 3rd: DFS_R37[11:0]: pulse width 370e6cb3291SLorenzo Bianconi * 3rd: DFS_R37[25:16]: phase 371e6cb3291SLorenzo Bianconi * 4th: DFS_R37[12:0]: current pwr 372e6cb3291SLorenzo Bianconi * 4th: DFS_R37[21:16]: pwr stable counter 373e6cb3291SLorenzo Bianconi * 374e6cb3291SLorenzo Bianconi * 1st: DFS_R37[31:0] set to 0xffffffff means no event detected 375e6cb3291SLorenzo Bianconi */ 376e6cb3291SLorenzo Bianconi data = mt76_rr(dev, MT_BBP(DFS, 37)); 377e6cb3291SLorenzo Bianconi if (!MT_DFS_CHECK_EVENT(data)) 378e6cb3291SLorenzo Bianconi return false; 379e6cb3291SLorenzo Bianconi 380e6cb3291SLorenzo Bianconi event->engine = MT_DFS_EVENT_ENGINE(data); 381e6cb3291SLorenzo Bianconi data = mt76_rr(dev, MT_BBP(DFS, 37)); 382e6cb3291SLorenzo Bianconi event->ts = MT_DFS_EVENT_TIMESTAMP(data); 383e6cb3291SLorenzo Bianconi data = mt76_rr(dev, MT_BBP(DFS, 37)); 384e6cb3291SLorenzo Bianconi event->width = MT_DFS_EVENT_WIDTH(data); 385e6cb3291SLorenzo Bianconi 386e6cb3291SLorenzo Bianconi return true; 387e6cb3291SLorenzo Bianconi } 388e6cb3291SLorenzo Bianconi 389e6cb3291SLorenzo Bianconi static bool mt76x02_dfs_check_event(struct mt76x02_dev *dev, 390e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event) 391e6cb3291SLorenzo Bianconi { 392e6cb3291SLorenzo Bianconi if (event->engine == 2) { 393e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 394e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event_rb *event_buff = &dfs_pd->event_rb[1]; 395e6cb3291SLorenzo Bianconi u16 last_event_idx; 396e6cb3291SLorenzo Bianconi u32 delta_ts; 397e6cb3291SLorenzo Bianconi 398e6cb3291SLorenzo Bianconi last_event_idx = mt76_decr(event_buff->t_rb, 399e6cb3291SLorenzo Bianconi MT_DFS_EVENT_BUFLEN); 400e6cb3291SLorenzo Bianconi delta_ts = event->ts - event_buff->data[last_event_idx].ts; 401e6cb3291SLorenzo Bianconi if (delta_ts < MT_DFS_EVENT_TIME_MARGIN && 402e6cb3291SLorenzo Bianconi event_buff->data[last_event_idx].width >= 200) 403e6cb3291SLorenzo Bianconi return false; 404e6cb3291SLorenzo Bianconi } 405e6cb3291SLorenzo Bianconi return true; 406e6cb3291SLorenzo Bianconi } 407e6cb3291SLorenzo Bianconi 408e6cb3291SLorenzo Bianconi static void mt76x02_dfs_queue_event(struct mt76x02_dev *dev, 409e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event) 410e6cb3291SLorenzo Bianconi { 411e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 412e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event_rb *event_buff; 413e6cb3291SLorenzo Bianconi 414e6cb3291SLorenzo Bianconi /* add radar event to ring buffer */ 415e6cb3291SLorenzo Bianconi event_buff = event->engine == 2 ? &dfs_pd->event_rb[1] 416e6cb3291SLorenzo Bianconi : &dfs_pd->event_rb[0]; 417e6cb3291SLorenzo Bianconi event_buff->data[event_buff->t_rb] = *event; 418e6cb3291SLorenzo Bianconi event_buff->data[event_buff->t_rb].fetch_ts = jiffies; 419e6cb3291SLorenzo Bianconi 420e6cb3291SLorenzo Bianconi event_buff->t_rb = mt76_incr(event_buff->t_rb, MT_DFS_EVENT_BUFLEN); 421e6cb3291SLorenzo Bianconi if (event_buff->t_rb == event_buff->h_rb) 422e6cb3291SLorenzo Bianconi event_buff->h_rb = mt76_incr(event_buff->h_rb, 423e6cb3291SLorenzo Bianconi MT_DFS_EVENT_BUFLEN); 424e6cb3291SLorenzo Bianconi } 425e6cb3291SLorenzo Bianconi 426e6cb3291SLorenzo Bianconi static int mt76x02_dfs_create_sequence(struct mt76x02_dev *dev, 427e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event, 428e6cb3291SLorenzo Bianconi u16 cur_len) 429e6cb3291SLorenzo Bianconi { 430e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 431e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sw_detector_params *sw_params; 432e6cb3291SLorenzo Bianconi u32 width_delta, with_sum, factor, cur_pri; 433e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence seq, *seq_p; 434e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event_rb *event_rb; 435e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *cur_event; 436e6cb3291SLorenzo Bianconi int i, j, end, pri; 437e6cb3291SLorenzo Bianconi 438e6cb3291SLorenzo Bianconi event_rb = event->engine == 2 ? &dfs_pd->event_rb[1] 439e6cb3291SLorenzo Bianconi : &dfs_pd->event_rb[0]; 440e6cb3291SLorenzo Bianconi 441e6cb3291SLorenzo Bianconi i = mt76_decr(event_rb->t_rb, MT_DFS_EVENT_BUFLEN); 442e6cb3291SLorenzo Bianconi end = mt76_decr(event_rb->h_rb, MT_DFS_EVENT_BUFLEN); 443e6cb3291SLorenzo Bianconi 444e6cb3291SLorenzo Bianconi while (i != end) { 445e6cb3291SLorenzo Bianconi cur_event = &event_rb->data[i]; 446e6cb3291SLorenzo Bianconi with_sum = event->width + cur_event->width; 447e6cb3291SLorenzo Bianconi 448e6cb3291SLorenzo Bianconi sw_params = &dfs_pd->sw_dpd_params; 449d8b8890dSLorenzo Bianconi switch (dev->mt76.region) { 450e6cb3291SLorenzo Bianconi case NL80211_DFS_FCC: 451e6cb3291SLorenzo Bianconi case NL80211_DFS_JP: 452e6cb3291SLorenzo Bianconi if (with_sum < 600) 453e6cb3291SLorenzo Bianconi width_delta = 8; 454e6cb3291SLorenzo Bianconi else 455e6cb3291SLorenzo Bianconi width_delta = with_sum >> 3; 456e6cb3291SLorenzo Bianconi break; 457e6cb3291SLorenzo Bianconi case NL80211_DFS_ETSI: 458e6cb3291SLorenzo Bianconi if (event->engine == 2) 459e6cb3291SLorenzo Bianconi width_delta = with_sum >> 6; 460e6cb3291SLorenzo Bianconi else if (with_sum < 620) 461e6cb3291SLorenzo Bianconi width_delta = 24; 462e6cb3291SLorenzo Bianconi else 463e6cb3291SLorenzo Bianconi width_delta = 8; 464e6cb3291SLorenzo Bianconi break; 465e6cb3291SLorenzo Bianconi case NL80211_DFS_UNSET: 466e6cb3291SLorenzo Bianconi default: 467e6cb3291SLorenzo Bianconi return -EINVAL; 468e6cb3291SLorenzo Bianconi } 469e6cb3291SLorenzo Bianconi 470e6cb3291SLorenzo Bianconi pri = event->ts - cur_event->ts; 471e6cb3291SLorenzo Bianconi if (abs(event->width - cur_event->width) > width_delta || 472e6cb3291SLorenzo Bianconi pri < sw_params->min_pri) 473e6cb3291SLorenzo Bianconi goto next; 474e6cb3291SLorenzo Bianconi 475e6cb3291SLorenzo Bianconi if (pri > sw_params->max_pri) 476e6cb3291SLorenzo Bianconi break; 477e6cb3291SLorenzo Bianconi 478e6cb3291SLorenzo Bianconi seq.pri = event->ts - cur_event->ts; 479e6cb3291SLorenzo Bianconi seq.first_ts = cur_event->ts; 480e6cb3291SLorenzo Bianconi seq.last_ts = event->ts; 481e6cb3291SLorenzo Bianconi seq.engine = event->engine; 482e6cb3291SLorenzo Bianconi seq.count = 2; 483e6cb3291SLorenzo Bianconi 484e6cb3291SLorenzo Bianconi j = mt76_decr(i, MT_DFS_EVENT_BUFLEN); 485e6cb3291SLorenzo Bianconi while (j != end) { 486e6cb3291SLorenzo Bianconi cur_event = &event_rb->data[j]; 487e6cb3291SLorenzo Bianconi cur_pri = event->ts - cur_event->ts; 488e6cb3291SLorenzo Bianconi factor = mt76x02_dfs_get_multiple(cur_pri, seq.pri, 489e6cb3291SLorenzo Bianconi sw_params->pri_margin); 490e6cb3291SLorenzo Bianconi if (factor > 0) { 491e6cb3291SLorenzo Bianconi seq.first_ts = cur_event->ts; 492e6cb3291SLorenzo Bianconi seq.count++; 493e6cb3291SLorenzo Bianconi } 494e6cb3291SLorenzo Bianconi 495e6cb3291SLorenzo Bianconi j = mt76_decr(j, MT_DFS_EVENT_BUFLEN); 496e6cb3291SLorenzo Bianconi } 497e6cb3291SLorenzo Bianconi if (seq.count <= cur_len) 498e6cb3291SLorenzo Bianconi goto next; 499e6cb3291SLorenzo Bianconi 500e6cb3291SLorenzo Bianconi seq_p = mt76x02_dfs_seq_pool_get(dev); 501e6cb3291SLorenzo Bianconi if (!seq_p) 502e6cb3291SLorenzo Bianconi return -ENOMEM; 503e6cb3291SLorenzo Bianconi 504e6cb3291SLorenzo Bianconi *seq_p = seq; 505e6cb3291SLorenzo Bianconi INIT_LIST_HEAD(&seq_p->head); 506e6cb3291SLorenzo Bianconi list_add(&seq_p->head, &dfs_pd->sequences); 507e6cb3291SLorenzo Bianconi next: 508e6cb3291SLorenzo Bianconi i = mt76_decr(i, MT_DFS_EVENT_BUFLEN); 509e6cb3291SLorenzo Bianconi } 510e6cb3291SLorenzo Bianconi return 0; 511e6cb3291SLorenzo Bianconi } 512e6cb3291SLorenzo Bianconi 513e6cb3291SLorenzo Bianconi static u16 mt76x02_dfs_add_event_to_sequence(struct mt76x02_dev *dev, 514e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event) 515e6cb3291SLorenzo Bianconi { 516e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 517e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sw_detector_params *sw_params; 518e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence *seq, *tmp_seq; 519e6cb3291SLorenzo Bianconi u16 max_seq_len = 0; 520e6cb3291SLorenzo Bianconi u32 factor, pri; 521e6cb3291SLorenzo Bianconi 522e6cb3291SLorenzo Bianconi sw_params = &dfs_pd->sw_dpd_params; 523e6cb3291SLorenzo Bianconi list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) { 524e6cb3291SLorenzo Bianconi if (event->ts > seq->first_ts + MT_DFS_SEQUENCE_WINDOW) { 525e6cb3291SLorenzo Bianconi list_del_init(&seq->head); 526e6cb3291SLorenzo Bianconi mt76x02_dfs_seq_pool_put(dev, seq); 527e6cb3291SLorenzo Bianconi continue; 528e6cb3291SLorenzo Bianconi } 529e6cb3291SLorenzo Bianconi 530e6cb3291SLorenzo Bianconi if (event->engine != seq->engine) 531e6cb3291SLorenzo Bianconi continue; 532e6cb3291SLorenzo Bianconi 533e6cb3291SLorenzo Bianconi pri = event->ts - seq->last_ts; 534e6cb3291SLorenzo Bianconi factor = mt76x02_dfs_get_multiple(pri, seq->pri, 535e6cb3291SLorenzo Bianconi sw_params->pri_margin); 536e6cb3291SLorenzo Bianconi if (factor > 0) { 537e6cb3291SLorenzo Bianconi seq->last_ts = event->ts; 538e6cb3291SLorenzo Bianconi seq->count++; 539e6cb3291SLorenzo Bianconi max_seq_len = max_t(u16, max_seq_len, seq->count); 540e6cb3291SLorenzo Bianconi } 541e6cb3291SLorenzo Bianconi } 542e6cb3291SLorenzo Bianconi return max_seq_len; 543e6cb3291SLorenzo Bianconi } 544e6cb3291SLorenzo Bianconi 545e6cb3291SLorenzo Bianconi static bool mt76x02_dfs_check_detection(struct mt76x02_dev *dev) 546e6cb3291SLorenzo Bianconi { 547e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 548e6cb3291SLorenzo Bianconi struct mt76x02_dfs_sequence *seq; 549e6cb3291SLorenzo Bianconi 550e6cb3291SLorenzo Bianconi if (list_empty(&dfs_pd->sequences)) 551e6cb3291SLorenzo Bianconi return false; 552e6cb3291SLorenzo Bianconi 553e6cb3291SLorenzo Bianconi list_for_each_entry(seq, &dfs_pd->sequences, head) { 554e6cb3291SLorenzo Bianconi if (seq->count > MT_DFS_SEQUENCE_TH) { 555e6cb3291SLorenzo Bianconi dfs_pd->stats[seq->engine].sw_pattern++; 556e6cb3291SLorenzo Bianconi return true; 557e6cb3291SLorenzo Bianconi } 558e6cb3291SLorenzo Bianconi } 559e6cb3291SLorenzo Bianconi return false; 560e6cb3291SLorenzo Bianconi } 561e6cb3291SLorenzo Bianconi 562e6cb3291SLorenzo Bianconi static void mt76x02_dfs_add_events(struct mt76x02_dev *dev) 563e6cb3291SLorenzo Bianconi { 564e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 565e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event event; 566e6cb3291SLorenzo Bianconi int i, seq_len; 567e6cb3291SLorenzo Bianconi 568e6cb3291SLorenzo Bianconi /* disable debug mode */ 569e6cb3291SLorenzo Bianconi mt76x02_dfs_set_capture_mode_ctrl(dev, false); 570e6cb3291SLorenzo Bianconi for (i = 0; i < MT_DFS_EVENT_LOOP; i++) { 571e6cb3291SLorenzo Bianconi if (!mt76x02_dfs_fetch_event(dev, &event)) 572e6cb3291SLorenzo Bianconi break; 573e6cb3291SLorenzo Bianconi 574e6cb3291SLorenzo Bianconi if (dfs_pd->last_event_ts > event.ts) 575e6cb3291SLorenzo Bianconi mt76x02_dfs_detector_reset(dev); 576e6cb3291SLorenzo Bianconi dfs_pd->last_event_ts = event.ts; 577e6cb3291SLorenzo Bianconi 578e6cb3291SLorenzo Bianconi if (!mt76x02_dfs_check_event(dev, &event)) 579e6cb3291SLorenzo Bianconi continue; 580e6cb3291SLorenzo Bianconi 581e6cb3291SLorenzo Bianconi seq_len = mt76x02_dfs_add_event_to_sequence(dev, &event); 582e6cb3291SLorenzo Bianconi mt76x02_dfs_create_sequence(dev, &event, seq_len); 583e6cb3291SLorenzo Bianconi 584e6cb3291SLorenzo Bianconi mt76x02_dfs_queue_event(dev, &event); 585e6cb3291SLorenzo Bianconi } 586e6cb3291SLorenzo Bianconi mt76x02_dfs_set_capture_mode_ctrl(dev, true); 587e6cb3291SLorenzo Bianconi } 588e6cb3291SLorenzo Bianconi 589e6cb3291SLorenzo Bianconi static void mt76x02_dfs_check_event_window(struct mt76x02_dev *dev) 590e6cb3291SLorenzo Bianconi { 591e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 592e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event_rb *event_buff; 593e6cb3291SLorenzo Bianconi struct mt76x02_dfs_event *event; 594e6cb3291SLorenzo Bianconi int i; 595e6cb3291SLorenzo Bianconi 596e6cb3291SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) { 597e6cb3291SLorenzo Bianconi event_buff = &dfs_pd->event_rb[i]; 598e6cb3291SLorenzo Bianconi 599e6cb3291SLorenzo Bianconi while (event_buff->h_rb != event_buff->t_rb) { 600e6cb3291SLorenzo Bianconi event = &event_buff->data[event_buff->h_rb]; 601e6cb3291SLorenzo Bianconi 602e6cb3291SLorenzo Bianconi /* sorted list */ 603e6cb3291SLorenzo Bianconi if (time_is_after_jiffies(event->fetch_ts + 604e6cb3291SLorenzo Bianconi MT_DFS_EVENT_WINDOW)) 605e6cb3291SLorenzo Bianconi break; 606e6cb3291SLorenzo Bianconi event_buff->h_rb = mt76_incr(event_buff->h_rb, 607e6cb3291SLorenzo Bianconi MT_DFS_EVENT_BUFLEN); 608e6cb3291SLorenzo Bianconi } 609e6cb3291SLorenzo Bianconi } 610e6cb3291SLorenzo Bianconi } 611e6cb3291SLorenzo Bianconi 612e6cb3291SLorenzo Bianconi static void mt76x02_dfs_tasklet(unsigned long arg) 613e6cb3291SLorenzo Bianconi { 614e6cb3291SLorenzo Bianconi struct mt76x02_dev *dev = (struct mt76x02_dev *)arg; 615e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 616e6cb3291SLorenzo Bianconi u32 engine_mask; 617e6cb3291SLorenzo Bianconi int i; 618e6cb3291SLorenzo Bianconi 619e6cb3291SLorenzo Bianconi if (test_bit(MT76_SCANNING, &dev->mt76.state)) 620e6cb3291SLorenzo Bianconi goto out; 621e6cb3291SLorenzo Bianconi 622e6cb3291SLorenzo Bianconi if (time_is_before_jiffies(dfs_pd->last_sw_check + 623e6cb3291SLorenzo Bianconi MT_DFS_SW_TIMEOUT)) { 624e6cb3291SLorenzo Bianconi bool radar_detected; 625e6cb3291SLorenzo Bianconi 626e6cb3291SLorenzo Bianconi dfs_pd->last_sw_check = jiffies; 627e6cb3291SLorenzo Bianconi 628e6cb3291SLorenzo Bianconi mt76x02_dfs_add_events(dev); 629e6cb3291SLorenzo Bianconi radar_detected = mt76x02_dfs_check_detection(dev); 630e6cb3291SLorenzo Bianconi if (radar_detected) { 631e6cb3291SLorenzo Bianconi /* sw detector rx radar pattern */ 632e6cb3291SLorenzo Bianconi ieee80211_radar_detected(dev->mt76.hw); 633e6cb3291SLorenzo Bianconi mt76x02_dfs_detector_reset(dev); 634e6cb3291SLorenzo Bianconi 635e6cb3291SLorenzo Bianconi return; 636e6cb3291SLorenzo Bianconi } 637e6cb3291SLorenzo Bianconi mt76x02_dfs_check_event_window(dev); 638e6cb3291SLorenzo Bianconi } 639e6cb3291SLorenzo Bianconi 640e6cb3291SLorenzo Bianconi engine_mask = mt76_rr(dev, MT_BBP(DFS, 1)); 641e6cb3291SLorenzo Bianconi if (!(engine_mask & 0xf)) 642e6cb3291SLorenzo Bianconi goto out; 643e6cb3291SLorenzo Bianconi 644e6cb3291SLorenzo Bianconi for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { 645e6cb3291SLorenzo Bianconi struct mt76x02_dfs_hw_pulse pulse; 646e6cb3291SLorenzo Bianconi 647e6cb3291SLorenzo Bianconi if (!(engine_mask & (1 << i))) 648e6cb3291SLorenzo Bianconi continue; 649e6cb3291SLorenzo Bianconi 650e6cb3291SLorenzo Bianconi pulse.engine = i; 651e6cb3291SLorenzo Bianconi mt76x02_dfs_get_hw_pulse(dev, &pulse); 652e6cb3291SLorenzo Bianconi 653e6cb3291SLorenzo Bianconi if (!mt76x02_dfs_check_hw_pulse(dev, &pulse)) { 654e6cb3291SLorenzo Bianconi dfs_pd->stats[i].hw_pulse_discarded++; 655e6cb3291SLorenzo Bianconi continue; 656e6cb3291SLorenzo Bianconi } 657e6cb3291SLorenzo Bianconi 658e6cb3291SLorenzo Bianconi /* hw detector rx radar pattern */ 659e6cb3291SLorenzo Bianconi dfs_pd->stats[i].hw_pattern++; 660e6cb3291SLorenzo Bianconi ieee80211_radar_detected(dev->mt76.hw); 661e6cb3291SLorenzo Bianconi mt76x02_dfs_detector_reset(dev); 662e6cb3291SLorenzo Bianconi 663e6cb3291SLorenzo Bianconi return; 664e6cb3291SLorenzo Bianconi } 665e6cb3291SLorenzo Bianconi 666e6cb3291SLorenzo Bianconi /* reset hw detector */ 667e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 1), 0xf); 668e6cb3291SLorenzo Bianconi 669e6cb3291SLorenzo Bianconi out: 670e6cb3291SLorenzo Bianconi mt76x02_irq_enable(dev, MT_INT_GPTIMER); 671e6cb3291SLorenzo Bianconi } 672e6cb3291SLorenzo Bianconi 673e6cb3291SLorenzo Bianconi static void mt76x02_dfs_init_sw_detector(struct mt76x02_dev *dev) 674e6cb3291SLorenzo Bianconi { 675e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 676e6cb3291SLorenzo Bianconi 677d8b8890dSLorenzo Bianconi switch (dev->mt76.region) { 678e6cb3291SLorenzo Bianconi case NL80211_DFS_FCC: 679e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI; 680e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI; 681e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN; 682e6cb3291SLorenzo Bianconi break; 683e6cb3291SLorenzo Bianconi case NL80211_DFS_ETSI: 684e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.max_pri = MT_DFS_ETSI_MAX_PRI; 685e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.min_pri = MT_DFS_ETSI_MIN_PRI; 686e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN << 2; 687e6cb3291SLorenzo Bianconi break; 688e6cb3291SLorenzo Bianconi case NL80211_DFS_JP: 689e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.max_pri = MT_DFS_JP_MAX_PRI; 690e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.min_pri = MT_DFS_JP_MIN_PRI; 691e6cb3291SLorenzo Bianconi dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN; 692e6cb3291SLorenzo Bianconi break; 693e6cb3291SLorenzo Bianconi case NL80211_DFS_UNSET: 694e6cb3291SLorenzo Bianconi default: 695e6cb3291SLorenzo Bianconi break; 696e6cb3291SLorenzo Bianconi } 697e6cb3291SLorenzo Bianconi } 698e6cb3291SLorenzo Bianconi 699e6cb3291SLorenzo Bianconi static void mt76x02_dfs_set_bbp_params(struct mt76x02_dev *dev) 700e6cb3291SLorenzo Bianconi { 701e6cb3291SLorenzo Bianconi const struct mt76x02_radar_specs *radar_specs; 702e6cb3291SLorenzo Bianconi u8 i, shift; 703e6cb3291SLorenzo Bianconi u32 data; 704e6cb3291SLorenzo Bianconi 705e6cb3291SLorenzo Bianconi switch (dev->mt76.chandef.width) { 706e6cb3291SLorenzo Bianconi case NL80211_CHAN_WIDTH_40: 707e6cb3291SLorenzo Bianconi shift = MT_DFS_NUM_ENGINES; 708e6cb3291SLorenzo Bianconi break; 709e6cb3291SLorenzo Bianconi case NL80211_CHAN_WIDTH_80: 710e6cb3291SLorenzo Bianconi shift = 2 * MT_DFS_NUM_ENGINES; 711e6cb3291SLorenzo Bianconi break; 712e6cb3291SLorenzo Bianconi default: 713e6cb3291SLorenzo Bianconi shift = 0; 714e6cb3291SLorenzo Bianconi break; 715e6cb3291SLorenzo Bianconi } 716e6cb3291SLorenzo Bianconi 717d8b8890dSLorenzo Bianconi switch (dev->mt76.region) { 718e6cb3291SLorenzo Bianconi case NL80211_DFS_FCC: 719e6cb3291SLorenzo Bianconi radar_specs = &fcc_radar_specs[shift]; 720e6cb3291SLorenzo Bianconi break; 721e6cb3291SLorenzo Bianconi case NL80211_DFS_ETSI: 722e6cb3291SLorenzo Bianconi radar_specs = &etsi_radar_specs[shift]; 723e6cb3291SLorenzo Bianconi break; 724e6cb3291SLorenzo Bianconi case NL80211_DFS_JP: 725e6cb3291SLorenzo Bianconi if (dev->mt76.chandef.chan->center_freq >= 5250 && 726e6cb3291SLorenzo Bianconi dev->mt76.chandef.chan->center_freq <= 5350) 727e6cb3291SLorenzo Bianconi radar_specs = &jp_w53_radar_specs[shift]; 728e6cb3291SLorenzo Bianconi else 729e6cb3291SLorenzo Bianconi radar_specs = &jp_w56_radar_specs[shift]; 730e6cb3291SLorenzo Bianconi break; 731e6cb3291SLorenzo Bianconi case NL80211_DFS_UNSET: 732e6cb3291SLorenzo Bianconi default: 733e6cb3291SLorenzo Bianconi return; 734e6cb3291SLorenzo Bianconi } 735e6cb3291SLorenzo Bianconi 736e6cb3291SLorenzo Bianconi data = (MT_DFS_VGA_MASK << 16) | 737e6cb3291SLorenzo Bianconi (MT_DFS_PWR_GAIN_OFFSET << 12) | 738e6cb3291SLorenzo Bianconi (MT_DFS_PWR_DOWN_TIME << 8) | 739e6cb3291SLorenzo Bianconi (MT_DFS_SYM_ROUND << 4) | 740e6cb3291SLorenzo Bianconi (MT_DFS_DELTA_DELAY & 0xf); 741e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 2), data); 742e6cb3291SLorenzo Bianconi 743e6cb3291SLorenzo Bianconi data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK; 744e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 3), data); 745e6cb3291SLorenzo Bianconi 746e6cb3291SLorenzo Bianconi for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { 747e6cb3291SLorenzo Bianconi /* configure engine */ 748e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), i); 749e6cb3291SLorenzo Bianconi 750e6cb3291SLorenzo Bianconi /* detection mode + avg_len */ 751e6cb3291SLorenzo Bianconi data = ((radar_specs[i].avg_len & 0x1ff) << 16) | 752e6cb3291SLorenzo Bianconi (radar_specs[i].mode & 0xf); 753e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 4), data); 754e6cb3291SLorenzo Bianconi 755e6cb3291SLorenzo Bianconi /* dfs energy */ 756e6cb3291SLorenzo Bianconi data = ((radar_specs[i].e_high & 0x0fff) << 16) | 757e6cb3291SLorenzo Bianconi (radar_specs[i].e_low & 0x0fff); 758e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 5), data); 759e6cb3291SLorenzo Bianconi 760e6cb3291SLorenzo Bianconi /* dfs period */ 761e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low); 762e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high); 763e6cb3291SLorenzo Bianconi 764e6cb3291SLorenzo Bianconi /* dfs burst */ 765e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low); 766e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high); 767e6cb3291SLorenzo Bianconi 768e6cb3291SLorenzo Bianconi /* dfs width */ 769e6cb3291SLorenzo Bianconi data = ((radar_specs[i].w_high & 0x0fff) << 16) | 770e6cb3291SLorenzo Bianconi (radar_specs[i].w_low & 0x0fff); 771e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 14), data); 772e6cb3291SLorenzo Bianconi 773e6cb3291SLorenzo Bianconi /* dfs margins */ 774e6cb3291SLorenzo Bianconi data = (radar_specs[i].w_margin << 16) | 775e6cb3291SLorenzo Bianconi radar_specs[i].t_margin; 776e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 15), data); 777e6cb3291SLorenzo Bianconi 778e6cb3291SLorenzo Bianconi /* dfs event expiration */ 779e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration); 780e6cb3291SLorenzo Bianconi 781e6cb3291SLorenzo Bianconi /* dfs pwr adj */ 782e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp); 783e6cb3291SLorenzo Bianconi } 784e6cb3291SLorenzo Bianconi 785e6cb3291SLorenzo Bianconi /* reset status */ 786e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 1), 0xf); 787e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 36), 0x3); 788e6cb3291SLorenzo Bianconi 789e6cb3291SLorenzo Bianconi /* enable detection*/ 790e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16); 7916bf4a8e9SLorenzo Bianconi mt76_wr(dev, MT_BBP(IBI, 11), 0x0c350001); 792e6cb3291SLorenzo Bianconi } 793e6cb3291SLorenzo Bianconi 794801ccc8aSLorenzo Bianconi void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev) 795801ccc8aSLorenzo Bianconi { 796801ccc8aSLorenzo Bianconi u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31; 797801ccc8aSLorenzo Bianconi 798801ccc8aSLorenzo Bianconi agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8)); 799801ccc8aSLorenzo Bianconi agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4)); 800801ccc8aSLorenzo Bianconi 801801ccc8aSLorenzo Bianconi val_r8 = (agc_r8 & 0x00007e00) >> 9; 802801ccc8aSLorenzo Bianconi val_r4 = agc_r4 & ~0x1f000000; 803801ccc8aSLorenzo Bianconi val_r4 += (((val_r8 + 1) >> 1) << 24); 804801ccc8aSLorenzo Bianconi mt76_wr(dev, MT_BBP(AGC, 4), val_r4); 805801ccc8aSLorenzo Bianconi 806801ccc8aSLorenzo Bianconi dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4); 807801ccc8aSLorenzo Bianconi dfs_r31 += val_r8; 808801ccc8aSLorenzo Bianconi dfs_r31 -= (agc_r8 & 0x00000038) >> 3; 809801ccc8aSLorenzo Bianconi dfs_r31 = (dfs_r31 << 16) | 0x00000307; 810801ccc8aSLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31); 811801ccc8aSLorenzo Bianconi 812801ccc8aSLorenzo Bianconi if (is_mt76x2(dev)) { 813801ccc8aSLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071); 814801ccc8aSLorenzo Bianconi } else { 815801ccc8aSLorenzo Bianconi /* disable hw detector */ 816801ccc8aSLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), 0); 817801ccc8aSLorenzo Bianconi /* enable hw detector */ 818801ccc8aSLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16); 819801ccc8aSLorenzo Bianconi } 820801ccc8aSLorenzo Bianconi } 821801ccc8aSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_phy_dfs_adjust_agc); 822801ccc8aSLorenzo Bianconi 823e6cb3291SLorenzo Bianconi void mt76x02_dfs_init_params(struct mt76x02_dev *dev) 824e6cb3291SLorenzo Bianconi { 825e6cb3291SLorenzo Bianconi struct cfg80211_chan_def *chandef = &dev->mt76.chandef; 826e6cb3291SLorenzo Bianconi 827e6cb3291SLorenzo Bianconi if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && 828d8b8890dSLorenzo Bianconi dev->mt76.region != NL80211_DFS_UNSET) { 829e6cb3291SLorenzo Bianconi mt76x02_dfs_init_sw_detector(dev); 830e6cb3291SLorenzo Bianconi mt76x02_dfs_set_bbp_params(dev); 831e6cb3291SLorenzo Bianconi /* enable debug mode */ 832e6cb3291SLorenzo Bianconi mt76x02_dfs_set_capture_mode_ctrl(dev, true); 833e6cb3291SLorenzo Bianconi 834e6cb3291SLorenzo Bianconi mt76x02_irq_enable(dev, MT_INT_GPTIMER); 835e6cb3291SLorenzo Bianconi mt76_rmw_field(dev, MT_INT_TIMER_EN, 836e6cb3291SLorenzo Bianconi MT_INT_TIMER_EN_GP_TIMER_EN, 1); 837e6cb3291SLorenzo Bianconi } else { 838e6cb3291SLorenzo Bianconi /* disable hw detector */ 839e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 0), 0); 840e6cb3291SLorenzo Bianconi /* clear detector status */ 841e6cb3291SLorenzo Bianconi mt76_wr(dev, MT_BBP(DFS, 1), 0xf); 8426bf4a8e9SLorenzo Bianconi if (mt76_chip(&dev->mt76) == 0x7610 || 8436bf4a8e9SLorenzo Bianconi mt76_chip(&dev->mt76) == 0x7630) 8446bf4a8e9SLorenzo Bianconi mt76_wr(dev, MT_BBP(IBI, 11), 0xfde8081); 8456bf4a8e9SLorenzo Bianconi else 8466bf4a8e9SLorenzo Bianconi mt76_wr(dev, MT_BBP(IBI, 11), 0); 847e6cb3291SLorenzo Bianconi 848e6cb3291SLorenzo Bianconi mt76x02_irq_disable(dev, MT_INT_GPTIMER); 849e6cb3291SLorenzo Bianconi mt76_rmw_field(dev, MT_INT_TIMER_EN, 850e6cb3291SLorenzo Bianconi MT_INT_TIMER_EN_GP_TIMER_EN, 0); 851e6cb3291SLorenzo Bianconi } 852e6cb3291SLorenzo Bianconi } 853e6cb3291SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_dfs_init_params); 854e6cb3291SLorenzo Bianconi 855e6cb3291SLorenzo Bianconi void mt76x02_dfs_init_detector(struct mt76x02_dev *dev) 856e6cb3291SLorenzo Bianconi { 857e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 858e6cb3291SLorenzo Bianconi 859e6cb3291SLorenzo Bianconi INIT_LIST_HEAD(&dfs_pd->sequences); 860e6cb3291SLorenzo Bianconi INIT_LIST_HEAD(&dfs_pd->seq_pool); 861d8b8890dSLorenzo Bianconi dev->mt76.region = NL80211_DFS_UNSET; 862e6cb3291SLorenzo Bianconi dfs_pd->last_sw_check = jiffies; 863e6cb3291SLorenzo Bianconi tasklet_init(&dfs_pd->dfs_tasklet, mt76x02_dfs_tasklet, 864e6cb3291SLorenzo Bianconi (unsigned long)dev); 865e6cb3291SLorenzo Bianconi } 866e6cb3291SLorenzo Bianconi 867e6cb3291SLorenzo Bianconi static void 868e6cb3291SLorenzo Bianconi mt76x02_dfs_set_domain(struct mt76x02_dev *dev, 869e6cb3291SLorenzo Bianconi enum nl80211_dfs_regions region) 870e6cb3291SLorenzo Bianconi { 871e6cb3291SLorenzo Bianconi struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; 872e6cb3291SLorenzo Bianconi 87335c57281SLorenzo Bianconi mutex_lock(&dev->mt76.mutex); 874d8b8890dSLorenzo Bianconi if (dev->mt76.region != region) { 875e6cb3291SLorenzo Bianconi tasklet_disable(&dfs_pd->dfs_tasklet); 876f82ce8d9SLorenzo Bianconi 877643749d4SFelix Fietkau dev->ed_monitor = dev->ed_monitor_enabled && 878643749d4SFelix Fietkau region == NL80211_DFS_ETSI; 879a78f1547SLorenzo Bianconi mt76x02_edcca_init(dev); 880f82ce8d9SLorenzo Bianconi 881d8b8890dSLorenzo Bianconi dev->mt76.region = region; 882e6cb3291SLorenzo Bianconi mt76x02_dfs_init_params(dev); 883e6cb3291SLorenzo Bianconi tasklet_enable(&dfs_pd->dfs_tasklet); 884e6cb3291SLorenzo Bianconi } 88535c57281SLorenzo Bianconi mutex_unlock(&dev->mt76.mutex); 886e6cb3291SLorenzo Bianconi } 887e6cb3291SLorenzo Bianconi 888e6cb3291SLorenzo Bianconi void mt76x02_regd_notifier(struct wiphy *wiphy, 889e6cb3291SLorenzo Bianconi struct regulatory_request *request) 890e6cb3291SLorenzo Bianconi { 891e6cb3291SLorenzo Bianconi struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 892e6cb3291SLorenzo Bianconi struct mt76x02_dev *dev = hw->priv; 893e6cb3291SLorenzo Bianconi 894e6cb3291SLorenzo Bianconi mt76x02_dfs_set_domain(dev, request->dfs_region); 895e6cb3291SLorenzo Bianconi } 896