Files
mt76/mt76x02_dfs.c
Felix Fietkau 33f29d2345 wifi: mt76: relicense to BSD-3-Clause-Clear
MediaTek has asked to switch from the ISC license to BSD-3-Clause-Clear,
in order to improve clarity and the legal integrity of the code.

The BSD-3-Clause license includes the "no endorsement" clause, which is
important for protecting the reputation of the original authors and
contributors by preventing unauthorized use of their names for endorsement
purposes.

This clause is absent in the BSD-2-Clause license, which is more permissive
but lacks this specific protection.

This change also cleans up the license of some Kconfig/Makefile files,
which were accidentally marked as GPL.

The GPL 2.0 remains in use on mt76x0, as well as two source files in mt7615
for which the license situation still needs to be clarified.

Signed-off-by: Felix Fietkau <[email protected]>
2025-10-13 11:11:19 +02:00

893 lines
24 KiB
C

// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Lorenzo Bianconi <[email protected]>
*/
#include "mt76x02.h"
#define RADAR_SPEC(m, len, el, eh, wl, wh, \
w_tolerance, tl, th, t_tolerance, \
bl, bh, event_exp, power_jmp) \
{ \
.mode = m, \
.avg_len = len, \
.e_low = el, \
.e_high = eh, \
.w_low = wl, \
.w_high = wh, \
.w_margin = w_tolerance, \
.t_low = tl, \
.t_high = th, \
.t_margin = t_tolerance, \
.b_low = bl, \
.b_high = bh, \
.event_expiration = event_exp, \
.pwr_jmp = power_jmp \
}
static const struct mt76x02_radar_specs etsi_radar_specs[] = {
/* 20MHz */
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
0x7fffffff, 0x155cc0, 0x19dd),
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
0x7fffffff, 0x2191c0, 0x15cc),
/* 40MHz */
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
0x7fffffff, 0x155cc0, 0x19dd),
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
0x7fffffff, 0x2191c0, 0x15cc),
/* 80MHz */
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
0x7fffffff, 0x155cc0, 0x19cc),
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
0x7fffffff, 0x155cc0, 0x19dd),
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
0x7fffffff, 0x2191c0, 0x15cc)
};
static const struct mt76x02_radar_specs fcc_radar_specs[] = {
/* 20MHz */
RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
0x7fffffff, 0xfe808, 0x13dc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0xfe808, 0x19dd),
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0xfe808, 0x12cc),
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0x57bcf00, 0x1289),
/* 40MHz */
RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
0x7fffffff, 0xfe808, 0x13dc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0xfe808, 0x19dd),
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0xfe808, 0x12cc),
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0x57bcf00, 0x1289),
/* 80MHz */
RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0,
0x7fffffff, 0xfe808, 0x16cc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0xfe808, 0x19dd),
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0xfe808, 0x12cc),
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0x57bcf00, 0x1289)
};
static const struct mt76x02_radar_specs jp_w56_radar_specs[] = {
/* 20MHz */
RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
0x7fffffff, 0x14c080, 0x13dc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0x14c080, 0x19dd),
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0x14c080, 0x12cc),
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0X57bcf00, 0x1289),
/* 40MHz */
RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
0x7fffffff, 0x14c080, 0x13dc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0x14c080, 0x19dd),
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0x14c080, 0x12cc),
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0X57bcf00, 0x1289),
/* 80MHz */
RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0,
0x7fffffff, 0x14c080, 0x16cc),
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
0x7fffffff, 0x14c080, 0x19dd),
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
0x7fffffff, 0x14c080, 0x12cc),
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
0x3938700, 0X57bcf00, 0x1289)
};
static const struct mt76x02_radar_specs jp_w53_radar_specs[] = {
/* 20MHz */
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 },
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 },
/* 40MHz */
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 },
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 },
/* 80MHz */
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 },
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
0x7fffffff, 0x14c080, 0x16cc),
{ 0 }
};
static void
mt76x02_dfs_set_capture_mode_ctrl(struct mt76x02_dev *dev, u8 enable)
{
u32 data;
data = (1 << 1) | enable;
mt76_wr(dev, MT_BBP(DFS, 36), data);
}
static void mt76x02_dfs_seq_pool_put(struct mt76x02_dev *dev,
struct mt76x02_dfs_sequence *seq)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
list_add(&seq->head, &dfs_pd->seq_pool);
dfs_pd->seq_stats.seq_pool_len++;
dfs_pd->seq_stats.seq_len--;
}
static struct mt76x02_dfs_sequence *
mt76x02_dfs_seq_pool_get(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_sequence *seq;
if (list_empty(&dfs_pd->seq_pool)) {
seq = devm_kzalloc(dev->mt76.dev, sizeof(*seq), GFP_ATOMIC);
} else {
seq = list_first_entry(&dfs_pd->seq_pool,
struct mt76x02_dfs_sequence,
head);
list_del(&seq->head);
dfs_pd->seq_stats.seq_pool_len--;
}
if (seq)
dfs_pd->seq_stats.seq_len++;
return seq;
}
static int mt76x02_dfs_get_multiple(int val, int frac, int margin)
{
int remainder, factor;
if (!frac)
return 0;
if (abs(val - frac) <= margin)
return 1;
factor = val / frac;
remainder = val % frac;
if (remainder > margin) {
if ((frac - remainder) <= margin)
factor++;
else
factor = 0;
}
return factor;
}
static void mt76x02_dfs_detector_reset(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_sequence *seq, *tmp_seq;
int i;
/* reset hw detector */
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
/* reset sw detector */
for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
dfs_pd->event_rb[i].h_rb = 0;
dfs_pd->event_rb[i].t_rb = 0;
}
list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
list_del_init(&seq->head);
mt76x02_dfs_seq_pool_put(dev, seq);
}
}
static bool mt76x02_dfs_check_chirp(struct mt76x02_dev *dev)
{
bool ret = false;
u32 current_ts, delta_ts;
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER);
delta_ts = current_ts - dfs_pd->chirp_pulse_ts;
dfs_pd->chirp_pulse_ts = current_ts;
/* 12 sec */
if (delta_ts <= (12 * (1 << 20))) {
if (++dfs_pd->chirp_pulse_cnt > 8)
ret = true;
} else {
dfs_pd->chirp_pulse_cnt = 1;
}
return ret;
}
static void mt76x02_dfs_get_hw_pulse(struct mt76x02_dev *dev,
struct mt76x02_dfs_hw_pulse *pulse)
{
u32 data;
/* select channel */
data = (MT_DFS_CH_EN << 16) | pulse->engine;
mt76_wr(dev, MT_BBP(DFS, 0), data);
/* reported period */
pulse->period = mt76_rr(dev, MT_BBP(DFS, 19));
/* reported width */
pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20));
pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23));
/* reported burst number */
pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22));
}
static bool mt76x02_dfs_check_hw_pulse(struct mt76x02_dev *dev,
struct mt76x02_dfs_hw_pulse *pulse)
{
bool ret = false;
if (!pulse->period || !pulse->w1)
return false;
switch (dev->mt76.region) {
case NL80211_DFS_FCC:
if (pulse->engine > 3)
break;
if (pulse->engine == 3) {
ret = mt76x02_dfs_check_chirp(dev);
break;
}
/* check short pulse*/
if (pulse->w1 < 120)
ret = (pulse->period >= 2900 &&
(pulse->period <= 4700 ||
pulse->period >= 6400) &&
(pulse->period <= 6800 ||
pulse->period >= 10200) &&
pulse->period <= 61600);
else if (pulse->w1 < 130) /* 120 - 130 */
ret = (pulse->period >= 2900 &&
pulse->period <= 61600);
else
ret = (pulse->period >= 3500 &&
pulse->period <= 10100);
break;
case NL80211_DFS_ETSI:
if (pulse->engine >= 3)
break;
ret = (pulse->period >= 4900 &&
(pulse->period <= 10200 ||
pulse->period >= 12400) &&
pulse->period <= 100100);
break;
case NL80211_DFS_JP:
if (dev->mphy.chandef.chan->center_freq >= 5250 &&
dev->mphy.chandef.chan->center_freq <= 5350) {
/* JPW53 */
if (pulse->w1 <= 130)
ret = (pulse->period >= 28360 &&
(pulse->period <= 28700 ||
pulse->period >= 76900) &&
pulse->period <= 76940);
break;
}
if (pulse->engine > 3)
break;
if (pulse->engine == 3) {
ret = mt76x02_dfs_check_chirp(dev);
break;
}
/* check short pulse*/
if (pulse->w1 < 120)
ret = (pulse->period >= 2900 &&
(pulse->period <= 4700 ||
pulse->period >= 6400) &&
(pulse->period <= 6800 ||
pulse->period >= 27560) &&
(pulse->period <= 27960 ||
pulse->period >= 28360) &&
(pulse->period <= 28700 ||
pulse->period >= 79900) &&
pulse->period <= 80100);
else if (pulse->w1 < 130) /* 120 - 130 */
ret = (pulse->period >= 2900 &&
(pulse->period <= 10100 ||
pulse->period >= 27560) &&
(pulse->period <= 27960 ||
pulse->period >= 28360) &&
(pulse->period <= 28700 ||
pulse->period >= 79900) &&
pulse->period <= 80100);
else
ret = (pulse->period >= 3900 &&
pulse->period <= 10100);
break;
case NL80211_DFS_UNSET:
default:
return false;
}
return ret;
}
static bool mt76x02_dfs_fetch_event(struct mt76x02_dev *dev,
struct mt76x02_dfs_event *event)
{
u32 data;
/* 1st: DFS_R37[31]: 0 (engine 0) - 1 (engine 2)
* 2nd: DFS_R37[21:0]: pulse time
* 3rd: DFS_R37[11:0]: pulse width
* 3rd: DFS_R37[25:16]: phase
* 4th: DFS_R37[12:0]: current pwr
* 4th: DFS_R37[21:16]: pwr stable counter
*
* 1st: DFS_R37[31:0] set to 0xffffffff means no event detected
*/
data = mt76_rr(dev, MT_BBP(DFS, 37));
if (!MT_DFS_CHECK_EVENT(data))
return false;
event->engine = MT_DFS_EVENT_ENGINE(data);
data = mt76_rr(dev, MT_BBP(DFS, 37));
event->ts = MT_DFS_EVENT_TIMESTAMP(data);
data = mt76_rr(dev, MT_BBP(DFS, 37));
event->width = MT_DFS_EVENT_WIDTH(data);
return true;
}
static bool mt76x02_dfs_check_event(struct mt76x02_dev *dev,
struct mt76x02_dfs_event *event)
{
if (event->engine == 2) {
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_event_rb *event_buff = &dfs_pd->event_rb[1];
u16 last_event_idx;
u32 delta_ts;
last_event_idx = mt76_decr(event_buff->t_rb,
MT_DFS_EVENT_BUFLEN);
delta_ts = event->ts - event_buff->data[last_event_idx].ts;
if (delta_ts < MT_DFS_EVENT_TIME_MARGIN &&
event_buff->data[last_event_idx].width >= 200)
return false;
}
return true;
}
static void mt76x02_dfs_queue_event(struct mt76x02_dev *dev,
struct mt76x02_dfs_event *event)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_event_rb *event_buff;
/* add radar event to ring buffer */
event_buff = event->engine == 2 ? &dfs_pd->event_rb[1]
: &dfs_pd->event_rb[0];
event_buff->data[event_buff->t_rb] = *event;
event_buff->data[event_buff->t_rb].fetch_ts = jiffies;
event_buff->t_rb = mt76_incr(event_buff->t_rb, MT_DFS_EVENT_BUFLEN);
if (event_buff->t_rb == event_buff->h_rb)
event_buff->h_rb = mt76_incr(event_buff->h_rb,
MT_DFS_EVENT_BUFLEN);
}
static int mt76x02_dfs_create_sequence(struct mt76x02_dev *dev,
struct mt76x02_dfs_event *event,
u16 cur_len)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_sw_detector_params *sw_params;
u32 width_delta, with_sum;
struct mt76x02_dfs_sequence seq, *seq_p;
struct mt76x02_dfs_event_rb *event_rb;
struct mt76x02_dfs_event *cur_event;
int i, j, end, pri, factor, cur_pri;
event_rb = event->engine == 2 ? &dfs_pd->event_rb[1]
: &dfs_pd->event_rb[0];
i = mt76_decr(event_rb->t_rb, MT_DFS_EVENT_BUFLEN);
end = mt76_decr(event_rb->h_rb, MT_DFS_EVENT_BUFLEN);
while (i != end) {
cur_event = &event_rb->data[i];
with_sum = event->width + cur_event->width;
sw_params = &dfs_pd->sw_dpd_params;
switch (dev->mt76.region) {
case NL80211_DFS_FCC:
case NL80211_DFS_JP:
if (with_sum < 600)
width_delta = 8;
else
width_delta = with_sum >> 3;
break;
case NL80211_DFS_ETSI:
if (event->engine == 2)
width_delta = with_sum >> 6;
else if (with_sum < 620)
width_delta = 24;
else
width_delta = 8;
break;
case NL80211_DFS_UNSET:
default:
return -EINVAL;
}
pri = event->ts - cur_event->ts;
if (abs(event->width - cur_event->width) > width_delta ||
pri < sw_params->min_pri)
goto next;
if (pri > sw_params->max_pri)
break;
seq.pri = event->ts - cur_event->ts;
seq.first_ts = cur_event->ts;
seq.last_ts = event->ts;
seq.engine = event->engine;
seq.count = 2;
j = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
while (j != end) {
cur_event = &event_rb->data[j];
cur_pri = event->ts - cur_event->ts;
factor = mt76x02_dfs_get_multiple(cur_pri, seq.pri,
sw_params->pri_margin);
if (factor > 0) {
seq.first_ts = cur_event->ts;
seq.count++;
}
j = mt76_decr(j, MT_DFS_EVENT_BUFLEN);
}
if (seq.count <= cur_len)
goto next;
seq_p = mt76x02_dfs_seq_pool_get(dev);
if (!seq_p)
return -ENOMEM;
*seq_p = seq;
INIT_LIST_HEAD(&seq_p->head);
list_add(&seq_p->head, &dfs_pd->sequences);
next:
i = mt76_decr(i, MT_DFS_EVENT_BUFLEN);
}
return 0;
}
static u16 mt76x02_dfs_add_event_to_sequence(struct mt76x02_dev *dev,
struct mt76x02_dfs_event *event)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_sw_detector_params *sw_params;
struct mt76x02_dfs_sequence *seq, *tmp_seq;
u16 max_seq_len = 0;
int factor, pri;
sw_params = &dfs_pd->sw_dpd_params;
list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) {
if (event->ts > seq->first_ts + MT_DFS_SEQUENCE_WINDOW) {
list_del_init(&seq->head);
mt76x02_dfs_seq_pool_put(dev, seq);
continue;
}
if (event->engine != seq->engine)
continue;
pri = event->ts - seq->last_ts;
factor = mt76x02_dfs_get_multiple(pri, seq->pri,
sw_params->pri_margin);
if (factor > 0) {
seq->last_ts = event->ts;
seq->count++;
max_seq_len = max_t(u16, max_seq_len, seq->count);
}
}
return max_seq_len;
}
static bool mt76x02_dfs_check_detection(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_sequence *seq;
if (list_empty(&dfs_pd->sequences))
return false;
list_for_each_entry(seq, &dfs_pd->sequences, head) {
if (seq->count > MT_DFS_SEQUENCE_TH) {
dfs_pd->stats[seq->engine].sw_pattern++;
return true;
}
}
return false;
}
static void mt76x02_dfs_add_events(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_event event;
int i, seq_len;
/* disable debug mode */
mt76x02_dfs_set_capture_mode_ctrl(dev, false);
for (i = 0; i < MT_DFS_EVENT_LOOP; i++) {
if (!mt76x02_dfs_fetch_event(dev, &event))
break;
if (dfs_pd->last_event_ts > event.ts)
mt76x02_dfs_detector_reset(dev);
dfs_pd->last_event_ts = event.ts;
if (!mt76x02_dfs_check_event(dev, &event))
continue;
seq_len = mt76x02_dfs_add_event_to_sequence(dev, &event);
mt76x02_dfs_create_sequence(dev, &event, seq_len);
mt76x02_dfs_queue_event(dev, &event);
}
mt76x02_dfs_set_capture_mode_ctrl(dev, true);
}
static void mt76x02_dfs_check_event_window(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
struct mt76x02_dfs_event_rb *event_buff;
struct mt76x02_dfs_event *event;
int i;
for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) {
event_buff = &dfs_pd->event_rb[i];
while (event_buff->h_rb != event_buff->t_rb) {
event = &event_buff->data[event_buff->h_rb];
/* sorted list */
if (time_is_after_jiffies(event->fetch_ts +
MT_DFS_EVENT_WINDOW))
break;
event_buff->h_rb = mt76_incr(event_buff->h_rb,
MT_DFS_EVENT_BUFLEN);
}
}
}
static void mt76x02_dfs_tasklet(struct tasklet_struct *t)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = from_tasklet(dfs_pd, t,
dfs_tasklet);
struct mt76x02_dev *dev = container_of(dfs_pd, typeof(*dev), dfs_pd);
u32 engine_mask;
int i;
if (test_bit(MT76_SCANNING, &dev->mphy.state))
goto out;
if (time_is_before_jiffies(dfs_pd->last_sw_check +
MT_DFS_SW_TIMEOUT)) {
bool radar_detected;
dfs_pd->last_sw_check = jiffies;
mt76x02_dfs_add_events(dev);
radar_detected = mt76x02_dfs_check_detection(dev);
if (radar_detected) {
/* sw detector rx radar pattern */
ieee80211_radar_detected(dev->mt76.hw, NULL);
mt76x02_dfs_detector_reset(dev);
return;
}
mt76x02_dfs_check_event_window(dev);
}
engine_mask = mt76_rr(dev, MT_BBP(DFS, 1));
if (!(engine_mask & 0xf))
goto out;
for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
struct mt76x02_dfs_hw_pulse pulse;
if (!(engine_mask & (1 << i)))
continue;
pulse.engine = i;
mt76x02_dfs_get_hw_pulse(dev, &pulse);
if (!mt76x02_dfs_check_hw_pulse(dev, &pulse)) {
dfs_pd->stats[i].hw_pulse_discarded++;
continue;
}
/* hw detector rx radar pattern */
dfs_pd->stats[i].hw_pattern++;
ieee80211_radar_detected(dev->mt76.hw, NULL);
mt76x02_dfs_detector_reset(dev);
return;
}
/* reset hw detector */
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
out:
mt76x02_irq_enable(dev, MT_INT_GPTIMER);
}
static void mt76x02_dfs_init_sw_detector(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
switch (dev->mt76.region) {
case NL80211_DFS_FCC:
dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI;
dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI;
dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
break;
case NL80211_DFS_ETSI:
dfs_pd->sw_dpd_params.max_pri = MT_DFS_ETSI_MAX_PRI;
dfs_pd->sw_dpd_params.min_pri = MT_DFS_ETSI_MIN_PRI;
dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN << 2;
break;
case NL80211_DFS_JP:
dfs_pd->sw_dpd_params.max_pri = MT_DFS_JP_MAX_PRI;
dfs_pd->sw_dpd_params.min_pri = MT_DFS_JP_MIN_PRI;
dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN;
break;
case NL80211_DFS_UNSET:
default:
break;
}
}
static void mt76x02_dfs_set_bbp_params(struct mt76x02_dev *dev)
{
const struct mt76x02_radar_specs *radar_specs;
u8 i, shift;
u32 data;
switch (dev->mphy.chandef.width) {
case NL80211_CHAN_WIDTH_40:
shift = MT_DFS_NUM_ENGINES;
break;
case NL80211_CHAN_WIDTH_80:
shift = 2 * MT_DFS_NUM_ENGINES;
break;
default:
shift = 0;
break;
}
switch (dev->mt76.region) {
case NL80211_DFS_FCC:
radar_specs = &fcc_radar_specs[shift];
break;
case NL80211_DFS_ETSI:
radar_specs = &etsi_radar_specs[shift];
break;
case NL80211_DFS_JP:
if (dev->mphy.chandef.chan->center_freq >= 5250 &&
dev->mphy.chandef.chan->center_freq <= 5350)
radar_specs = &jp_w53_radar_specs[shift];
else
radar_specs = &jp_w56_radar_specs[shift];
break;
case NL80211_DFS_UNSET:
default:
return;
}
data = (MT_DFS_VGA_MASK << 16) |
(MT_DFS_PWR_GAIN_OFFSET << 12) |
(MT_DFS_PWR_DOWN_TIME << 8) |
(MT_DFS_SYM_ROUND << 4) |
(MT_DFS_DELTA_DELAY & 0xf);
mt76_wr(dev, MT_BBP(DFS, 2), data);
data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK;
mt76_wr(dev, MT_BBP(DFS, 3), data);
for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
/* configure engine */
mt76_wr(dev, MT_BBP(DFS, 0), i);
/* detection mode + avg_len */
data = ((radar_specs[i].avg_len & 0x1ff) << 16) |
(radar_specs[i].mode & 0xf);
mt76_wr(dev, MT_BBP(DFS, 4), data);
/* dfs energy */
data = ((radar_specs[i].e_high & 0x0fff) << 16) |
(radar_specs[i].e_low & 0x0fff);
mt76_wr(dev, MT_BBP(DFS, 5), data);
/* dfs period */
mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low);
mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high);
/* dfs burst */
mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low);
mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high);
/* dfs width */
data = ((radar_specs[i].w_high & 0x0fff) << 16) |
(radar_specs[i].w_low & 0x0fff);
mt76_wr(dev, MT_BBP(DFS, 14), data);
/* dfs margins */
data = (radar_specs[i].w_margin << 16) |
radar_specs[i].t_margin;
mt76_wr(dev, MT_BBP(DFS, 15), data);
/* dfs event expiration */
mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration);
/* dfs pwr adj */
mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp);
}
/* reset status */
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
mt76_wr(dev, MT_BBP(DFS, 36), 0x3);
/* enable detection*/
mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
mt76_wr(dev, MT_BBP(IBI, 11), 0x0c350001);
}
void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev)
{
u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31;
agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8));
agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4));
val_r8 = (agc_r8 & 0x00007e00) >> 9;
val_r4 = agc_r4 & ~0x1f000000;
val_r4 += (((val_r8 + 1) >> 1) << 24);
mt76_wr(dev, MT_BBP(AGC, 4), val_r4);
dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4);
dfs_r31 += val_r8;
dfs_r31 -= (agc_r8 & 0x00000038) >> 3;
dfs_r31 = (dfs_r31 << 16) | 0x00000307;
mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31);
if (is_mt76x2(dev)) {
mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071);
} else {
/* disable hw detector */
mt76_wr(dev, MT_BBP(DFS, 0), 0);
/* enable hw detector */
mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
}
}
EXPORT_SYMBOL_GPL(mt76x02_phy_dfs_adjust_agc);
void mt76x02_dfs_init_params(struct mt76x02_dev *dev)
{
if (mt76_phy_dfs_state(&dev->mphy) > MT_DFS_STATE_DISABLED) {
mt76x02_dfs_init_sw_detector(dev);
mt76x02_dfs_set_bbp_params(dev);
/* enable debug mode */
mt76x02_dfs_set_capture_mode_ctrl(dev, true);
mt76x02_irq_enable(dev, MT_INT_GPTIMER);
mt76_rmw_field(dev, MT_INT_TIMER_EN,
MT_INT_TIMER_EN_GP_TIMER_EN, 1);
} else {
/* disable hw detector */
mt76_wr(dev, MT_BBP(DFS, 0), 0);
/* clear detector status */
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
if (mt76_chip(&dev->mt76) == 0x7610 ||
mt76_chip(&dev->mt76) == 0x7630)
mt76_wr(dev, MT_BBP(IBI, 11), 0xfde8081);
else
mt76_wr(dev, MT_BBP(IBI, 11), 0);
mt76x02_irq_disable(dev, MT_INT_GPTIMER);
mt76_rmw_field(dev, MT_INT_TIMER_EN,
MT_INT_TIMER_EN_GP_TIMER_EN, 0);
}
}
EXPORT_SYMBOL_GPL(mt76x02_dfs_init_params);
void mt76x02_dfs_init_detector(struct mt76x02_dev *dev)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
INIT_LIST_HEAD(&dfs_pd->sequences);
INIT_LIST_HEAD(&dfs_pd->seq_pool);
dev->mt76.region = NL80211_DFS_UNSET;
dfs_pd->last_sw_check = jiffies;
tasklet_setup(&dfs_pd->dfs_tasklet, mt76x02_dfs_tasklet);
}
static void
mt76x02_dfs_set_domain(struct mt76x02_dev *dev,
enum nl80211_dfs_regions region)
{
struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
mutex_lock(&dev->mt76.mutex);
if (dev->mt76.region != region) {
tasklet_disable(&dfs_pd->dfs_tasklet);
dev->ed_monitor = dev->ed_monitor_enabled &&
region == NL80211_DFS_ETSI;
mt76x02_edcca_init(dev);
dev->mt76.region = region;
mt76x02_dfs_init_params(dev);
tasklet_enable(&dfs_pd->dfs_tasklet);
}
mutex_unlock(&dev->mt76.mutex);
}
void mt76x02_regd_notifier(struct wiphy *wiphy,
struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct mt76x02_dev *dev = hw->priv;
mt76x02_dfs_set_domain(dev, request->dfs_region);
}