From 6375fc1abef935726d39b4e27cb933c9cb28dd3e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 25 Sep 2016 16:13:02 +0200 Subject: [PATCH] mt76x2: add hw radar detector add hw DFS pattern detector support for mt76x2 based devices DFS region supported: - FCC - ETSI - JAPAN_W53 - JAPAN_W56 Signed-off-by: Lorenzo Bianconi --- Makefile | 2 +- mt76x2.h | 3 + mt76x2_core.c | 5 + mt76x2_dfs.c | 512 ++++++++++++++++++++++++++++++++++++++++++++++++++ mt76x2_dfs.h | 84 +++++++++ mt76x2_init.c | 15 ++ mt76x2_main.c | 2 + mt76x2_phy.c | 3 + mt76x2_regs.h | 2 + 9 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 mt76x2_dfs.c create mode 100644 mt76x2_dfs.h diff --git a/Makefile b/Makefile index b0592f43..70df76b2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ mt76x2e-y := \ mt76x2_pci.o mt76x2_dma.o \ mt76x2_main.o mt76x2_init.o mt76x2_debugfs.o mt76x2_tx.o \ mt76x2_core.o mt76x2_mac.o mt76x2_eeprom.o mt76x2_mcu.o mt76x2_phy.o \ - mt76x2_trace.o + mt76x2_dfs.o mt76x2_trace.o mt7603e-y := \ mt7603_pci.o mt7603_soc.o mt7603_main.o mt7603_init.o mt7603_mcu.o \ diff --git a/mt76x2.h b/mt76x2.h index 94405994..6311eaa8 100644 --- a/mt76x2.h +++ b/mt76x2.h @@ -41,6 +41,7 @@ #include "mt76.h" #include "mt76x2_regs.h" #include "mt76x2_mac.h" +#include "mt76x2_dfs.h" struct mt76x2_mcu { struct mutex mutex; @@ -126,6 +127,8 @@ struct mt76x2_dev { u8 coverage_class; u8 slottime; + + struct mt76x2_dfs_pattern_detector dfs_pd; }; struct mt76x2_vif { diff --git a/mt76x2_core.c b/mt76x2_core.c index 3449e166..2629779e 100644 --- a/mt76x2_core.c +++ b/mt76x2_core.c @@ -78,6 +78,11 @@ irqreturn_t mt76x2_irq_handler(int irq, void *dev_instance) tasklet_schedule(&dev->tx_tasklet); } + if (intr & MT_INT_GPTIMER) { + mt76x2_irq_disable(dev, MT_INT_GPTIMER); + tasklet_schedule(&dev->dfs_pd.dfs_tasklet); + } + return IRQ_HANDLED; } diff --git a/mt76x2_dfs.c b/mt76x2_dfs.c new file mode 100644 index 00000000..b382be9c --- /dev/null +++ b/mt76x2_dfs.c @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2016 Lorenzo Bianconi + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mt76x2.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 mt76x2_radar_specs etsi_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 9, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 44, 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, 9, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 44, 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, 9, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 44, 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 mt76x2_radar_specs fcc_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 7, 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, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289), + /* 40MHz */ + RADAR_SPEC(0, 8, 2, 7, 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, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289), + /* 80MHz */ + RADAR_SPEC(0, 8, 2, 9, 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, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289) +}; + +static const struct mt76x2_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 mt76x2_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 mt76x2_dfs_set_capture_mode_ctrl(struct mt76x2_dev *dev, + u8 enable) +{ + u32 data; + + data = (1 << 1) | enable; + mt76_wr(dev, MT_BBP(DFS, 36), data); +} + +static bool mt76x2_dfs_check_chirp(struct mt76x2_dev *dev) +{ + bool ret = false; + u32 current_ts, delta_ts; + struct mt76x2_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 mt76x2_dfs_get_hw_pulse(struct mt76x2_dev *dev, + struct mt76x2_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 mt76x2_dfs_check_hw_pulse(struct mt76x2_dev *dev, + struct mt76x2_dfs_hw_pulse *pulse) +{ + bool ret = false; + + if (!pulse->period || !pulse->w1) + return false; + + switch (dev->dfs_pd.region) { + case NL80211_DFS_FCC: + if (pulse->engine < 3) { + /* 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 >= 3900 && + pulse->period <= 10100); + } else if (pulse->engine == 3) { + ret = mt76x2_dfs_check_chirp(dev); + } + break; + case NL80211_DFS_ETSI: + if (pulse->engine < 3) { + ret = (pulse->period >= 4900 && + (pulse->period <= 10200 || + pulse->period >= 12400) && + pulse->period <= 100100); + } + break; + case NL80211_DFS_JP: + if (dev->mt76.chandef.chan->center_freq >= 5250 && + dev->mt76.chandef.chan->center_freq <= 5350) { + /* JPW53 */ + if (pulse->w1 <= 130) + ret = (pulse->period >= 28360 && + (pulse->period <= 28700 || + pulse->period >= 76900) && + pulse->period <= 76940); + } else { + if (pulse->engine < 3) { + /* 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); + } else if (pulse->engine == 3) { + ret = mt76x2_dfs_check_chirp(dev); + } + } + break; + case NL80211_DFS_UNSET: + default: + return false; + } + + return ret; +} + +static void mt76x2_dfs_chan_state(struct mt76x2_dev *dev) +{ + u64 ch_active_time, ch_busy_time; + u32 ch_active_delta, ch_busy_delta; + struct mt76_channel_state *state; + struct cfg80211_chan_def *chandef = &dev->mt76.chandef; + struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + mt76x2_update_channel(&dev->mt76); + state = mt76_channel_state(&dev->mt76, chandef->chan); + + spin_lock_bh(&dev->mt76.cc_lock); + ch_active_time = state->cc_active; + ch_busy_time = state->cc_busy; + spin_unlock_bh(&dev->mt76.cc_lock); + + ch_active_delta = ch_active_time - dfs_pd->ch_active_time; + ch_busy_delta = ch_busy_time - dfs_pd->ch_busy_time; + + dfs_pd->ch_busy = (MT_DFS_FRAC(ch_busy_delta, ch_active_delta) > + MT_DFS_FRAC(80, 100)); + + dfs_pd->ch_active_time = ch_active_time; + dfs_pd->ch_busy_time = ch_busy_time; +} + +void mt76x2_dfs_tasklet(unsigned long arg) +{ + u32 engine_mask; + struct mt76x2_dev *dev = (struct mt76x2_dev *)arg; + struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + if (test_bit(MT76_SCANNING, &dev->mt76.state)) + goto out; + + if (++dfs_pd->ch_state_cnt > MT_DFS_CH_STATE_INTERVAL) { + dfs_pd->ch_state_cnt = 0; + mt76x2_dfs_chan_state(dev); + } + + if (dfs_pd->ch_busy) + goto out; + + engine_mask = mt76_rr(dev, MT_BBP(DFS, 1)); + if (engine_mask & 0xf) { + int i; + + for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { + if (engine_mask & (1 << i)) { + struct mt76x2_dfs_hw_pulse pulse; + + pulse.engine = i; + mt76x2_dfs_get_hw_pulse(dev, &pulse); + + if (mt76x2_dfs_check_hw_pulse(dev, &pulse)) { + /* hw detector rx radar pattern */ + ieee80211_radar_detected(dev->mt76.hw); + /* reset hw detector */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + + return; + } + } + } + /* reset hw detector */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + } + +out: + mt76x2_irq_enable(dev, MT_INT_GPTIMER); +} + +static void mt76x2_dfs_set_bbp_params(struct mt76x2_dev *dev) +{ + u32 data; + u8 i, shift; + const struct mt76x2_radar_specs *radar_specs; + + switch (dev->mt76.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->dfs_pd.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->mt76.chandef.chan->center_freq >= 5250 && + dev->mt76.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, 0x212c, 0x0c350001); +} + +void mt76x2_dfs_adjust_agc(struct mt76x2_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_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); + + mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071); +} + +void mt76x2_dfs_init_params(struct mt76x2_dev *dev) +{ + struct cfg80211_chan_def *chandef = &dev->mt76.chandef; + + tasklet_kill(&dev->dfs_pd.dfs_tasklet); + if (chandef->chan->flags & IEEE80211_CHAN_RADAR) { + mt76x2_dfs_set_bbp_params(dev); + /* enable debug mode */ + mt76x2_dfs_set_capture_mode_ctrl(dev, true); + + mt76x2_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); + mt76_wr(dev, 0x212c, 0); + + mt76x2_irq_disable(dev, MT_INT_GPTIMER); + mt76_rmw_field(dev, MT_INT_TIMER_EN, + MT_INT_TIMER_EN_GP_TIMER_EN, 0); + } +} + +void mt76x2_dfs_init_detector(struct mt76x2_dev *dev) +{ + struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + memset(dfs_pd, 0, sizeof(*dfs_pd)); + + dfs_pd->region = NL80211_DFS_UNSET; + tasklet_init(&dfs_pd->dfs_tasklet, mt76x2_dfs_tasklet, + (unsigned long)dev); +} + diff --git a/mt76x2_dfs.h b/mt76x2_dfs.h new file mode 100644 index 00000000..8a029595 --- /dev/null +++ b/mt76x2_dfs.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Lorenzo Bianconi + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT76x2_DFS_H +#define __MT76x2_DFS_H + +#include +#include + +#define MT_DFS_GP_INTERVAL (10 << 4) /* 64 us unit */ +#define MT_DFS_CH_STATE_INTERVAL 20 +#define MT_DFS_NUM_ENGINES 4 + +/* scaled fraction values */ +#define MT_DFS_SCALE 16 +#define MT_DFS_FRAC(val, div) (((val) << MT_DFS_SCALE) / div) + +/* bbp params */ +#define MT_DFS_SYM_ROUND 0 +#define MT_DFS_DELTA_DELAY 2 +#define MT_DFS_VGA_MASK 0 +#define MT_DFS_PWR_GAIN_OFFSET 3 +#define MT_DFS_PWR_DOWN_TIME 0xf +#define MT_DFS_RX_PE_MASK 0xff +#define MT_DFS_PKT_END_MASK 0 +#define MT_DFS_CH_EN 0xf + +struct mt76x2_radar_specs { + u8 mode; + u16 avg_len; + u16 e_low; + u16 e_high; + u16 w_low; + u16 w_high; + u16 w_margin; + u32 t_low; + u32 t_high; + u16 t_margin; + u32 b_low; + u32 b_high; + u32 event_expiration; + u16 pwr_jmp; +}; + +struct mt76x2_dfs_hw_pulse { + u8 engine; + u32 period; + u32 w1; + u32 w2; + u32 burst; +}; + +struct mt76x2_dfs_pattern_detector { + enum nl80211_dfs_regions region; + + u8 chirp_pulse_cnt; + u32 chirp_pulse_ts; + + u16 ch_state_cnt; + u64 ch_active_time; + u64 ch_busy_time; + bool ch_busy; + + struct tasklet_struct dfs_tasklet; +}; + +void mt76x2_dfs_init_params(struct mt76x2_dev *dev); +void mt76x2_dfs_init_detector(struct mt76x2_dev *dev); +void mt76x2_dfs_adjust_agc(struct mt76x2_dev *dev); + +#endif /* __MT76x2_DFS_H */ diff --git a/mt76x2_init.c b/mt76x2_init.c index 1fbd842b..7e946494 100644 --- a/mt76x2_init.c +++ b/mt76x2_init.c @@ -282,6 +282,8 @@ int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard) /* Fire a pre-TBTT interrupt 8 ms before TBTT */ mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT, 8 << 4); + mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_GP_TIMER, + MT_DFS_GP_INTERVAL); mt76_wr(dev, MT_INT_TIMER_EN, 0); mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff); @@ -642,6 +644,15 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev) return dev; } +static void mt76x2_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt76x2_dev *dev = hw->priv; + + dev->dfs_pd.region = request->dfs_region; +} + #define CCK_RATE(_idx, _rate) { \ .bitrate = _rate, \ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ @@ -739,6 +750,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev) wiphy->iface_combinations = if_comb; wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->reg_notifier = mt76x2_regd_notifier; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); @@ -748,6 +761,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev) dev->mt76.sband_2g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; dev->mt76.sband_5g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + mt76x2_dfs_init_detector(dev); + ret = mt76_register_device(&dev->mt76, true, mt76x2_rates, ARRAY_SIZE(mt76x2_rates)); if (ret) diff --git a/mt76x2_main.c b/mt76x2_main.c index 1cd648ef..bb6b3a75 100644 --- a/mt76x2_main.c +++ b/mt76x2_main.c @@ -137,6 +137,8 @@ mt76x2_set_channel(struct mt76x2_dev *dev, struct cfg80211_chan_def *chandef) mt76_rr(dev, MT_CH_IDLE); mt76_rr(dev, MT_CH_BUSY); + mt76x2_dfs_init_params(dev); + mt76x2_mac_resume(dev); tasklet_enable(&dev->pre_tbtt_tasklet); diff --git a/mt76x2_phy.c b/mt76x2_phy.c index 71b57b42..0a41427b 100644 --- a/mt76x2_phy.c +++ b/mt76x2_phy.c @@ -49,6 +49,9 @@ mt76x2_apply_gain_adj(struct mt76x2_dev *dev) mt76x2_adjust_agc_gain(dev, 8, gain_adj[0]); mt76x2_adjust_agc_gain(dev, 9, gain_adj[1]); + + if (dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR) + mt76x2_dfs_adjust_agc(dev); } static u32 diff --git a/mt76x2_regs.h b/mt76x2_regs.h index 1da69943..07b30805 100644 --- a/mt76x2_regs.h +++ b/mt76x2_regs.h @@ -266,6 +266,8 @@ #define MT_CH_TIME_CFG_CH_TIMER_CLR GENMASK(9, 8) #define MT_CH_TIME_CFG_MDRDY_CLR GENMASK(11, 10) +#define MT_PBF_LIFE_TIMER 0x1110 + #define MT_BEACON_TIME_CFG 0x1114 #define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0) #define MT_BEACON_TIME_CFG_TIMER_EN BIT(16)