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