From patchwork Mon Nov 20 19:35:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 10067145 X-Patchwork-Delegate: kvalo@adurom.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D1969603FA for ; Mon, 20 Nov 2017 19:36:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B3B2129085 for ; Mon, 20 Nov 2017 19:36:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A6DD42936F; Mon, 20 Nov 2017 19:36:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 075A929317 for ; Mon, 20 Nov 2017 19:36:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752610AbdKTTf7 (ORCPT ); Mon, 20 Nov 2017 14:35:59 -0500 Received: from nbd.name ([46.4.11.11]:60492 "EHLO nbd.name" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752411AbdKTTfw (ORCPT ); Mon, 20 Nov 2017 14:35:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=WGm8nJxReUMl214aex9xW5Th6+JPL7oG5aQmZZV84CU=; b=WB1H2mgTx6A8WNZ0ITj9Kx4D2K 5K6ALZCIY97z8vobsKiMLy3yTuZlIOfHyhALNELgLNEOuvYTFxFGMKXDd0sJ82LdA810azM9DwdqW 1sQ6Hgumk+BoiC4D38pWIcbR6rnXOpHgOd66+twIEDfkVyVQTXHpNFlkvN9aL4Fy5kgI=; Received: by nf.local (Postfix, from userid 501) id 5E2C41BBD3FC8; Mon, 20 Nov 2017 20:35:49 +0100 (CET) From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: kvalo@codeaurora.org Subject: [PATCH v7 3/3] mt76: add driver code for MT76x2e Date: Mon, 20 Nov 2017 20:35:49 +0100 Message-Id: <20171120193549.80831-4-nbd@nbd.name> X-Mailer: git-send-email 2.14.2 In-Reply-To: <20171120193549.80831-1-nbd@nbd.name> References: <20171120193549.80831-1-nbd@nbd.name> Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP MT76x2e is a 2x2 PCIe 802.11ac chipset by MediaTek. This driver has full support for AP, station, ad-hoc, mesh and monitor mode. Signed-off-by: Felix Fietkau Signed-off-by: Lorenzo Bianconi --- drivers/net/wireless/mediatek/Kconfig | 1 + drivers/net/wireless/mediatek/Makefile | 1 + drivers/net/wireless/mediatek/mt76/Kconfig | 10 + drivers/net/wireless/mediatek/mt76/Makefile | 15 + drivers/net/wireless/mediatek/mt76/mt76x2.h | 227 ++++++ drivers/net/wireless/mediatek/mt76/mt76x2_core.c | 88 +++ .../net/wireless/mediatek/mt76/mt76x2_debugfs.c | 133 ++++ drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c | 493 ++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h | 80 ++ drivers/net/wireless/mediatek/mt76/mt76x2_dma.c | 183 +++++ drivers/net/wireless/mediatek/mt76/mt76x2_dma.h | 68 ++ drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 647 ++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h | 182 +++++ drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 839 +++++++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_mac.c | 755 ++++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_mac.h | 190 +++++ drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 545 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c | 451 +++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h | 155 ++++ drivers/net/wireless/mediatek/mt76/mt76x2_pci.c | 110 +++ drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 758 +++++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_regs.h | 587 ++++++++++++++ drivers/net/wireless/mediatek/mt76/mt76x2_trace.c | 23 + drivers/net/wireless/mediatek/mt76/mt76x2_trace.h | 144 ++++ drivers/net/wireless/mediatek/mt76/mt76x2_tx.c | 258 +++++++ 25 files changed, 6943 insertions(+) create mode 100644 drivers/net/wireless/mediatek/mt76/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_core.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_dma.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2_tx.c diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig index 28843fed750a..92ce4062f307 100644 --- a/drivers/net/wireless/mediatek/Kconfig +++ b/drivers/net/wireless/mediatek/Kconfig @@ -11,4 +11,5 @@ config WLAN_VENDOR_MEDIATEK if WLAN_VENDOR_MEDIATEK source "drivers/net/wireless/mediatek/mt7601u/Kconfig" +source "drivers/net/wireless/mediatek/mt76/Kconfig" endif # WLAN_VENDOR_MEDIATEK diff --git a/drivers/net/wireless/mediatek/Makefile b/drivers/net/wireless/mediatek/Makefile index 9d5f182fd7fd..00f945f59b38 100644 --- a/drivers/net/wireless/mediatek/Makefile +++ b/drivers/net/wireless/mediatek/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MT7601U) += mt7601u/ +obj-$(CONFIG_MT76_CORE) += mt76/ diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig new file mode 100644 index 000000000000..fc05d79c80d0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -0,0 +1,10 @@ +config MT76_CORE + tristate + +config MT76x2E + tristate "MediaTek MT76x2E (PCIe) support" + select MT76_CORE + depends on MAC80211 + depends on PCI + ---help--- + This adds support for MT7612/MT7602/MT7662-based wireless PCIe devices. diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile new file mode 100644 index 000000000000..2bb919863616 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -0,0 +1,15 @@ +obj-$(CONFIG_MT76_CORE) += mt76.o +obj-$(CONFIG_MT76x2E) += mt76x2e.o + +mt76-y := \ + mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o + +CFLAGS_trace.o := -I$(src) + +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_dfs.o mt76x2_trace.o + +CFLAGS_mt76x2_trace.o := -I$(src) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h new file mode 100644 index 000000000000..a12dfce8c0d1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_H +#define __MT76x2_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MT7662_FIRMWARE "mt7662.bin" +#define MT7662_ROM_PATCH "mt7662_rom_patch.bin" +#define MT7662_EEPROM_SIZE 512 + +#define MT76x2_RX_RING_SIZE 256 +#define MT_RX_HEADROOM 32 + +#define MT_MAX_CHAINS 2 + +#define MT_CALIBRATE_INTERVAL HZ + +#include "mt76.h" +#include "mt76x2_regs.h" +#include "mt76x2_mac.h" +#include "mt76x2_dfs.h" + +struct mt76x2_mcu { + struct mutex mutex; + + wait_queue_head_t wait; + struct sk_buff_head res_q; + + u32 msg_seq; +}; + +struct mt76x2_rx_freq_cal { + s8 high_gain[MT_MAX_CHAINS]; + s8 rssi_offset[MT_MAX_CHAINS]; + s8 lna_gain; + u32 mcu_gain; +}; + +struct mt76x2_calibration { + struct mt76x2_rx_freq_cal rx; + + u8 agc_gain_init[MT_MAX_CHAINS]; + u8 agc_gain_cur[MT_MAX_CHAINS]; + + int avg_rssi[MT_MAX_CHAINS]; + int avg_rssi_all; + + s8 agc_gain_adjust; + s8 low_gain; + + u8 temp; + + bool init_cal_done; + bool tssi_cal_done; + bool tssi_comp_pending; + bool dpd_cal_done; + bool channel_cal_done; +}; + +struct mt76x2_dev { + struct mt76_dev mt76; /* must be first */ + + struct mac_address macaddr_list[8]; + + struct mutex mutex; + + const u16 *beacon_offsets; + unsigned long wcid_mask[128 / BITS_PER_LONG]; + + int txpower_conf; + int txpower_cur; + + u8 txdone_seq; + DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x2_tx_status); + + struct mt76x2_mcu mcu; + struct sk_buff *rx_head; + + struct tasklet_struct tx_tasklet; + struct tasklet_struct pre_tbtt_tasklet; + struct delayed_work cal_work; + struct delayed_work mac_work; + + u32 aggr_stats[32]; + + struct mt76_wcid global_wcid; + struct mt76_wcid __rcu *wcid[128]; + + spinlock_t irq_lock; + u32 irqmask; + + struct sk_buff *beacons[8]; + u8 beacon_mask; + u8 beacon_data_mask; + + u32 rev; + u32 rxfilter; + + u16 chainmask; + + struct mt76x2_calibration cal; + + s8 target_power; + s8 target_power_delta[2]; + struct mt76_rate_power rate_power; + bool enable_tpc; + + u8 coverage_class; + u8 slottime; + + struct mt76x2_dfs_pattern_detector dfs_pd; +}; + +struct mt76x2_vif { + u8 idx; + + struct mt76_wcid group_wcid; +}; + +struct mt76x2_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt76x2_tx_status status; + int n_frames; +}; + +static inline bool is_mt7612(struct mt76x2_dev *dev) +{ + return (dev->rev >> 16) == 0x7612; +} + +void mt76x2_set_irq_mask(struct mt76x2_dev *dev, u32 clear, u32 set); + +static inline void mt76x2_irq_enable(struct mt76x2_dev *dev, u32 mask) +{ + mt76x2_set_irq_mask(dev, 0, mask); +} + +static inline void mt76x2_irq_disable(struct mt76x2_dev *dev, u32 mask) +{ + mt76x2_set_irq_mask(dev, mask, 0); +} + +extern const struct ieee80211_ops mt76x2_ops; + +struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev); +int mt76x2_register_device(struct mt76x2_dev *dev); +void mt76x2_init_debugfs(struct mt76x2_dev *dev); + +irqreturn_t mt76x2_irq_handler(int irq, void *dev_instance); +void mt76x2_phy_power_on(struct mt76x2_dev *dev); +int mt76x2_init_hardware(struct mt76x2_dev *dev); +void mt76x2_stop_hardware(struct mt76x2_dev *dev); +int mt76x2_eeprom_init(struct mt76x2_dev *dev); +int mt76x2_apply_calibration_data(struct mt76x2_dev *dev, int channel); +void mt76x2_set_tx_ackto(struct mt76x2_dev *dev); + +int mt76x2_phy_start(struct mt76x2_dev *dev); +int mt76x2_phy_set_channel(struct mt76x2_dev *dev, + struct cfg80211_chan_def *chandef); +int mt76x2_phy_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain); +void mt76x2_phy_calibrate(struct work_struct *work); +void mt76x2_phy_set_txpower(struct mt76x2_dev *dev); + +int mt76x2_mcu_init(struct mt76x2_dev *dev); +int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, + u8 bw_index, bool scan); +int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on); +int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, + u8 channel); +int mt76x2_mcu_cleanup(struct mt76x2_dev *dev); + +int mt76x2_dma_init(struct mt76x2_dev *dev); +void mt76x2_dma_cleanup(struct mt76x2_dev *dev); + +void mt76x2_cleanup(struct mt76x2_dev *dev); + +int mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, + struct sk_buff *skb, int cmd, int seq); +void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +void mt76x2_tx_complete(struct mt76x2_dev *dev, struct sk_buff *skb); +int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info); +void mt76x2_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush); + +void mt76x2_pre_tbtt_tasklet(unsigned long arg); + +void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb); + +void mt76x2_update_channel(struct mt76_dev *mdev); + +s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev, + const struct ieee80211_tx_rate *rate); +s8 mt76x2_tx_get_txpwr_adj(struct mt76x2_dev *dev, s8 txpwr, s8 max_txpwr_adj); +void mt76x2_tx_set_txpwr_auto(struct mt76x2_dev *dev, s8 txpwr); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_core.c b/drivers/net/wireless/mediatek/mt76/mt76x2_core.c new file mode 100644 index 000000000000..2629779e8d3e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_core.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" +#include "mt76x2_trace.h" + +void mt76x2_set_irq_mask(struct mt76x2_dev *dev, u32 clear, u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->irq_lock, flags); + dev->irqmask &= ~clear; + dev->irqmask |= set; + mt76_wr(dev, MT_INT_MASK_CSR, dev->irqmask); + spin_unlock_irqrestore(&dev->irq_lock, flags); +} + +void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + + mt76x2_irq_enable(dev, MT_INT_RX_DONE(q)); +} + +irqreturn_t mt76x2_irq_handler(int irq, void *dev_instance) +{ + struct mt76x2_dev *dev = dev_instance; + u32 intr; + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.state)) + return IRQ_NONE; + + trace_dev_irq(dev, intr, dev->irqmask); + + intr &= dev->irqmask; + + if (intr & MT_INT_TX_DONE_ALL) { + mt76x2_irq_disable(dev, MT_INT_TX_DONE_ALL); + tasklet_schedule(&dev->tx_tasklet); + } + + if (intr & MT_INT_RX_DONE(0)) { + mt76x2_irq_disable(dev, MT_INT_RX_DONE(0)); + napi_schedule(&dev->mt76.napi[0]); + } + + if (intr & MT_INT_RX_DONE(1)) { + mt76x2_irq_disable(dev, MT_INT_RX_DONE(1)); + napi_schedule(&dev->mt76.napi[1]); + } + + if (intr & MT_INT_PRE_TBTT) + tasklet_schedule(&dev->pre_tbtt_tasklet); + + /* send buffered multicast frames now */ + if (intr & MT_INT_TBTT) + mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]); + + if (intr & MT_INT_TX_STAT) { + mt76x2_mac_poll_tx_status(dev, true); + 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/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c new file mode 100644 index 000000000000..612feb593d7d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" + +static int +mt76x2_ampdu_stat_read(struct seq_file *file, void *data) +{ + struct mt76x2_dev *dev = file->private; + int i, j; + + for (i = 0; i < 4; i++) { + seq_puts(file, "Length: "); + for (j = 0; j < 8; j++) + seq_printf(file, "%8d | ", i * 8 + j + 1); + seq_puts(file, "\n"); + seq_puts(file, "Count: "); + for (j = 0; j < 8; j++) + seq_printf(file, "%8d | ", dev->aggr_stats[i * 8 + j]); + seq_puts(file, "\n"); + seq_puts(file, "--------"); + for (j = 0; j < 8; j++) + seq_puts(file, "-----------"); + seq_puts(file, "\n"); + } + + return 0; +} + +static int +mt76x2_ampdu_stat_open(struct inode *inode, struct file *f) +{ + return single_open(f, mt76x2_ampdu_stat_read, inode->i_private); +} + +static void +seq_puts_array(struct seq_file *file, const char *str, s8 *val, int len) +{ + int i; + + seq_printf(file, "%10s:", str); + for (i = 0; i < len; i++) + seq_printf(file, " %2d", val[i]); + seq_puts(file, "\n"); +} + +static int read_txpower(struct seq_file *file, void *data) +{ + struct mt76x2_dev *dev = dev_get_drvdata(file->private); + + seq_printf(file, "Target power: %d\n", dev->target_power); + + seq_puts_array(file, "Delta", dev->target_power_delta, + ARRAY_SIZE(dev->target_power_delta)); + seq_puts_array(file, "CCK", dev->rate_power.cck, + ARRAY_SIZE(dev->rate_power.cck)); + seq_puts_array(file, "OFDM", dev->rate_power.ofdm, + ARRAY_SIZE(dev->rate_power.ofdm)); + seq_puts_array(file, "HT", dev->rate_power.ht, + ARRAY_SIZE(dev->rate_power.ht)); + seq_puts_array(file, "VHT", dev->rate_power.vht, + ARRAY_SIZE(dev->rate_power.vht)); + return 0; +} + +static const struct file_operations fops_ampdu_stat = { + .open = mt76x2_ampdu_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int +mt76x2_dfs_stat_read(struct seq_file *file, void *data) +{ + int i; + struct mt76x2_dev *dev = file->private; + struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { + seq_printf(file, "engine: %d\n", i); + seq_printf(file, " hw pattern detected:\t%d\n", + dfs_pd->stats[i].hw_pattern); + seq_printf(file, " hw pulse discarded:\t%d\n", + dfs_pd->stats[i].hw_pulse_discarded); + } + + return 0; +} + +static int +mt76x2_dfs_stat_open(struct inode *inode, struct file *f) +{ + return single_open(f, mt76x2_dfs_stat_read, inode->i_private); +} + +static const struct file_operations fops_dfs_stat = { + .open = mt76x2_dfs_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void mt76x2_init_debugfs(struct mt76x2_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs(&dev->mt76); + if (!dir) + return; + + debugfs_create_u8("temperature", S_IRUSR, dir, &dev->cal.temp); + debugfs_create_bool("tpc", S_IRUSR | S_IWUSR, dir, &dev->enable_tpc); + + debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat); + debugfs_create_file("dfs_stats", S_IRUSR, dir, dev, &fops_dfs_stat); + debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir, + read_txpower); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c new file mode 100644 index 000000000000..5b452a596016 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c @@ -0,0 +1,493 @@ +/* + * 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, 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 mt76x2_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 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) + break; + + if (pulse->engine == 3) { + ret = mt76x2_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->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); + break; + } + + if (pulse->engine > 3) + break; + + if (pulse->engine == 3) { + ret = mt76x2_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 void mt76x2_dfs_tasklet(unsigned long arg) +{ + struct mt76x2_dev *dev = (struct mt76x2_dev *)arg; + struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + u32 engine_mask; + int i; + + if (test_bit(MT76_SCANNING, &dev->mt76.state)) + goto out; + + 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 mt76x2_dfs_hw_pulse pulse; + + if (!(engine_mask & (1 << i))) + continue; + + pulse.engine = i; + mt76x2_dfs_get_hw_pulse(dev, &pulse); + + if (!mt76x2_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); + + /* 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_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); + + 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; + + dfs_pd->region = NL80211_DFS_UNSET; + tasklet_init(&dfs_pd->dfs_tasklet, mt76x2_dfs_tasklet, + (unsigned long)dev); +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h b/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h new file mode 100644 index 000000000000..9ac69b6a116d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h @@ -0,0 +1,80 @@ +/* + * 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_NUM_ENGINES 4 + +/* 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_engine_stats { + u32 hw_pattern; + u32 hw_pulse_discarded; +}; + +struct mt76x2_dfs_pattern_detector { + enum nl80211_dfs_regions region; + + u8 chirp_pulse_cnt; + u32 chirp_pulse_ts; + + struct mt76x2_dfs_engine_stats stats[MT_DFS_NUM_ENGINES]; + 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/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c new file mode 100644 index 000000000000..0a3f729a7156 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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" +#include "mt76x2_dma.h" + +int +mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, + struct sk_buff *skb, int cmd, int seq) +{ + struct mt76_queue *q = &dev->mt76.q_tx[qid]; + struct mt76_queue_buf buf; + dma_addr_t addr; + u32 tx_info; + + tx_info = MT_MCU_MSG_TYPE_CMD | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | + FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | + FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_LEN, skb->len); + + addr = dma_map_single(dev->mt76.dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev->mt76.dev, addr)) + return -ENOMEM; + + buf.addr = addr; + buf.len = skb->len; + spin_lock_bh(&q->lock); + mt76_queue_add_buf(dev, q, &buf, 1, tx_info, skb, NULL); + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); + + return 0; +} + +static int +mt76x2_init_tx_queue(struct mt76x2_dev *dev, struct mt76_queue *q, + int idx, int n_desc) +{ + int ret; + + q->regs = dev->mt76.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE; + q->ndesc = n_desc; + + ret = mt76_queue_alloc(dev, q); + if (ret) + return ret; + + mt76x2_irq_enable(dev, MT_INT_TX_DONE(idx)); + + return 0; +} + +void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb) +{ + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + void *rxwi = skb->data; + + if (q == MT_RXQ_MCU) { + skb_queue_tail(&dev->mcu.res_q, skb); + wake_up(&dev->mcu.wait); + return; + } + + skb_pull(skb, sizeof(struct mt76x2_rxwi)); + if (mt76x2_mac_process_rx(dev, skb, rxwi)) { + dev_kfree_skb(skb); + return; + } + + mt76_rx(&dev->mt76, q, skb); +} + +static int +mt76x2_init_rx_queue(struct mt76x2_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize) +{ + int ret; + + q->regs = dev->mt76.regs + MT_RX_RING_BASE + idx * MT_RING_SIZE; + q->ndesc = n_desc; + q->buf_size = bufsize; + + ret = mt76_queue_alloc(dev, q); + if (ret) + return ret; + + mt76x2_irq_enable(dev, MT_INT_RX_DONE(idx)); + + return 0; +} + +static void +mt76x2_tx_tasklet(unsigned long data) +{ + struct mt76x2_dev *dev = (struct mt76x2_dev *) data; + int i; + + mt76x2_mac_process_tx_status_fifo(dev); + + for (i = MT_TXQ_MCU; i >= 0; i--) + mt76_queue_tx_cleanup(dev, i, false); + + mt76x2_mac_poll_tx_status(dev, false); + mt76x2_irq_enable(dev, MT_INT_TX_DONE_ALL); +} + +int mt76x2_dma_init(struct mt76x2_dev *dev) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + int ret; + int i; + struct mt76_txwi_cache __maybe_unused *t; + struct mt76_queue *q; + + BUILD_BUG_ON(sizeof(t->txwi) < sizeof(struct mt76x2_txwi)); + BUILD_BUG_ON(sizeof(struct mt76x2_rxwi) > MT_RX_HEADROOM); + + mt76_dma_attach(&dev->mt76); + + init_waitqueue_head(&dev->mcu.wait); + skb_queue_head_init(&dev->mcu.res_q); + + tasklet_init(&dev->tx_tasklet, mt76x2_tx_tasklet, (unsigned long) dev); + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[i], + wmm_queue_map[i], MT_TX_RING_SIZE); + if (ret) + return ret; + } + + ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_PSD], + MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE); + if (ret) + return ret; + + ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_MCU], + MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE); + if (ret) + return ret; + + ret = mt76x2_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, + MT_MCU_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + q = &dev->mt76.q_rx[MT_RXQ_MAIN]; + q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x2_rxwi); + ret = mt76x2_init_rx_queue(dev, q, 0, MT76x2_RX_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + return mt76_init_queues(dev); +} + +void mt76x2_dma_cleanup(struct mt76x2_dev *dev) +{ + tasklet_kill(&dev->tx_tasklet); + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_dma.h b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.h new file mode 100644 index 000000000000..47f79d83fcb4 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_DMA_H +#define __MT76x2_DMA_H + +#include "dma.h" + +#define MT_TXD_INFO_LEN GENMASK(13, 0) +#define MT_TXD_INFO_NEXT_VLD BIT(16) +#define MT_TXD_INFO_TX_BURST BIT(17) +#define MT_TXD_INFO_80211 BIT(19) +#define MT_TXD_INFO_TSO BIT(20) +#define MT_TXD_INFO_CSO BIT(21) +#define MT_TXD_INFO_WIV BIT(24) +#define MT_TXD_INFO_QSEL GENMASK(26, 25) +#define MT_TXD_INFO_TCO BIT(29) +#define MT_TXD_INFO_UCO BIT(30) +#define MT_TXD_INFO_ICO BIT(31) + +#define MT_RX_FCE_INFO_LEN GENMASK(13, 0) +#define MT_RX_FCE_INFO_SELF_GEN BIT(15) +#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16) +#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20) +#define MT_RX_FCE_INFO_PCIE_INTR BIT(24) +#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25) +#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27) +#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30) + +/* MCU request message header */ +#define MT_MCU_MSG_LEN GENMASK(15, 0) +#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16) +#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20) +#define MT_MCU_MSG_PORT GENMASK(29, 27) +#define MT_MCU_MSG_TYPE GENMASK(31, 30) +#define MT_MCU_MSG_TYPE_CMD BIT(30) + +enum mt76x2_qsel { + MT_QSEL_MGMT, + MT_QSEL_HCCA, + MT_QSEL_EDCA, + MT_QSEL_EDCA_2, +}; + +enum dma_msg_port { + WLAN_PORT, + CPU_RX_PORT, + CPU_TX_PORT, + HOST_PORT, + VIRTUAL_CPU_RX_PORT, + VIRTUAL_CPU_TX_PORT, + DISCARD, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c new file mode 100644 index 000000000000..440b7e7d73a9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" +#include "mt76x2_eeprom.h" + +#define EE_FIELD(_name, _value) [MT_EE_##_name] = (_value) | 1 + +static int +mt76x2_eeprom_copy(struct mt76x2_dev *dev, enum mt76x2_eeprom_field field, + void *dest, int len) +{ + if (field + len > dev->mt76.eeprom.size) + return -1; + + memcpy(dest, dev->mt76.eeprom.data + field, len); + return 0; +} + +static int +mt76x2_eeprom_get_macaddr(struct mt76x2_dev *dev) +{ + void *src = dev->mt76.eeprom.data + MT_EE_MAC_ADDR; + + memcpy(dev->mt76.macaddr, src, ETH_ALEN); + return 0; +} + +static void +mt76x2_eeprom_parse_hw_cap(struct mt76x2_dev *dev) +{ + u16 val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0); + + switch (FIELD_GET(MT_EE_NIC_CONF_0_BOARD_TYPE, val)) { + case BOARD_TYPE_5GHZ: + dev->mt76.cap.has_5ghz = true; + break; + case BOARD_TYPE_2GHZ: + dev->mt76.cap.has_2ghz = true; + break; + default: + dev->mt76.cap.has_2ghz = true; + dev->mt76.cap.has_5ghz = true; + break; + } +} + +static int +mt76x2_efuse_read(struct mt76x2_dev *dev, u16 addr, u8 *data) +{ + u32 val; + int i; + + val = mt76_rr(dev, MT_EFUSE_CTRL); + val &= ~(MT_EFUSE_CTRL_AIN | + MT_EFUSE_CTRL_MODE); + val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf); + val |= MT_EFUSE_CTRL_KICK; + mt76_wr(dev, MT_EFUSE_CTRL, val); + + if (!mt76_poll(dev, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) + return -ETIMEDOUT; + + udelay(2); + + val = mt76_rr(dev, MT_EFUSE_CTRL); + if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) { + memset(data, 0xff, 16); + return 0; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, MT_EFUSE_DATA(i)); + put_unaligned_le32(val, data + 4 * i); + } + + return 0; +} + +static int +mt76x2_get_efuse_data(struct mt76x2_dev *dev, void *buf, int len) +{ + int ret, i; + + for (i = 0; i + 16 <= len; i += 16) { + ret = mt76x2_efuse_read(dev, i, buf + i); + if (ret) + return ret; + } + + return 0; +} + +static bool +mt76x2_has_cal_free_data(struct mt76x2_dev *dev, u8 *efuse) +{ + u16 *efuse_w = (u16 *) efuse; + + if (efuse_w[MT_EE_NIC_CONF_0] != 0) + return false; + + if (efuse_w[MT_EE_XTAL_TRIM_1] == 0xffff) + return false; + + if (efuse_w[MT_EE_TX_POWER_DELTA_BW40] != 0) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_START_2G] == 0xffff) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA] != 0) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE] == 0xffff) + return false; + + return true; +} + +static void +mt76x2_apply_cal_free_data(struct mt76x2_dev *dev, u8 *efuse) +{ +#define GROUP_5G(_id) \ + MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \ + MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1, \ + MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \ + MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1 + + static const u8 cal_free_bytes[] = { + MT_EE_XTAL_TRIM_1, + MT_EE_TX_POWER_EXT_PA_5G + 1, + MT_EE_TX_POWER_0_START_2G, + MT_EE_TX_POWER_0_START_2G + 1, + MT_EE_TX_POWER_1_START_2G, + MT_EE_TX_POWER_1_START_2G + 1, + GROUP_5G(0), + GROUP_5G(1), + GROUP_5G(2), + GROUP_5G(3), + GROUP_5G(4), + GROUP_5G(5), + MT_EE_RF_2G_TSSI_OFF_TXPOWER, + MT_EE_RF_2G_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN + 1, + }; + u8 *eeprom = dev->mt76.eeprom.data; + u8 prev_grp0[4] = { + eeprom[MT_EE_TX_POWER_0_START_5G], + eeprom[MT_EE_TX_POWER_0_START_5G + 1], + eeprom[MT_EE_TX_POWER_1_START_5G], + eeprom[MT_EE_TX_POWER_1_START_5G + 1] + }; + u16 val; + int i; + + if (!mt76x2_has_cal_free_data(dev, efuse)) + return; + + for (i = 0; i < ARRAY_SIZE(cal_free_bytes); i++) { + int offset = cal_free_bytes[i]; + + eeprom[offset] = efuse[offset]; + } + + if (!(efuse[MT_EE_TX_POWER_0_START_5G] | + efuse[MT_EE_TX_POWER_0_START_5G + 1])) + memcpy(eeprom + MT_EE_TX_POWER_0_START_5G, prev_grp0, 2); + if (!(efuse[MT_EE_TX_POWER_1_START_5G] | + efuse[MT_EE_TX_POWER_1_START_5G + 1])) + memcpy(eeprom + MT_EE_TX_POWER_1_START_5G, prev_grp0 + 2, 2); + + val = get_unaligned_le16(efuse + MT_EE_BT_RCAL_RESULT); + if (val != 0xffff) + eeprom[MT_EE_BT_RCAL_RESULT] = val & 0xff; + + val = get_unaligned_le16(efuse + MT_EE_BT_VCDL_CALIBRATION); + if (val != 0xffff) + eeprom[MT_EE_BT_VCDL_CALIBRATION + 1] = val >> 8; + + val = get_unaligned_le16(efuse + MT_EE_BT_PMUCFG); + if (val != 0xffff) + eeprom[MT_EE_BT_PMUCFG] = val & 0xff; +} + +static int mt76x2_check_eeprom(struct mt76x2_dev *dev) +{ + u16 val = get_unaligned_le16(dev->mt76.eeprom.data); + + if (!val) + val = get_unaligned_le16(dev->mt76.eeprom.data + MT_EE_PCI_ID); + + switch (val) { + case 0x7662: + case 0x7612: + return 0; + default: + dev_err(dev->mt76.dev, "EEPROM data check failed: %04x\n", val); + return -EINVAL; + } +} + +static int +mt76x2_eeprom_load(struct mt76x2_dev *dev) +{ + void *efuse; + int len = MT7662_EEPROM_SIZE; + bool found; + int ret; + + ret = mt76_eeprom_init(&dev->mt76, len); + if (ret < 0) + return ret; + + found = ret; + if (found) + found = !mt76x2_check_eeprom(dev); + + dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL); + dev->mt76.otp.size = len; + if (!dev->mt76.otp.data) + return -ENOMEM; + + efuse = dev->mt76.otp.data; + + if (mt76x2_get_efuse_data(dev, efuse, len)) + goto out; + + if (found) { + mt76x2_apply_cal_free_data(dev, efuse); + } else { + /* FIXME: check if efuse data is complete */ + found = true; + memcpy(dev->mt76.eeprom.data, efuse, len); + } + +out: + if (!found) + return -ENOENT; + + return 0; +} + +static inline int +mt76x2_sign_extend(u32 val, unsigned int size) +{ + bool sign = val & BIT(size - 1); + + val &= BIT(size - 1) - 1; + + return sign ? val : -val; +} + +static inline int +mt76x2_sign_extend_optional(u32 val, unsigned int size) +{ + bool enable = val & BIT(size); + + return enable ? mt76x2_sign_extend(val, size) : 0; +} + +static bool +field_valid(u8 val) +{ + return val != 0 && val != 0xff; +} + +static void +mt76x2_set_rx_gain_group(struct mt76x2_dev *dev, u8 val) +{ + s8 *dest = dev->cal.rx.high_gain; + + if (!field_valid(val)) { + dest[0] = 0; + dest[1] = 0; + return; + } + + dest[0] = mt76x2_sign_extend(val, 4); + dest[1] = mt76x2_sign_extend(val >> 4, 4); +} + +static void +mt76x2_set_rssi_offset(struct mt76x2_dev *dev, int chain, u8 val) +{ + s8 *dest = dev->cal.rx.rssi_offset; + + if (!field_valid(val)) { + dest[chain] = 0; + return; + } + + dest[chain] = mt76x2_sign_extend_optional(val, 7); +} + +static enum mt76x2_cal_channel_group +mt76x2_get_cal_channel_group(int channel) +{ + if (channel >= 184 && channel <= 196) + return MT_CH_5G_JAPAN; + if (channel <= 48) + return MT_CH_5G_UNII_1; + if (channel <= 64) + return MT_CH_5G_UNII_2; + if (channel <= 114) + return MT_CH_5G_UNII_2E_1; + if (channel <= 144) + return MT_CH_5G_UNII_2E_2; + return MT_CH_5G_UNII_3; +} + +static u8 +mt76x2_get_5g_rx_gain(struct mt76x2_dev *dev, u8 channel) +{ + enum mt76x2_cal_channel_group group; + + group = mt76x2_get_cal_channel_group(channel); + switch (group) { + case MT_CH_5G_JAPAN: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN); + case MT_CH_5G_UNII_1: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN) >> 8; + case MT_CH_5G_UNII_2: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN); + case MT_CH_5G_UNII_2E_1: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN) >> 8; + case MT_CH_5G_UNII_2E_2: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN); + default: + return mt76x2_eeprom_get(dev, MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN) >> 8; + } +} + +void mt76x2_read_rx_gain(struct mt76x2_dev *dev) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + int channel = chan->hw_value; + s8 lna_5g[3], lna_2g; + u8 lna; + u16 val; + + if (chan->band == NL80211_BAND_2GHZ) + val = mt76x2_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN) >> 8; + else + val = mt76x2_get_5g_rx_gain(dev, channel); + + mt76x2_set_rx_gain_group(dev, val); + + if (chan->band == NL80211_BAND_2GHZ) { + val = mt76x2_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_0); + mt76x2_set_rssi_offset(dev, 0, val); + mt76x2_set_rssi_offset(dev, 1, val >> 8); + } else { + val = mt76x2_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_0); + mt76x2_set_rssi_offset(dev, 0, val); + mt76x2_set_rssi_offset(dev, 1, val >> 8); + } + + val = mt76x2_eeprom_get(dev, MT_EE_LNA_GAIN); + lna_2g = val & 0xff; + lna_5g[0] = val >> 8; + + val = mt76x2_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_1); + lna_5g[1] = val >> 8; + + val = mt76x2_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_1); + lna_5g[2] = val >> 8; + + if (!field_valid(lna_5g[1])) + lna_5g[1] = lna_5g[0]; + + if (!field_valid(lna_5g[2])) + lna_5g[2] = lna_5g[0]; + + dev->cal.rx.mcu_gain = (lna_2g & 0xff); + dev->cal.rx.mcu_gain |= (lna_5g[0] & 0xff) << 8; + dev->cal.rx.mcu_gain |= (lna_5g[1] & 0xff) << 16; + dev->cal.rx.mcu_gain |= (lna_5g[2] & 0xff) << 24; + + val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1); + if (val & MT_EE_NIC_CONF_1_LNA_EXT_2G) + lna_2g = 0; + if (val & MT_EE_NIC_CONF_1_LNA_EXT_5G) + memset(lna_5g, 0, sizeof(lna_5g)); + + if (chan->band == NL80211_BAND_2GHZ) + lna = lna_2g; + else if (channel <= 64) + lna = lna_5g[0]; + else if (channel <= 128) + lna = lna_5g[1]; + else + lna = lna_5g[2]; + + if (lna == 0xff) + lna = 0; + + dev->cal.rx.lna_gain = mt76x2_sign_extend(lna, 8); +} + +static s8 +mt76x2_rate_power_val(u8 val) +{ + if (!field_valid(val)) + return 0; + + return mt76x2_sign_extend_optional(val, 7); +} + +void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t) +{ + bool is_5ghz; + u16 val; + + is_5ghz = dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ; + + memset(t, 0, sizeof(*t)); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_CCK); + t->cck[0] = t->cck[1] = mt76x2_rate_power_val(val); + t->cck[2] = t->cck[3] = mt76x2_rate_power_val(val >> 8); + + if (is_5ghz) + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_6M); + else + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_6M); + t->ofdm[0] = t->ofdm[1] = mt76x2_rate_power_val(val); + t->ofdm[2] = t->ofdm[3] = mt76x2_rate_power_val(val >> 8); + + if (is_5ghz) + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_24M); + else + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_24M); + t->ofdm[4] = t->ofdm[5] = mt76x2_rate_power_val(val); + t->ofdm[6] = t->ofdm[7] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS0); + t->ht[0] = t->ht[1] = mt76x2_rate_power_val(val); + t->ht[2] = t->ht[3] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS4); + t->ht[4] = t->ht[5] = mt76x2_rate_power_val(val); + t->ht[6] = t->ht[7] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS8); + t->ht[8] = t->ht[9] = mt76x2_rate_power_val(val); + t->ht[10] = t->ht[11] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS12); + t->ht[12] = t->ht[13] = mt76x2_rate_power_val(val); + t->ht[14] = t->ht[15] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS0); + t->vht[0] = t->vht[1] = mt76x2_rate_power_val(val); + t->vht[2] = t->vht[3] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS4); + t->vht[4] = t->vht[5] = mt76x2_rate_power_val(val); + t->vht[6] = t->vht[7] = mt76x2_rate_power_val(val >> 8); + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8); + if (!is_5ghz) + val >>= 8; + t->vht[8] = t->vht[9] = mt76x2_rate_power_val(val >> 8); +} + +static void +mt76x2_get_power_info_2g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t, + int chain, int offset) +{ + int channel = dev->mt76.chandef.chan->hw_value; + int delta_idx; + u8 data[6]; + u16 val; + + if (channel < 6) + delta_idx = 3; + else if (channel < 11) + delta_idx = 4; + else + delta_idx = 5; + + mt76x2_eeprom_copy(dev, offset, data, sizeof(data)); + + t->chain[chain].tssi_slope = data[0]; + t->chain[chain].tssi_offset = data[1]; + t->chain[chain].target_power = data[2]; + t->chain[chain].delta = mt76x2_sign_extend_optional(data[delta_idx], 7); + + val = mt76x2_eeprom_get(dev, MT_EE_RF_2G_TSSI_OFF_TXPOWER); + t->target_power = val >> 8; +} + +static void +mt76x2_get_power_info_5g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t, + int chain, int offset) +{ + int channel = dev->mt76.chandef.chan->hw_value; + enum mt76x2_cal_channel_group group; + int delta_idx; + u16 val; + u8 data[5]; + + group = mt76x2_get_cal_channel_group(channel); + offset += group * MT_TX_POWER_GROUP_SIZE_5G; + + if (channel >= 192) + delta_idx = 4; + else if (channel >= 484) + delta_idx = 3; + else if (channel < 44) + delta_idx = 3; + else if (channel < 52) + delta_idx = 4; + else if (channel < 58) + delta_idx = 3; + else if (channel < 98) + delta_idx = 4; + else if (channel < 106) + delta_idx = 3; + else if (channel < 116) + delta_idx = 4; + else if (channel < 130) + delta_idx = 3; + else if (channel < 149) + delta_idx = 4; + else if (channel < 157) + delta_idx = 3; + else + delta_idx = 4; + + mt76x2_eeprom_copy(dev, offset, data, sizeof(data)); + + t->chain[chain].tssi_slope = data[0]; + t->chain[chain].tssi_offset = data[1]; + t->chain[chain].target_power = data[2]; + t->chain[chain].delta = mt76x2_sign_extend_optional(data[delta_idx], 7); + + val = mt76x2_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN); + t->target_power = val & 0xff; +} + +void mt76x2_get_power_info(struct mt76x2_dev *dev, + struct mt76x2_tx_power_info *t) +{ + u16 bw40, bw80; + + memset(t, 0, sizeof(*t)); + + bw40 = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40); + bw80 = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80); + + if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) { + bw40 >>= 8; + mt76x2_get_power_info_5g(dev, t, 0, MT_EE_TX_POWER_0_START_5G); + mt76x2_get_power_info_5g(dev, t, 1, MT_EE_TX_POWER_1_START_5G); + } else { + mt76x2_get_power_info_2g(dev, t, 0, MT_EE_TX_POWER_0_START_2G); + mt76x2_get_power_info_2g(dev, t, 1, MT_EE_TX_POWER_1_START_2G); + } + + if (mt76x2_tssi_enabled(dev) || !field_valid(t->target_power)) + t->target_power = t->chain[0].target_power; + + t->delta_bw40 = mt76x2_rate_power_val(bw40); + t->delta_bw80 = mt76x2_rate_power_val(bw80); +} + +int mt76x2_get_temp_comp(struct mt76x2_dev *dev, struct mt76x2_temp_comp *t) +{ + enum nl80211_band band = dev->mt76.chandef.chan->band; + u16 val, slope; + u8 bounds; + + memset(t, 0, sizeof(*t)); + + val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1); + if (!(val & MT_EE_NIC_CONF_1_TEMP_TX_ALC)) + return -EINVAL; + + if (!mt76x2_ext_pa_enabled(dev, band)) + return -EINVAL; + + val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G) >> 8; + if (!(val & BIT(7))) + return -EINVAL; + + t->temp_25_ref = val & 0x7f; + if (band == NL80211_BAND_5GHZ) { + slope = mt76x2_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_5G); + bounds = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G); + } else { + slope = mt76x2_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_2G); + bounds = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80) >> 8; + } + + t->high_slope = slope & 0xff; + t->low_slope = slope >> 8; + t->lower_bound = 0 - (bounds & 0xf); + t->upper_bound = (bounds >> 4) & 0xf; + + return 0; +} + +bool mt76x2_ext_pa_enabled(struct mt76x2_dev *dev, enum nl80211_band band) +{ + u16 conf0 = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0); + + if (band == NL80211_BAND_5GHZ) + return !(conf0 & MT_EE_NIC_CONF_0_PA_INT_5G); + else + return !(conf0 & MT_EE_NIC_CONF_0_PA_INT_2G); +} + +int mt76x2_eeprom_init(struct mt76x2_dev *dev) +{ + int ret; + + ret = mt76x2_eeprom_load(dev); + if (ret) + return ret; + + mt76x2_eeprom_parse_hw_cap(dev); + mt76x2_eeprom_get_macaddr(dev); + mt76_eeprom_override(&dev->mt76); + dev->mt76.macaddr[0] &= ~BIT(1); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h new file mode 100644 index 000000000000..063d6c8451c9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_EEPROM_H +#define __MT76x2_EEPROM_H + +#include "mt76x2.h" + +enum mt76x2_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_PCI_ID = 0x00A, + MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_NIC_CONF_2 = 0x042, + + MT_EE_XTAL_TRIM_1 = 0x03a, + MT_EE_XTAL_TRIM_2 = 0x09e, + + MT_EE_LNA_GAIN = 0x044, + MT_EE_RSSI_OFFSET_2G_0 = 0x046, + MT_EE_RSSI_OFFSET_2G_1 = 0x048, + MT_EE_RSSI_OFFSET_5G_0 = 0x04a, + MT_EE_RSSI_OFFSET_5G_1 = 0x04c, + + MT_EE_TX_POWER_DELTA_BW40 = 0x050, + MT_EE_TX_POWER_DELTA_BW80 = 0x052, + + MT_EE_TX_POWER_EXT_PA_5G = 0x054, + + MT_EE_TX_POWER_0_START_2G = 0x056, + MT_EE_TX_POWER_1_START_2G = 0x05c, + + /* used as byte arrays */ +#define MT_TX_POWER_GROUP_SIZE_5G 5 +#define MT_TX_POWER_GROUPS_5G 6 + MT_EE_TX_POWER_0_START_5G = 0x062, + + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074, + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076, + + MT_EE_TX_POWER_1_START_5G = 0x080, + + MT_EE_TX_POWER_CCK = 0x0a0, + MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2, + MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4, + MT_EE_TX_POWER_OFDM_5G_6M = 0x0b2, + MT_EE_TX_POWER_OFDM_5G_24M = 0x0b4, + MT_EE_TX_POWER_HT_MCS0 = 0x0a6, + MT_EE_TX_POWER_HT_MCS4 = 0x0a8, + MT_EE_TX_POWER_HT_MCS8 = 0x0aa, + MT_EE_TX_POWER_HT_MCS12 = 0x0ac, + MT_EE_TX_POWER_VHT_MCS0 = 0x0ba, + MT_EE_TX_POWER_VHT_MCS4 = 0x0bc, + MT_EE_TX_POWER_VHT_MCS8 = 0x0be, + + MT_EE_RF_TEMP_COMP_SLOPE_5G = 0x0f2, + MT_EE_RF_TEMP_COMP_SLOPE_2G = 0x0f4, + + MT_EE_RF_2G_TSSI_OFF_TXPOWER = 0x0f6, + MT_EE_RF_2G_RX_HIGH_GAIN = 0x0f8, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN = 0x0fa, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN = 0x0fc, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN = 0x0fe, + + MT_EE_BT_RCAL_RESULT = 0x138, + MT_EE_BT_VCDL_CALIBRATION = 0x13c, + MT_EE_BT_PMUCFG = 0x13e, + + __MT_EE_MAX +}; + +#define MT_EE_NIC_CONF_0_PA_INT_2G BIT(8) +#define MT_EE_NIC_CONF_0_PA_INT_5G BIT(9) +#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12) + +#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1) +#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2) +#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3) +#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13) + +#define MT_EE_NIC_CONF_2_RX_STREAM GENMASK(3, 0) +#define MT_EE_NIC_CONF_2_TX_STREAM GENMASK(7, 4) +#define MT_EE_NIC_CONF_2_HW_ANTDIV BIT(8) +#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9) +#define MT_EE_NIC_CONF_2_TEMP_DISABLE BIT(11) +#define MT_EE_NIC_CONF_2_COEX_METHOD GENMASK(15, 13) + +enum mt76x2_board_type { + BOARD_TYPE_2GHZ = 1, + BOARD_TYPE_5GHZ = 2, +}; + +enum mt76x2_cal_channel_group { + MT_CH_5G_JAPAN, + MT_CH_5G_UNII_1, + MT_CH_5G_UNII_2, + MT_CH_5G_UNII_2E_1, + MT_CH_5G_UNII_2E_2, + MT_CH_5G_UNII_3, + __MT_CH_MAX +}; + +struct mt76x2_tx_power_info { + u8 target_power; + + s8 delta_bw40; + s8 delta_bw80; + + struct { + s8 tssi_slope; + s8 tssi_offset; + s8 target_power; + s8 delta; + } chain[MT_MAX_CHAINS]; +}; + +struct mt76x2_temp_comp { + u8 temp_25_ref; + int lower_bound; /* J */ + int upper_bound; /* J */ + unsigned int high_slope; /* J / dB */ + unsigned int low_slope; /* J / dB */ +}; + +static inline int +mt76x2_eeprom_get(struct mt76x2_dev *dev, enum mt76x2_eeprom_field field) +{ + if ((field & 1) || field >= __MT_EE_MAX) + return -1; + + return get_unaligned_le16(dev->mt76.eeprom.data + field); +} + +void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t); +void mt76x2_get_power_info(struct mt76x2_dev *dev, + struct mt76x2_tx_power_info *t); +int mt76x2_get_temp_comp(struct mt76x2_dev *dev, struct mt76x2_temp_comp *t); +bool mt76x2_ext_pa_enabled(struct mt76x2_dev *dev, enum nl80211_band band); +void mt76x2_read_rx_gain(struct mt76x2_dev *dev); + +static inline bool +mt76x2_temp_tx_alc_enabled(struct mt76x2_dev *dev) +{ + return mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) & + MT_EE_NIC_CONF_1_TEMP_TX_ALC; +} + +static inline bool +mt76x2_tssi_enabled(struct mt76x2_dev *dev) +{ + return !mt76x2_temp_tx_alc_enabled(dev) && + (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) & + MT_EE_NIC_CONF_1_TX_ALC_EN); +} + +static inline bool +mt76x2_has_ext_lna(struct mt76x2_dev *dev) +{ + u32 val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1); + + if (dev->mt76.chandef.chan->band == NL80211_BAND_2GHZ) + return val & MT_EE_NIC_CONF_1_LNA_EXT_2G; + else + return val & MT_EE_NIC_CONF_1_LNA_EXT_5G; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c new file mode 100644 index 000000000000..d3f03a8aee90 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" +#include "mt76x2_eeprom.h" +#include "mt76x2_mcu.h" + +struct mt76x2_reg_pair { + u32 reg; + u32 value; +}; + +static bool +mt76x2_wait_for_mac(struct mt76x2_dev *dev) +{ + int i; + + for (i = 0; i < 500; i++) { + switch (mt76_rr(dev, MT_MAC_CSR0)) { + case 0: + case ~0: + break; + default: + return true; + } + usleep_range(5000, 10000); + } + + return false; +} + +static bool +wait_for_wpdma(struct mt76x2_dev *dev) +{ + return mt76_poll(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, + 0, 1000); +} + +static void +mt76x2_mac_pbf_init(struct mt76x2_dev *dev) +{ + u32 val; + + val = MT_PBF_SYS_CTRL_MCU_RESET | + MT_PBF_SYS_CTRL_DMA_RESET | + MT_PBF_SYS_CTRL_MAC_RESET | + MT_PBF_SYS_CTRL_PBF_RESET | + MT_PBF_SYS_CTRL_ASY_RESET; + + mt76_set(dev, MT_PBF_SYS_CTRL, val); + mt76_clear(dev, MT_PBF_SYS_CTRL, val); + + mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f); + mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf); +} + +static void +mt76x2_write_reg_pairs(struct mt76x2_dev *dev, + const struct mt76x2_reg_pair *data, int len) +{ + while (len > 0) { + mt76_wr(dev, data->reg, data->value); + len--; + data++; + } +} + +static void +mt76_write_mac_initvals(struct mt76x2_dev *dev) +{ +#define DEFAULT_PROT_CFG \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f) | \ + MT_PROT_CFG_RTS_THRESH) + +#define DEFAULT_PROT_CFG_20 \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \ + FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x17)) + +#define DEFAULT_PROT_CFG_40 \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2084) | \ + FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f)) + + static const struct mt76x2_reg_pair vals[] = { + /* Copied from MediaTek reference source */ + { MT_PBF_SYS_CTRL, 0x00080c00 }, + { MT_PBF_CFG, 0x1efebcff }, + { MT_FCE_PSE_CTRL, 0x00000001 }, + { MT_MAC_SYS_CTRL, 0x0000000c }, + { MT_MAX_LEN_CFG, 0x003e3f00 }, + { MT_AMPDU_MAX_LEN_20M1S, 0xaaa99887 }, + { MT_AMPDU_MAX_LEN_20M2S, 0x000000aa }, + { MT_XIFS_TIME_CFG, 0x33a40d0a }, + { MT_BKOFF_SLOT_CFG, 0x00000209 }, + { MT_TBTT_SYNC_CFG, 0x00422010 }, + { MT_PWR_PIN_CFG, 0x00000000 }, + { 0x1238, 0x001700c8 }, + { MT_TX_SW_CFG0, 0x00101001 }, + { MT_TX_SW_CFG1, 0x00010000 }, + { MT_TX_SW_CFG2, 0x00000000 }, + { MT_TXOP_CTRL_CFG, 0x0400583f }, + { MT_TX_RTS_CFG, 0x00100020 }, + { MT_TX_TIMEOUT_CFG, 0x000a2290 }, + { MT_TX_RETRY_CFG, 0x47f01f0f }, + { MT_EXP_ACK_TIME, 0x002c00dc }, + { MT_TX_PROT_CFG6, 0xe3f42004 }, + { MT_TX_PROT_CFG7, 0xe3f42084 }, + { MT_TX_PROT_CFG8, 0xe3f42104 }, + { MT_PIFS_TX_CFG, 0x00060fff }, + { MT_RX_FILTR_CFG, 0x00015f97 }, + { MT_LEGACY_BASIC_RATE, 0x0000017f }, + { MT_HT_BASIC_RATE, 0x00004003 }, + { MT_PN_PAD_MODE, 0x00000002 }, + { MT_TXOP_HLDR_ET, 0x00000002 }, + { 0xa44, 0x00000000 }, + { MT_HEADER_TRANS_CTRL_REG, 0x00000000 }, + { MT_TSO_CTRL, 0x00000000 }, + { MT_AUX_CLK_CFG, 0x00000000 }, + { MT_DACCLK_EN_DLY_CFG, 0x00000000 }, + { MT_TX_ALC_CFG_4, 0x00000000 }, + { MT_TX_ALC_VGA3, 0x00000000 }, + { MT_TX_PWR_CFG_0, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_1, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_2, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_3, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_4, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_7, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_8, 0x0000003a }, + { MT_TX_PWR_CFG_9, 0x0000003a }, + { MT_EFUSE_CTRL, 0x0000d000 }, + { MT_PAUSE_ENABLE_CONTROL1, 0x0000000a }, + { MT_FCE_WLAN_FLOW_CONTROL1, 0x60401c18 }, + { MT_WPDMA_DELAY_INT_CFG, 0x94ff0000 }, + { MT_TX_SW_CFG3, 0x00000004 }, + { MT_HT_FBK_TO_LEGACY, 0x00001818 }, + { MT_VHT_HT_FBK_CFG1, 0xedcba980 }, + { MT_PROT_AUTO_TX_CFG, 0x00830083 }, + { MT_HT_CTRL_CFG, 0x000001ff }, + }; + struct mt76x2_reg_pair prot_vals[] = { + { MT_CCK_PROT_CFG, DEFAULT_PROT_CFG }, + { MT_OFDM_PROT_CFG, DEFAULT_PROT_CFG }, + { MT_MM20_PROT_CFG, DEFAULT_PROT_CFG_20 }, + { MT_MM40_PROT_CFG, DEFAULT_PROT_CFG_40 }, + { MT_GF20_PROT_CFG, DEFAULT_PROT_CFG_20 }, + { MT_GF40_PROT_CFG, DEFAULT_PROT_CFG_40 }, + }; + + mt76x2_write_reg_pairs(dev, vals, ARRAY_SIZE(vals)); + mt76x2_write_reg_pairs(dev, prot_vals, ARRAY_SIZE(prot_vals)); +} + +static void +mt76x2_fixup_xtal(struct mt76x2_dev *dev) +{ + u16 eep_val; + s8 offset = 0; + + eep_val = mt76x2_eeprom_get(dev, MT_EE_XTAL_TRIM_2); + + offset = eep_val & 0x7f; + if ((eep_val & 0xff) == 0xff) + offset = 0; + else if (eep_val & 0x80) + offset = 0 - offset; + + eep_val >>= 8; + if (eep_val == 0x00 || eep_val == 0xff) { + eep_val = mt76x2_eeprom_get(dev, MT_EE_XTAL_TRIM_1); + eep_val &= 0xff; + + if (eep_val == 0x00 || eep_val == 0xff) + eep_val = 0x14; + } + + eep_val &= 0x7f; + mt76_rmw_field(dev, MT_XO_CTRL5, MT_XO_CTRL5_C2_VAL, eep_val + offset); + mt76_set(dev, MT_XO_CTRL6, MT_XO_CTRL6_C2_CTRL); + + eep_val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_2); + switch (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) { + case 0: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80); + break; + case 1: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0); + break; + default: + break; + } +} + +static void +mt76x2_init_beacon_offsets(struct mt76x2_dev *dev) +{ + u16 base = MT_BEACON_BASE; + u32 regs[4] = {}; + int i; + + for (i = 0; i < 16; i++) { + u16 addr = dev->beacon_offsets[i]; + + regs[i / 4] |= ((addr - base) / 64) << (8 * (i % 4)); + } + + for (i = 0; i < 4; i++) + mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]); +} + +int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard) +{ + static const u8 null_addr[ETH_ALEN] = {}; + const u8 *macaddr = dev->mt76.macaddr; + u32 val; + int i, k; + + if (!mt76x2_wait_for_mac(dev)) + return -ETIMEDOUT; + + val = mt76_rr(dev, MT_WPDMA_GLO_CFG); + + val &= ~(MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY | + MT_WPDMA_GLO_CFG_DMA_BURST_SIZE); + val |= FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3); + + mt76_wr(dev, MT_WPDMA_GLO_CFG, val); + + mt76x2_mac_pbf_init(dev); + mt76_write_mac_initvals(dev); + mt76x2_fixup_xtal(dev); + + mt76_clear(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP); + + if (is_mt7612(dev)) + mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN); + + mt76_set(dev, MT_EXT_CCA_CFG, 0x0000f000); + mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31)); + + mt76_wr(dev, MT_RF_BYPASS_0, 0x06000000); + mt76_wr(dev, MT_RF_SETTING_0, 0x08800000); + usleep_range(5000, 10000); + mt76_wr(dev, MT_RF_BYPASS_0, 0x00000000); + + mt76_wr(dev, MT_MCU_CLOCK_CTL, 0x1401); + mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN); + + mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(macaddr)); + mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(macaddr + 4)); + + mt76_wr(dev, MT_MAC_BSSID_DW0, get_unaligned_le32(macaddr)); + mt76_wr(dev, MT_MAC_BSSID_DW1, get_unaligned_le16(macaddr + 4) | + FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */ + MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT); + + /* 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); + if (!hard) + return 0; + + for (i = 0; i < 256 / 32; i++) + mt76_wr(dev, MT_WCID_DROP_BASE + i * 4, 0); + + for (i = 0; i < 256; i++) + mt76x2_mac_wcid_setup(dev, i, 0, NULL); + + for (i = 0; i < 16; i++) + for (k = 0; k < 4; k++) + mt76x2_mac_shared_key_setup(dev, i, k, NULL); + + for (i = 0; i < 8; i++) { + mt76x2_mac_set_bssid(dev, i, null_addr); + mt76x2_mac_set_beacon(dev, i, NULL); + } + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_STAT_FIFO); + + mt76_set(dev, MT_MAC_APC_BSSID_H(0), MT_MAC_APC_BSSID0_H_EN); + + mt76_wr(dev, MT_CH_TIME_CFG, + MT_CH_TIME_CFG_TIMER_EN | + MT_CH_TIME_CFG_TX_AS_BUSY | + MT_CH_TIME_CFG_RX_AS_BUSY | + MT_CH_TIME_CFG_NAV_AS_BUSY | + MT_CH_TIME_CFG_EIFS_AS_BUSY | + FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1)); + + mt76x2_init_beacon_offsets(dev); + + mt76x2_set_tx_ackto(dev); + + return 0; +} + +int mt76x2_mac_start(struct mt76x2_dev *dev) +{ + int i; + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_AGG_CNT(i)); + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_STAT_FIFO); + + memset(dev->aggr_stats, 0, sizeof(dev->aggr_stats)); + + mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + wait_for_wpdma(dev); + usleep_range(50, 100); + + mt76_set(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN); + + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); + + mt76x2_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL | + MT_INT_TX_STAT); + + return 0; +} + +void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force) +{ + bool stopped = false; + u32 rts_cfg; + int i; + + mt76_wr(dev, MT_MAC_SYS_CTRL, 0); + + rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG); + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT); + + /* Wait for MAC to become idle */ + for (i = 0; i < 300; i++) { + if (mt76_rr(dev, MT_MAC_STATUS) & + (MT_MAC_STATUS_RX | MT_MAC_STATUS_TX)) + continue; + + if (mt76_rr(dev, MT_BBP(IBI, 12))) + continue; + + stopped = true; + break; + } + + if (force && !stopped) { + mt76_set(dev, MT_BBP(CORE, 4), BIT(1)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(1)); + + mt76_set(dev, MT_BBP(CORE, 4), BIT(0)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(0)); + } + + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg); +} + +void mt76x2_mac_resume(struct mt76x2_dev *dev) +{ + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); +} + +static void +mt76x2_power_on_rf_patch(struct mt76x2_dev *dev) +{ + mt76_set(dev, 0x10130, BIT(0) | BIT(16)); + udelay(1); + + mt76_clear(dev, 0x1001c, 0xff); + mt76_set(dev, 0x1001c, 0x30); + + mt76_wr(dev, 0x10014, 0x484f); + udelay(1); + + mt76_set(dev, 0x10130, BIT(17)); + udelay(125); + + mt76_clear(dev, 0x10130, BIT(16)); + udelay(50); + + mt76_set(dev, 0x1014c, BIT(19) | BIT(20)); +} + +static void +mt76x2_power_on_rf(struct mt76x2_dev *dev, int unit) +{ + int shift = unit ? 8 : 0; + + /* Enable RF BG */ + mt76_set(dev, 0x10130, BIT(0) << shift); + udelay(10); + + /* Enable RFDIG LDO/AFE/ABB/ADDA */ + mt76_set(dev, 0x10130, (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift); + udelay(10); + + /* Switch RFDIG power to internal LDO */ + mt76_clear(dev, 0x10130, BIT(2) << shift); + udelay(10); + + mt76x2_power_on_rf_patch(dev); + + mt76_set(dev, 0x530, 0xf); +} + +static void +mt76x2_power_on(struct mt76x2_dev *dev) +{ + u32 val; + + /* Turn on WL MTCMOS */ + mt76_set(dev, MT_WLAN_MTC_CTRL, MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP); + + val = MT_WLAN_MTC_CTRL_STATE_UP | + MT_WLAN_MTC_CTRL_PWR_ACK | + MT_WLAN_MTC_CTRL_PWR_ACK_S; + + mt76_poll(dev, MT_WLAN_MTC_CTRL, val, val, 1000); + + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0x7f << 16); + udelay(10); + + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xf << 24); + udelay(10); + + mt76_set(dev, MT_WLAN_MTC_CTRL, 0xf << 24); + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xfff); + + /* Turn on AD/DA power down */ + mt76_clear(dev, 0x11204, BIT(3)); + + /* WLAN function enable */ + mt76_set(dev, 0x10080, BIT(0)); + + /* Release BBP software reset */ + mt76_clear(dev, 0x10064, BIT(18)); + + mt76x2_power_on_rf(dev, 0); + mt76x2_power_on_rf(dev, 1); +} + +void mt76x2_set_tx_ackto(struct mt76x2_dev *dev) +{ + u8 ackto, sifs, slottime = dev->slottime; + + slottime += 3 * dev->coverage_class; + + sifs = mt76_get_field(dev, MT_XIFS_TIME_CFG, + MT_XIFS_TIME_CFG_OFDM_SIFS); + + ackto = slottime + sifs; + mt76_rmw_field(dev, MT_TX_TIMEOUT_CFG, + MT_TX_TIMEOUT_CFG_ACKTO, ackto); +} + +static void +mt76x2_set_wlan_state(struct mt76x2_dev *dev, bool enable) +{ + u32 val = mt76_rr(dev, MT_WLAN_FUN_CTRL); + + if (enable) + val |= (MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + else + val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); +} + +static void +mt76x2_reset_wlan(struct mt76x2_dev *dev, bool enable) +{ + u32 val; + + val = mt76_rr(dev, MT_WLAN_FUN_CTRL); + + val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL; + + if (val & MT_WLAN_FUN_CTRL_WLAN_EN) { + val |= MT_WLAN_FUN_CTRL_WLAN_RESET_RF; + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + val &= ~MT_WLAN_FUN_CTRL_WLAN_RESET_RF; + } + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + mt76x2_set_wlan_state(dev, enable); +} + +int mt76x2_init_hardware(struct mt76x2_dev *dev) +{ + static const u16 beacon_offsets[16] = { + /* 1024 byte per beacon */ + 0xc000, + 0xc400, + 0xc800, + 0xcc00, + 0xd000, + 0xd400, + 0xd800, + 0xdc00, + + /* BSS idx 8-15 not used for beacons */ + 0xc000, + 0xc000, + 0xc000, + 0xc000, + 0xc000, + 0xc000, + 0xc000, + 0xc000, + }; + u32 val; + int ret; + + dev->beacon_offsets = beacon_offsets; + tasklet_init(&dev->pre_tbtt_tasklet, mt76x2_pre_tbtt_tasklet, + (unsigned long) dev); + + dev->chainmask = 0x202; + dev->global_wcid.idx = 255; + dev->global_wcid.hw_key_idx = -1; + dev->slottime = 9; + + val = mt76_rr(dev, MT_WPDMA_GLO_CFG); + val &= MT_WPDMA_GLO_CFG_DMA_BURST_SIZE | + MT_WPDMA_GLO_CFG_BIG_ENDIAN | + MT_WPDMA_GLO_CFG_HDR_SEG_LEN; + val |= MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE; + mt76_wr(dev, MT_WPDMA_GLO_CFG, val); + + mt76x2_reset_wlan(dev, true); + mt76x2_power_on(dev); + + ret = mt76x2_eeprom_init(dev); + if (ret) + return ret; + + ret = mt76x2_mac_reset(dev, true); + if (ret) + return ret; + + ret = mt76x2_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mt76.state); + ret = mt76x2_mac_start(dev); + if (ret) + return ret; + + ret = mt76x2_mcu_init(dev); + if (ret) + return ret; + + mt76x2_mac_stop(dev, false); + dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + + return 0; +} + +void mt76x2_stop_hardware(struct mt76x2_dev *dev) +{ + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mac_work); + mt76x2_mcu_set_radio_state(dev, false); + mt76x2_mac_stop(dev, false); +} + +void mt76x2_cleanup(struct mt76x2_dev *dev) +{ + mt76x2_stop_hardware(dev); + mt76x2_dma_cleanup(dev); + mt76x2_mcu_cleanup(dev); +} + +struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = sizeof(struct mt76x2_txwi), + .update_survey = mt76x2_update_channel, + .tx_prepare_skb = mt76x2_tx_prepare_skb, + .tx_complete_skb = mt76x2_tx_complete_skb, + .rx_skb = mt76x2_queue_rx_skb, + .rx_poll_complete = mt76x2_rx_poll_complete, + }; + struct ieee80211_hw *hw; + struct mt76x2_dev *dev; + + hw = ieee80211_alloc_hw(sizeof(*dev), &mt76x2_ops); + if (!hw) + return NULL; + + dev = hw->priv; + dev->mt76.dev = pdev; + dev->mt76.hw = hw; + dev->mt76.drv = &drv_ops; + mutex_init(&dev->mutex); + spin_lock_init(&dev->irq_lock); + + 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, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \ +} + +#define OFDM_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \ +} + +static struct ieee80211_rate mt76x2_rates[] = { + CCK_RATE(0, 10), + CCK_RATE(1, 20), + CCK_RATE(2, 55), + CCK_RATE(3, 110), + OFDM_RATE(0, 60), + OFDM_RATE(1, 90), + OFDM_RATE(2, 120), + OFDM_RATE(3, 180), + OFDM_RATE(4, 240), + OFDM_RATE(5, 360), + OFDM_RATE(6, 480), + OFDM_RATE(7, 540), +}; + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 8, + .types = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + } +}; + +static void mt76x2_led_set_config(struct mt76_dev *mt76, u8 delay_on, + u8 delay_off) +{ + struct mt76x2_dev *dev = container_of(mt76, struct mt76x2_dev, + mt76); + u32 val; + + val = MT_LED_STATUS_DURATION(0xff) | + MT_LED_STATUS_OFF(delay_off) | + MT_LED_STATUS_ON(delay_on); + + mt76_wr(dev, MT_LED_S0(mt76->led_pin), val); + mt76_wr(dev, MT_LED_S1(mt76->led_pin), val); + + val = MT_LED_CTRL_REPLAY(mt76->led_pin) | + MT_LED_CTRL_KICK(mt76->led_pin); + if (mt76->led_al) + val |= MT_LED_CTRL_POLARITY(mt76->led_pin); + mt76_wr(dev, MT_LED_CTRL, val); +} + +static int mt76x2_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, + led_cdev); + u8 delta_on, delta_off; + + delta_off = max_t(u8, *delay_off / 10, 1); + delta_on = max_t(u8, *delay_on / 10, 1); + + mt76x2_led_set_config(mt76, delta_on, delta_off); + return 0; +} + +static void mt76x2_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, + led_cdev); + + if (!brightness) + mt76x2_led_set_config(mt76, 0, 0xff); + else + mt76x2_led_set_config(mt76, 0xff, 0); +} + +int mt76x2_register_device(struct mt76x2_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + void *status_fifo; + int fifo_size; + int i, ret; + + fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76x2_tx_status)); + status_fifo = devm_kzalloc(dev->mt76.dev, fifo_size, GFP_KERNEL); + if (!status_fifo) + return -ENOMEM; + + kfifo_init(&dev->txstatus_fifo, status_fifo, fifo_size); + + ret = mt76x2_init_hardware(dev); + if (ret) + return ret; + + hw->queues = 4; + hw->max_rates = 1; + hw->max_report_rates = 7; + hw->max_rate_tries = 1; + hw->extra_tx_headroom = 2; + + hw->sta_data_size = sizeof(struct mt76x2_sta); + hw->vif_data_size = sizeof(struct mt76x2_vif); + + for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) { + u8 *addr = dev->macaddr_list[i].addr; + + memcpy(addr, dev->mt76.macaddr, ETH_ALEN); + + if (!i) + continue; + + addr[0] |= BIT(1); + addr[0] ^= ((i - 1) << 2); + } + wiphy->addresses = dev->macaddr_list; + wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list); + + 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); + INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate); + INIT_DELAYED_WORK(&dev->mac_work, mt76x2_mac_work); + + 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); + + /* init led callbacks */ + dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness; + dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink; + + ret = mt76_register_device(&dev->mt76, true, mt76x2_rates, + ARRAY_SIZE(mt76x2_rates)); + if (ret) + goto fail; + + mt76x2_init_debugfs(dev); + + return 0; + +fail: + mt76x2_stop_hardware(dev); + return ret; +} + + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c new file mode 100644 index 000000000000..0b4fecc14096 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" +#include "mt76x2_mcu.h" +#include "mt76x2_eeprom.h" +#include "mt76x2_trace.h" + +void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr) +{ + idx &= 7; + mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr)); + mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR, + get_unaligned_le16(addr + 4)); +} + +static void +mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (idx >= 8) + idx = 0; + + if (status->band == NL80211_BAND_2GHZ) + idx += 4; + + status->rate_idx = idx; + return; + case MT_PHY_TYPE_CCK: + if (idx >= 8) { + idx -= 8; + status->flag |= RX_FLAG_SHORTPRE; + } + + if (idx >= 4) + idx = 0; + + status->rate_idx = idx; + return; + case MT_PHY_TYPE_HT_GF: + status->flag |= RX_FLAG_HT_GF; + /* fall through */ + case MT_PHY_TYPE_HT: + status->flag |= RX_FLAG_HT; + status->rate_idx = idx; + break; + case MT_PHY_TYPE_VHT: + status->flag |= RX_FLAG_VHT; + status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx); + status->vht_nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1; + break; + default: + WARN_ON(1); + return; + } + + if (rate & MT_RXWI_RATE_LDPC) + status->flag |= RX_FLAG_LDPC; + + if (rate & MT_RXWI_RATE_SGI) + status->flag |= RX_FLAG_SHORT_GI; + + if (rate & MT_RXWI_RATE_STBC) + status->flag |= 1 << RX_FLAG_STBC_SHIFT; + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + status->flag |= RX_FLAG_40MHZ; + break; + case MT_PHY_BW_80: + status->vht_flag |= RX_VHT_FLAG_80MHZ; + break; + default: + break; + } +} + +static __le16 +mt76x2_mac_tx_rate_val(struct mt76x2_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val) +{ + u16 rateval; + u8 phy, rate_idx; + u8 nss = 1; + u8 bw = 0; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 4); + phy = MT_PHY_TYPE_VHT; + if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = 2; + else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->mt76.chandef.chan->band; + u16 val; + + r = &mt76_hw(dev)->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + bw = 0; + } + + rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx); + rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); + rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rateval |= MT_RXWI_RATE_SGI; + + *nss_val = nss; + return cpu_to_le16(rateval); +} + +void mt76x2_mac_wcid_set_drop(struct mt76x2_dev *dev, u8 idx, bool drop) +{ + u32 val = mt76_rr(dev, MT_WCID_DROP(idx)); + u32 bit = MT_WCID_DROP_MASK(idx); + + /* prevent unnecessary writes */ + if ((val & bit) != (bit * drop)) + mt76_wr(dev, MT_WCID_DROP(idx), (val & ~bit) | (bit * drop)); +} + +void mt76x2_mac_wcid_set_rate(struct mt76x2_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate) +{ + spin_lock_bh(&dev->mt76.lock); + wcid->tx_rate = mt76x2_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); + wcid->tx_rate_set = true; + spin_unlock_bh(&dev->mt76.lock); +} + +void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2)); + u16 txwi_flags = 0; + u8 nss; + s8 txpwr_adj, max_txpwr_adj; + + memset(txwi, 0, sizeof(*txwi)); + + if (wcid) + txwi->wcid = wcid->idx; + else + txwi->wcid = 0xff; + + txwi->pktid = 1; + + spin_lock_bh(&dev->mt76.lock); + if (rate->idx < 0 || !rate->count) { + txwi->rate = wcid->tx_rate; + max_txpwr_adj = wcid->max_txpwr_adj; + nss = wcid->tx_rate_nss; + } else { + txwi->rate = mt76x2_mac_tx_rate_val(dev, rate, &nss); + max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj(dev, rate); + } + spin_unlock_bh(&dev->mt76.lock); + + txpwr_adj = mt76x2_tx_get_txpwr_adj(dev, dev->txpower_conf, + max_txpwr_adj); + txwi->ctl2 = FIELD_PREP(MT_TX_PWR_ADJ, txpwr_adj); + + if (mt76xx_rev(dev) >= MT76XX_REV_E4) + txwi->txstream = 0x13; + else if (mt76xx_rev(dev) >= MT76XX_REV_E3 && + !(txwi->rate & cpu_to_le16(rate_ht_mask))) + txwi->txstream = 0x93; + + if (info->flags & IEEE80211_TX_CTL_LDPC) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC); + if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC); + if (nss > 1 && sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC) + txwi_flags |= MT_TXWI_FLAGS_MMPS; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + txwi->pktid |= MT_TXWI_PKTID_PROBE; + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { + u8 ba_size = IEEE80211_MIN_AMPDU_BUF; + + ba_size <<= sta->ht_cap.ampdu_factor; + ba_size = min_t(int, 63, ba_size - 1); + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + ba_size = 0; + txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); + + txwi_flags |= MT_TXWI_FLAGS_AMPDU | + FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, + sta->ht_cap.ampdu_density); + } + + txwi->flags |= cpu_to_le16(txwi_flags); + txwi->len_ctl = cpu_to_le16(skb->len); +} + +static void mt76x2_remove_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + + memmove(skb->data + 2, skb->data, len); + skb_pull(skb, 2); +} + +int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, + void *rxi) +{ + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct mt76x2_rxwi *rxwi = rxi; + u32 ctl = le32_to_cpu(rxwi->ctl); + u16 rate = le16_to_cpu(rxwi->rate); + int len; + + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) + mt76x2_remove_hdr_pad(skb); + + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; + } + + len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); + if (WARN_ON_ONCE(len > skb->len)) + return -EINVAL; + + pskb_trim(skb, len); + status->chains = BIT(0) | BIT(1); + status->chain_signal[0] = mt76x2_phy_get_rssi(dev, rxwi->rssi[0], 0); + status->chain_signal[1] = mt76x2_phy_get_rssi(dev, rxwi->rssi[1], 1); + status->signal = max(status->chain_signal[0], status->chain_signal[1]); + status->freq = dev->mt76.chandef.chan->center_freq; + status->band = dev->mt76.chandef.chan->band; + + mt76x2_mac_process_rate(status, rate); + + return 0; +} + +static void +mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate, + enum nl80211_band band) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + txrate->idx = 0; + txrate->flags = 0; + txrate->count = 1; + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (band == NL80211_BAND_2GHZ) + idx += 4; + + txrate->idx = idx; + return; + case MT_PHY_TYPE_CCK: + if (idx >= 8) + idx -= 8; + + txrate->idx = idx; + return; + case MT_PHY_TYPE_HT_GF: + txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; + /* fall through */ + case MT_PHY_TYPE_HT: + txrate->flags |= IEEE80211_TX_RC_MCS; + txrate->idx = idx; + break; + case MT_PHY_TYPE_VHT: + txrate->flags |= IEEE80211_TX_RC_VHT_MCS; + txrate->idx = idx; + break; + default: + WARN_ON(1); + return; + } + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case MT_PHY_BW_80: + txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + default: + WARN_ON(1); + break; + } + + if (rate & MT_RXWI_RATE_SGI) + txrate->flags |= IEEE80211_TX_RC_SHORT_GI; +} + +static void +mt76x2_mac_fill_tx_status(struct mt76x2_dev *dev, + struct ieee80211_tx_info *info, + struct mt76x2_tx_status *st, int n_frames) +{ + struct ieee80211_tx_rate *rate = info->status.rates; + int cur_idx, last_rate; + int i; + + if (!n_frames) + return; + + last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); + mt76x2_mac_process_tx_rate(&rate[last_rate], st->rate, + dev->mt76.chandef.chan->band); + if (last_rate < IEEE80211_TX_MAX_RATES - 1) + rate[last_rate + 1].idx = -1; + + cur_idx = rate[last_rate].idx + st->retry; + for (i = 0; i <= last_rate; i++) { + rate[i].flags = rate[last_rate].flags; + rate[i].idx = max_t(int, 0, cur_idx - i); + rate[i].count = 1; + } + + if (last_rate > 0) + rate[last_rate - 1].count = st->retry + 1 - last_rate; + + info->status.ampdu_len = n_frames; + info->status.ampdu_ack_len = st->success ? n_frames : 0; + + if (st->pktid & MT_TXWI_PKTID_PROBE) + info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + + if (st->aggr) + info->flags |= IEEE80211_TX_CTL_AMPDU | + IEEE80211_TX_STAT_AMPDU; + + if (!st->ack_req) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + else if (st->success) + info->flags |= IEEE80211_TX_STAT_ACK; +} + +static void +mt76x2_send_tx_status(struct mt76x2_dev *dev, struct mt76x2_tx_status *stat, + u8 *update) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + struct mt76x2_sta *msta = NULL; + + rcu_read_lock(); + if (stat->wcid < ARRAY_SIZE(dev->wcid)) + wcid = rcu_dereference(dev->wcid[stat->wcid]); + + if (wcid) { + void *priv; + + priv = msta = container_of(wcid, struct mt76x2_sta, wcid); + sta = container_of(priv, struct ieee80211_sta, + drv_priv); + } + + if (msta && stat->aggr) { + u32 stat_val, stat_cache; + + stat_val = stat->rate; + stat_val |= ((u32) stat->retry) << 16; + stat_cache = msta->status.rate; + stat_cache |= ((u32) msta->status.retry) << 16; + + if (*update == 0 && stat_val == stat_cache && + stat->wcid == msta->status.wcid && msta->n_frames < 32) { + msta->n_frames++; + goto out; + } + + mt76x2_mac_fill_tx_status(dev, &info, &msta->status, + msta->n_frames); + + msta->status = *stat; + msta->n_frames = 1; + *update = 0; + } else { + mt76x2_mac_fill_tx_status(dev, &info, stat, 1); + *update = 1; + } + + ieee80211_tx_status_noskb(mt76_hw(dev), sta, &info); + +out: + rcu_read_unlock(); +} + +void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq) +{ + struct mt76x2_tx_status stat = {}; + unsigned long flags; + u8 update = 1; + + if (!test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) + return; + + trace_mac_txstat_poll(dev); + + while (!irq || !kfifo_is_full(&dev->txstatus_fifo)) { + u32 stat1, stat2; + + spin_lock_irqsave(&dev->irq_lock, flags); + stat2 = mt76_rr(dev, MT_TX_STAT_FIFO_EXT); + stat1 = mt76_rr(dev, MT_TX_STAT_FIFO); + if (!(stat1 & MT_TX_STAT_FIFO_VALID)) { + spin_unlock_irqrestore(&dev->irq_lock, flags); + break; + } + + spin_unlock_irqrestore(&dev->irq_lock, flags); + + stat.valid = 1; + stat.success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS); + stat.aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR); + stat.ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ); + stat.wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, stat1); + stat.rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, stat1); + stat.retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2); + stat.pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2); + trace_mac_txstat_fetch(dev, &stat); + + if (!irq) { + mt76x2_send_tx_status(dev, &stat, &update); + continue; + } + + kfifo_put(&dev->txstatus_fifo, stat); + } +} + +static void +mt76x2_mac_queue_txdone(struct mt76x2_dev *dev, struct sk_buff *skb, + void *txwi_ptr) +{ + struct mt76x2_tx_info *txi = mt76x2_skb_tx_info(skb); + struct mt76x2_txwi *txwi = txwi_ptr; + + mt76x2_mac_poll_tx_status(dev, false); + + txi->tries = 0; + txi->jiffies = jiffies; + txi->wcid = txwi->wcid; + txi->pktid = txwi->pktid; + trace_mac_txdone_add(dev, txwi->wcid, txwi->pktid); + mt76x2_tx_complete(dev, skb); +} + +void mt76x2_mac_process_tx_status_fifo(struct mt76x2_dev *dev) +{ + struct mt76x2_tx_status stat; + u8 update = 1; + + while (kfifo_get(&dev->txstatus_fifo, &stat)) + mt76x2_send_tx_status(dev, &stat, &update); +} + +void mt76x2_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush) +{ + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + + if (e->txwi) + mt76x2_mac_queue_txdone(dev, e->skb, &e->txwi->txwi); + else + dev_kfree_skb_any(e->skb); +} + +static enum mt76x2_cipher_type +mt76x2_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT_CIPHER_NONE; + + if (key->keylen > 32) + return MT_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + default: + return MT_CIPHER_NONE; + } +} + +void mt76x2_mac_wcid_setup(struct mt76x2_dev *dev, u8 idx, u8 vif_idx, u8 *mac) +{ + struct mt76_wcid_addr addr = {}; + u32 attr; + + attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | + FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); + + mt76_wr(dev, MT_WCID_ATTR(idx), attr); + + mt76_wr(dev, MT_WCID_TX_RATE(idx), 0); + mt76_wr(dev, MT_WCID_TX_RATE(idx) + 4, 0); + + if (idx >= 128) + return; + + if (mac) + memcpy(addr.macaddr, mac, ETH_ALEN); + + mt76_wr_copy(dev, MT_WCID_ADDR(idx), &addr, sizeof(addr)); +} + +int mt76x2_mac_wcid_set_key(struct mt76x2_dev *dev, u8 idx, + struct ieee80211_key_conf *key) +{ + enum mt76x2_cipher_type cipher; + u8 key_data[32]; + u8 iv_data[8]; + + cipher = mt76x2_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PKEY_MODE, cipher); + mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); + + memset(iv_data, 0, sizeof(iv_data)); + if (key) { + mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + iv_data[3] = key->keyidx << 6; + if (cipher >= MT_CIPHER_TKIP) + iv_data[3] |= 0x20; + } + + mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); + + return 0; +} + +int mt76x2_mac_shared_key_setup(struct mt76x2_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key) +{ + enum mt76x2_cipher_type cipher; + u8 key_data[32]; + u32 val; + + cipher = mt76x2_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); + val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); + val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); + mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); + + mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), key_data, + sizeof(key_data)); + + return 0; +} + +static int +mt76_write_beacon(struct mt76x2_dev *dev, int offset, struct sk_buff *skb) +{ + int beacon_len = dev->beacon_offsets[1] - dev->beacon_offsets[0]; + struct mt76x2_txwi txwi; + + if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x2_txwi))) + return -ENOSPC; + + mt76x2_mac_write_txwi(dev, &txwi, skb, NULL, NULL); + txwi.flags |= cpu_to_le16(MT_TXWI_FLAGS_TS); + + mt76_wr_copy(dev, offset, &txwi, sizeof(txwi)); + offset += sizeof(txwi); + + mt76_wr_copy(dev, offset, skb->data, skb->len); + return 0; +} + +static int +__mt76x2_mac_set_beacon(struct mt76x2_dev *dev, u8 bcn_idx, struct sk_buff *skb) +{ + int beacon_len = dev->beacon_offsets[1] - dev->beacon_offsets[0]; + int beacon_addr = dev->beacon_offsets[bcn_idx]; + int ret = 0; + int i; + + /* Prevent corrupt transmissions during update */ + mt76_set(dev, MT_BCN_BYPASS_MASK, BIT(bcn_idx)); + + if (skb) { + ret = mt76_write_beacon(dev, beacon_addr, skb); + if (!ret) + dev->beacon_data_mask |= BIT(bcn_idx) & + dev->beacon_mask; + } else { + dev->beacon_data_mask &= ~BIT(bcn_idx); + for (i = 0; i < beacon_len; i += 4) + mt76_wr(dev, beacon_addr + i, 0); + } + + mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xff00 | ~dev->beacon_data_mask); + + return ret; +} + +int mt76x2_mac_set_beacon(struct mt76x2_dev *dev, u8 vif_idx, + struct sk_buff *skb) +{ + bool force_update = false; + int bcn_idx = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(dev->beacons); i++) { + if (vif_idx == i) { + force_update = !!dev->beacons[i] ^ !!skb; + + if (dev->beacons[i]) + dev_kfree_skb(dev->beacons[i]); + + dev->beacons[i] = skb; + __mt76x2_mac_set_beacon(dev, bcn_idx, skb); + } else if (force_update && dev->beacons[i]) { + __mt76x2_mac_set_beacon(dev, bcn_idx, dev->beacons[i]); + } + + bcn_idx += !!dev->beacons[i]; + } + + for (i = bcn_idx; i < ARRAY_SIZE(dev->beacons); i++) { + if (!(dev->beacon_data_mask & BIT(i))) + break; + + __mt76x2_mac_set_beacon(dev, i, NULL); + } + + mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N, + bcn_idx - 1); + return 0; +} + +void mt76x2_mac_set_beacon_enable(struct mt76x2_dev *dev, u8 vif_idx, bool val) +{ + u8 old_mask = dev->beacon_mask; + bool en; + u32 reg; + + if (val) { + dev->beacon_mask |= BIT(vif_idx); + } else { + dev->beacon_mask &= ~BIT(vif_idx); + mt76x2_mac_set_beacon(dev, vif_idx, NULL); + } + + if (!!old_mask == !!dev->beacon_mask) + return; + + en = dev->beacon_mask; + + mt76_rmw_field(dev, MT_INT_TIMER_EN, MT_INT_TIMER_EN_PRE_TBTT_EN, en); + reg = MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_TIMER_EN; + mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en); + + if (en) + mt76x2_irq_enable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT); + else + mt76x2_irq_disable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT); +} + +void mt76x2_update_channel(struct mt76_dev *mdev) +{ + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + struct mt76_channel_state *state; + u32 active, busy; + + state = mt76_channel_state(&dev->mt76, dev->mt76.chandef.chan); + + busy = mt76_rr(dev, MT_CH_BUSY); + active = busy + mt76_rr(dev, MT_CH_IDLE); + + spin_lock_bh(&dev->mt76.cc_lock); + state->cc_busy += busy; + state->cc_active += active; + spin_unlock_bh(&dev->mt76.cc_lock); +} + +void mt76x2_mac_work(struct work_struct *work) +{ + struct mt76x2_dev *dev = container_of(work, struct mt76x2_dev, + mac_work.work); + int i, idx; + + mt76x2_update_channel(&dev->mt76); + for (i = 0, idx = 0; i < 16; i++) { + u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); + + dev->aggr_stats[idx++] += val & 0xffff; + dev->aggr_stats[idx++] += val >> 16; + } + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, + MT_CALIBRATE_INTERVAL); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h new file mode 100644 index 000000000000..8a8a25e32d5f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_MAC_H +#define __MT76x2_MAC_H + +#include "mt76.h" + +struct mt76x2_dev; +struct mt76x2_sta; +struct mt76x2_vif; +struct mt76x2_txwi; + +struct mt76x2_tx_status { + u8 valid:1; + u8 success:1; + u8 aggr:1; + u8 ack_req:1; + u8 wcid; + u8 pktid; + u8 retry; + u16 rate; +} __packed __aligned(2); + +struct mt76x2_tx_info { + unsigned long jiffies; + u8 tries; + + u8 wcid; + u8 pktid; + u8 retry; +}; + +struct mt76x2_rxwi { + __le32 rxinfo; + + __le32 ctl; + + __le16 tid_sn; + __le16 rate; + + u8 rssi[4]; + + __le32 bbp_rxinfo[4]; +}; + +#define MT_RXINFO_BA BIT(0) +#define MT_RXINFO_DATA BIT(1) +#define MT_RXINFO_NULL BIT(2) +#define MT_RXINFO_FRAG BIT(3) +#define MT_RXINFO_UNICAST BIT(4) +#define MT_RXINFO_MULTICAST BIT(5) +#define MT_RXINFO_BROADCAST BIT(6) +#define MT_RXINFO_MYBSS BIT(7) +#define MT_RXINFO_CRCERR BIT(8) +#define MT_RXINFO_ICVERR BIT(9) +#define MT_RXINFO_MICERR BIT(10) +#define MT_RXINFO_AMSDU BIT(11) +#define MT_RXINFO_HTC BIT(12) +#define MT_RXINFO_RSSI BIT(13) +#define MT_RXINFO_L2PAD BIT(14) +#define MT_RXINFO_AMPDU BIT(15) +#define MT_RXINFO_DECRYPT BIT(16) +#define MT_RXINFO_BSSIDX3 BIT(17) +#define MT_RXINFO_WAPI_KEY BIT(18) +#define MT_RXINFO_PN_LEN GENMASK(21, 19) +#define MT_RXINFO_SW_FTYPE0 BIT(22) +#define MT_RXINFO_SW_FTYPE1 BIT(23) +#define MT_RXINFO_PROBE_RESP BIT(24) +#define MT_RXINFO_BEACON BIT(25) +#define MT_RXINFO_DISASSOC BIT(26) +#define MT_RXINFO_DEAUTH BIT(27) +#define MT_RXINFO_ACTION BIT(28) +#define MT_RXINFO_TCP_SUM_ERR BIT(30) +#define MT_RXINFO_IP_SUM_ERR BIT(31) + +#define MT_RXWI_CTL_WCID GENMASK(7, 0) +#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) +#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) +#define MT_RXWI_CTL_UDF GENMASK(15, 13) +#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16) +#define MT_RXWI_CTL_EOF BIT(31) + +#define MT_RXWI_TID GENMASK(3, 0) +#define MT_RXWI_SN GENMASK(15, 4) + +#define MT_RXWI_RATE_INDEX GENMASK(5, 0) +#define MT_RXWI_RATE_LDPC BIT(6) +#define MT_RXWI_RATE_BW GENMASK(8, 7) +#define MT_RXWI_RATE_SGI BIT(9) +#define MT_RXWI_RATE_STBC BIT(10) +#define MT_RXWI_RATE_LDPC_EXSYM BIT(11) +#define MT_RXWI_RATE_PHY GENMASK(15, 13) + +#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) +#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) + +#define MT_TX_PWR_ADJ GENMASK(3, 0) + +enum mt76x2_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, + MT_PHY_BW_80, +}; + +#define MT_TXWI_FLAGS_FRAG BIT(0) +#define MT_TXWI_FLAGS_MMPS BIT(1) +#define MT_TXWI_FLAGS_CFACK BIT(2) +#define MT_TXWI_FLAGS_TS BIT(3) +#define MT_TXWI_FLAGS_AMPDU BIT(4) +#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) +#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) +#define MT_TXWI_FLAGS_NDPS BIT(10) +#define MT_TXWI_FLAGS_RTSBWSIG BIT(11) +#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12) +#define MT_TXWI_FLAGS_SOUND BIT(14) +#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) + +#define MT_TXWI_ACK_CTL_REQ BIT(0) +#define MT_TXWI_ACK_CTL_NSEQ BIT(1) +#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) + +#define MT_TXWI_PKTID_PROBE BIT(7) + +struct mt76x2_txwi { + __le16 flags; + __le16 rate; + u8 ack_ctl; + u8 wcid; + __le16 len_ctl; + __le32 iv; + __le32 eiv; + u8 aid; + u8 txstream; + u8 ctl2; + u8 pktid; +} __packed __aligned(4); + +static inline struct mt76x2_tx_info * +mt76x2_skb_tx_info(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + return (void *) info->status.status_driver_data; +} + +int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard); +int mt76x2_mac_start(struct mt76x2_dev *dev); +void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force); +void mt76x2_mac_resume(struct mt76x2_dev *dev); +void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr); + +int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, + void *rxi); +void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta); +void mt76x2_mac_wcid_setup(struct mt76x2_dev *dev, u8 idx, u8 vif_idx, u8 *mac); +int mt76x2_mac_wcid_set_key(struct mt76x2_dev *dev, u8 idx, + struct ieee80211_key_conf *key); +void mt76x2_mac_wcid_set_rate(struct mt76x2_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate); +void mt76x2_mac_wcid_set_drop(struct mt76x2_dev *dev, u8 idx, bool drop); + +int mt76x2_mac_shared_key_setup(struct mt76x2_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key); + +int mt76x2_mac_set_beacon(struct mt76x2_dev *dev, u8 vif_idx, + struct sk_buff *skb); +void mt76x2_mac_set_beacon_enable(struct mt76x2_dev *dev, u8 vif_idx, bool val); + +void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq); +void mt76x2_mac_process_tx_status_fifo(struct mt76x2_dev *dev); + +void mt76x2_mac_work(struct work_struct *work); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c new file mode 100644 index 000000000000..2cef48edb275 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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" + +static int +mt76x2_start(struct ieee80211_hw *hw) +{ + struct mt76x2_dev *dev = hw->priv; + int ret; + + mutex_lock(&dev->mutex); + + ret = mt76x2_mac_start(dev); + if (ret) + goto out; + + ret = mt76x2_phy_start(dev); + if (ret) + goto out; + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, + MT_CALIBRATE_INTERVAL); + + set_bit(MT76_STATE_RUNNING, &dev->mt76.state); + +out: + mutex_unlock(&dev->mutex); + return ret; +} + +static void +mt76x2_stop(struct ieee80211_hw *hw) +{ + struct mt76x2_dev *dev = hw->priv; + + mutex_lock(&dev->mutex); + clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); + mt76x2_stop_hardware(dev); + mutex_unlock(&dev->mutex); +} + +static void +mt76x2_txq_init(struct mt76x2_dev *dev, struct ieee80211_txq *txq) +{ + struct mt76_txq *mtxq; + + if (!txq) + return; + + mtxq = (struct mt76_txq *) txq->drv_priv; + if (txq->sta) { + struct mt76x2_sta *sta; + + sta = (struct mt76x2_sta *) txq->sta->drv_priv; + mtxq->wcid = &sta->wcid; + } else { + struct mt76x2_vif *mvif; + + mvif = (struct mt76x2_vif *) txq->vif->drv_priv; + mtxq->wcid = &mvif->group_wcid; + } + + mt76_txq_init(&dev->mt76, txq); +} + +static int +mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + unsigned int idx = 0; + int ret = 0; + + if (vif->addr[0] & BIT(1)) + idx = 1 + (((dev->mt76.macaddr[0] ^ vif->addr[0]) >> 2) & 7); + + /* + * Client mode typically only has one configurable BSSID register, + * which is used for bssidx=0. This is linked to the MAC address. + * Since mac80211 allows changing interface types, and we cannot + * force the use of the primary MAC address for a station mode + * interface, we need some other way of configuring a per-interface + * remote BSSID. + * The hardware provides an AP-Client feature, where bssidx 0-7 are + * used for AP mode and bssidx 8-15 for client mode. + * We shift the station interface bss index by 8 to force the + * hardware to recognize the BSSID. + * The resulting bssidx mismatch for unicast frames is ignored by hw. + */ + if (vif->type == NL80211_IFTYPE_STATION) + idx += 8; + + mvif->idx = idx; + mvif->group_wcid.idx = 254 - idx; + mvif->group_wcid.hw_key_idx = -1; + mt76x2_txq_init(dev, vif->txq); + + return ret; +} + +static void +mt76x2_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76x2_dev *dev = hw->priv; + + mt76_txq_remove(&dev->mt76, vif->txq); +} + +static int +mt76x2_set_channel(struct mt76x2_dev *dev, struct cfg80211_chan_def *chandef) +{ + int ret; + + mt76_set_channel(&dev->mt76); + + tasklet_disable(&dev->pre_tbtt_tasklet); + cancel_delayed_work_sync(&dev->cal_work); + + mt76x2_mac_stop(dev, true); + ret = mt76x2_phy_set_channel(dev, chandef); + + /* channel cycle counters read-and-clear */ + 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); + + return ret; +} + +static int +mt76x2_config(struct ieee80211_hw *hw, u32 changed) +{ + struct mt76x2_dev *dev = hw->priv; + int ret = 0; + + mutex_lock(&dev->mutex); + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + dev->txpower_conf = hw->conf.power_level * 2; + + if (test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) { + mt76x2_phy_set_txpower(dev); + mt76x2_tx_set_txpwr_auto(dev, dev->txpower_conf); + } + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ieee80211_stop_queues(hw); + ret = mt76x2_set_channel(dev, &hw->conf.chandef); + ieee80211_wake_queues(hw); + } + + mutex_unlock(&dev->mutex); + + return ret; +} + +static void +mt76x2_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt76x2_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->rxfilter &= ~(_hw); \ + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mutex); + + dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; + + MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); + MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); + MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | + MT_RX_FILTR_CFG_CTS | + MT_RX_FILTR_CFG_CFEND | + MT_RX_FILTR_CFG_CFACK | + MT_RX_FILTR_CFG_BA | + MT_RX_FILTR_CFG_CTRL_RSV); + MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); + + *total_flags = flags; + mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + + mutex_unlock(&dev->mutex); +} + +static void +mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + + mutex_lock(&dev->mutex); + + if (changed & BSS_CHANGED_BSSID) + mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid); + + if (changed & BSS_CHANGED_BEACON_INT) + mt76_rmw_field(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_INTVAL, + info->beacon_int << 4); + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + tasklet_disable(&dev->pre_tbtt_tasklet); + mt76x2_mac_set_beacon_enable(dev, mvif->idx, + info->enable_beacon); + tasklet_enable(&dev->pre_tbtt_tasklet); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + dev->slottime = slottime; + mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, + MT_BKOFF_SLOT_CFG_SLOTTIME, slottime); + } + + mutex_unlock(&dev->mutex); +} + +static int +mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + int ret = 0; + int idx = 0; + int i; + + mutex_lock(&dev->mutex); + + idx = mt76_wcid_alloc(dev->wcid_mask, ARRAY_SIZE(dev->wcid)); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + + msta->wcid.idx = idx; + msta->wcid.hw_key_idx = -1; + mt76x2_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); + mt76x2_mac_wcid_set_drop(dev, idx, false); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + mt76x2_txq_init(dev, sta->txq[i]); + + rcu_assign_pointer(dev->wcid[idx], &msta->wcid); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} + +static int +mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + int idx = msta->wcid.idx; + int i; + + mutex_lock(&dev->mutex); + rcu_assign_pointer(dev->wcid[idx], NULL); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + mt76_txq_remove(&dev->mt76, sta->txq[i]); + mt76x2_mac_wcid_set_drop(dev, idx, true); + mt76_wcid_free(dev->wcid_mask, idx); + mt76x2_mac_wcid_setup(dev, idx, 0, NULL); + mutex_unlock(&dev->mutex); + + return 0; +} + +static void +mt76x2_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta) +{ + struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + struct mt76x2_dev *dev = hw->priv; + int idx = msta->wcid.idx; + + switch (cmd) { + case STA_NOTIFY_SLEEP: + mt76x2_mac_wcid_set_drop(dev, idx, true); + mt76_stop_tx_queues(&dev->mt76, sta, true); + break; + case STA_NOTIFY_AWAKE: + mt76x2_mac_wcid_set_drop(dev, idx, false); + break; + } +} + +static int +mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct mt76x2_sta *msta; + struct mt76_wcid *wcid; + int idx = key->keyidx; + int ret; + + /* + * The hardware does not support per-STA RX GTK, fall back + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + msta = sta ? (struct mt76x2_sta *) sta->drv_priv : NULL; + wcid = msta ? &msta->wcid : &mvif->group_wcid; + + if (cmd == SET_KEY) { + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + } else { + if (idx == wcid->hw_key_idx) + wcid->hw_key_idx = -1; + + key = NULL; + } + + if (!msta) { + if (key || wcid->hw_key_idx == idx) { + ret = mt76x2_mac_wcid_set_key(dev, wcid->idx, key); + if (ret) + return ret; + } + + return mt76x2_mac_shared_key_setup(dev, mvif->idx, idx, key); + } + + return mt76x2_mac_wcid_set_key(dev, msta->wcid.idx, key); +} + +static int +mt76x2_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt76x2_dev *dev = hw->priv; + u8 cw_min = 5, cw_max = 10; + u32 val; + + if (params->cw_min) + cw_min = fls(params->cw_min); + if (params->cw_max) + cw_max = fls(params->cw_max); + + val = FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop) | + FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | + FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | + FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); + mt76_wr(dev, MT_EDCA_CFG_AC(queue), val); + + val = mt76_rr(dev, MT_WMM_TXOP(queue)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue)); + val |= params->txop << MT_WMM_TXOP_SHIFT(queue); + mt76_wr(dev, MT_WMM_TXOP(queue), val); + + val = mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue); + mt76_wr(dev, MT_WMM_AIFSN, val); + + val = mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMIN, val); + + val = mt76_rr(dev, MT_WMM_CWMAX); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMAX, val); + + return 0; +} + +static void +mt76x2_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *mac) +{ + struct mt76x2_dev *dev = hw->priv; + + tasklet_disable(&dev->pre_tbtt_tasklet); + set_bit(MT76_SCANNING, &dev->mt76.state); +} + +static void +mt76x2_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76x2_dev *dev = hw->priv; + + clear_bit(MT76_SCANNING, &dev->mt76.state); + tasklet_enable(&dev->pre_tbtt_tasklet); + mt76_txq_schedule_all(&dev->mt76); +} + +static void +mt76x2_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ +} + +static int +mt76x2_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm) +{ + struct mt76x2_dev *dev = hw->priv; + + *dbm = dev->txpower_cur / 2; + return 0; +} + +static int +mt76x2_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct ieee80211_sta *sta = params->sta; + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + + if (!txq) + return -EINVAL; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, + BIT(16 + tid)); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + break; + case IEEE80211_AMPDU_TX_START: + mtxq->agg_ssn = *ssn << 4; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + + return 0; +} + +static void +mt76x2_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x2_dev *dev = hw->priv; + struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates); + struct ieee80211_tx_rate rate = {}; + + if (!rates) + return; + + rate.idx = rates->rate[0].idx; + rate.flags = rates->rate[0].flags; + mt76x2_mac_wcid_set_rate(dev, &msta->wcid, &rate); + msta->wcid.max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj(dev, &rate); +} + +static void mt76x2_set_coverage_class(struct ieee80211_hw *hw, + s16 coverage_class) +{ + struct mt76x2_dev *dev = hw->priv; + + mutex_lock(&dev->mutex); + dev->coverage_class = coverage_class; + mt76x2_set_tx_ackto(dev); + mutex_unlock(&dev->mutex); +} + +const struct ieee80211_ops mt76x2_ops = { + .tx = mt76x2_tx, + .start = mt76x2_start, + .stop = mt76x2_stop, + .add_interface = mt76x2_add_interface, + .remove_interface = mt76x2_remove_interface, + .config = mt76x2_config, + .configure_filter = mt76x2_configure_filter, + .bss_info_changed = mt76x2_bss_info_changed, + .sta_add = mt76x2_sta_add, + .sta_remove = mt76x2_sta_remove, + .sta_notify = mt76x2_sta_notify, + .set_key = mt76x2_set_key, + .conf_tx = mt76x2_conf_tx, + .sw_scan_start = mt76x2_sw_scan, + .sw_scan_complete = mt76x2_sw_scan_complete, + .flush = mt76x2_flush, + .ampdu_action = mt76x2_ampdu_action, + .get_txpower = mt76x2_get_txpower, + .wake_tx_queue = mt76_wake_tx_queue, + .sta_rate_tbl_update = mt76x2_sta_rate_tbl_update, + .release_buffered_frames = mt76_release_buffered_frames, + .set_coverage_class = mt76x2_set_coverage_class, + .get_survey = mt76_get_survey, +}; + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c new file mode 100644 index 000000000000..d45737ee1412 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include +#include + +#include "mt76x2.h" +#include "mt76x2_mcu.h" +#include "mt76x2_dma.h" +#include "mt76x2_eeprom.h" + +struct mt76x2_fw_header { + __le32 ilm_len; + __le32 dlm_len; + __le16 build_ver; + __le16 fw_ver; + u8 pad[4]; + char build_time[16]; +}; + +struct mt76x2_patch_header { + char build_time[16]; + char platform[4]; + char hw_version[4]; + char patch_version[4]; + u8 pad[2]; +}; + +static struct sk_buff *mt76x2_mcu_msg_alloc(const void *data, int len) +{ + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + memcpy(skb_put(skb, len), data, len); + + return skb; +} + +static struct sk_buff * +mt76x2_mcu_get_response(struct mt76x2_dev *dev, unsigned long expires) +{ + unsigned long timeout; + + if (!time_is_after_jiffies(expires)) + return NULL; + + timeout = expires - jiffies; + wait_event_timeout(dev->mcu.wait, !skb_queue_empty(&dev->mcu.res_q), + timeout); + return skb_dequeue(&dev->mcu.res_q); +} + +static int +mt76x2_mcu_msg_send(struct mt76x2_dev *dev, struct sk_buff *skb, + enum mcu_cmd cmd) +{ + unsigned long expires = jiffies + HZ; + int ret; + u8 seq; + + if (!skb) + return -EINVAL; + + mutex_lock(&dev->mcu.mutex); + + seq = ++dev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mcu.msg_seq & 0xf; + + ret = mt76x2_tx_queue_mcu(dev, MT_TXQ_MCU, skb, cmd, seq); + if (ret) + goto out; + + while (1) { + u32 *rxfce; + bool check_seq = false; + + skb = mt76x2_mcu_get_response(dev, expires); + if (!skb) { + dev_err(dev->mt76.dev, + "MCU message %d (seq %d) timed out\n", cmd, + seq); + ret = -ETIMEDOUT; + break; + } + + rxfce = (u32 *) skb->cb; + + if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) + check_seq = true; + + dev_kfree_skb(skb); + if (check_seq) + break; + } + +out: + mutex_unlock(&dev->mcu.mutex); + + return ret; +} + +static int +mt76pci_load_rom_patch(struct mt76x2_dev *dev) +{ + const struct firmware *fw = NULL; + struct mt76x2_patch_header *hdr; + bool rom_protect = !is_mt7612(dev); + int len, ret = 0; + __le32 *cur; + u32 patch_mask, patch_reg; + + if (rom_protect && !mt76_poll(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) { + dev_err(dev->mt76.dev, + "Could not get hardware semaphore for ROM PATCH\n"); + return -ETIMEDOUT; + } + + if (mt76xx_rev(dev) >= MT76XX_REV_E3) { + patch_mask = BIT(0); + patch_reg = MT_MCU_CLOCK_CTL; + } else { + patch_mask = BIT(1); + patch_reg = MT_MCU_COM_REG0; + } + + if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) { + dev_info(dev->mt76.dev, "ROM patch already applied\n"); + goto out; + } + + ret = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size <= sizeof(*hdr)) { + ret = -EIO; + dev_err(dev->mt76.dev, "Failed to load firmware\n"); + goto out; + } + + hdr = (struct mt76x2_patch_header *) fw->data; + dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET); + + cur = (__le32 *) (fw->data + sizeof(*hdr)); + len = fw->size - sizeof(*hdr); + mt76_wr_copy(dev, MT_MCU_ROM_PATCH_ADDR, cur, len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + + /* Trigger ROM */ + mt76_wr(dev, MT_MCU_INT_LEVEL, 4); + + if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 2000)) { + dev_err(dev->mt76.dev, "Failed to load ROM patch\n"); + ret = -ETIMEDOUT; + } + +out: + /* release semaphore */ + if (rom_protect) + mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1); + release_firmware(fw); + return ret; +} + +static int +mt76pci_load_firmware(struct mt76x2_dev *dev) +{ + const struct firmware *fw; + const struct mt76x2_fw_header *hdr; + int i, len, ret; + __le32 *cur; + u32 offset, val; + + ret = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) + goto error; + + hdr = (const struct mt76x2_fw_header *) fw->data; + + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); + + if (fw->size != len) + goto error; + + val = le16_to_cpu(hdr->fw_ver); + dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf); + + val = le16_to_cpu(hdr->build_ver); + dev_info(dev->mt76.dev, "Build: %x\n", val); + dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time); + + cur = (__le32 *) (fw->data + sizeof(*hdr)); + len = le32_to_cpu(hdr->ilm_len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ILM_OFFSET); + mt76_wr_copy(dev, MT_MCU_ILM_ADDR, cur, len); + + cur += len / sizeof(*cur); + len = le32_to_cpu(hdr->dlm_len); + + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + offset = MT_MCU_DLM_ADDR_E3; + else + offset = MT_MCU_DLM_ADDR; + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET); + mt76_wr_copy(dev, offset, cur, len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + + val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_2); + if (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, val) == 1) + mt76_set(dev, MT_MCU_COM_REG0, BIT(30)); + + /* trigger firmware */ + mt76_wr(dev, MT_MCU_INT_LEVEL, 2); + for (i = 200; i > 0; i--) { + val = mt76_rr(dev, MT_MCU_COM_REG0); + + if (val & 1) + break; + + msleep(10); + } + + if (!i) { + dev_err(dev->mt76.dev, "Firmware failed to start\n"); + release_firmware(fw); + return -ETIMEDOUT; + } + + dev_info(dev->mt76.dev, "Firmware running!\n"); + + release_firmware(fw); + + return ret; + +error: + dev_err(dev->mt76.dev, "Invalid firmware\n"); + release_firmware(fw); + return -ENOENT; +} + +static int +mt76x2_mcu_function_select(struct mt76x2_dev *dev, enum mcu_function func, + u32 val) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(func), + .value = cpu_to_le32(val), + }; + + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_FUN_SET_OP); +} + +int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, + u8 channel) +{ + struct sk_buff *skb; + struct { + u8 cr_mode; + u8 temp; + u8 ch; + u8 _pad0; + + __le32 cfg; + } __packed __aligned(4) msg = { + .cr_mode = type, + .temp = temp_level, + .ch = channel, + }; + u32 val; + + val = BIT(31); + val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff; + val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00; + msg.cfg = cpu_to_le32(val); + + /* first set the channel without the extension channel info */ + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_LOAD_CR); +} + +int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, + u8 bw_index, bool scan) +{ + struct sk_buff *skb; + struct { + u8 idx; + u8 scan; + u8 bw; + u8 _pad0; + + __le16 chainmask; + u8 ext_chan; + u8 _pad1; + + } __packed __aligned(4) msg = { + .idx = channel, + .scan = scan, + .bw = bw, + .chainmask = cpu_to_le16(dev->chainmask), + }; + + /* first set the channel without the extension channel info */ + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP); + + usleep_range(5000, 10000); + + msg.ext_chan = 0xe0 + bw_index; + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP); +} + +int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on) +{ + struct sk_buff *skb; + struct { + __le32 mode; + __le32 level; + } __packed __aligned(4) msg = { + .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), + .level = cpu_to_le32(0), + }; + + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_POWER_SAVING_OP); +} + +int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, + u32 param) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(type), + .value = cpu_to_le32(param), + }; + int ret; + + mt76_clear(dev, MT_MCU_COM_REG0, BIT(31)); + + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + ret = mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP); + if (ret) + return ret; + + if (WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, + BIT(31), BIT(31), 100))) + return -ETIMEDOUT; + + return 0; +} + +int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, + struct mt76x2_tssi_comp *tssi_data) +{ + struct sk_buff *skb; + struct { + __le32 id; + struct mt76x2_tssi_comp data; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(MCU_CAL_TSSI_COMP), + .data = *tssi_data, + }; + + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP); +} + +int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, + bool force) +{ + struct sk_buff *skb; + struct { + __le32 channel; + __le32 gain_val; + } __packed __aligned(4) msg = { + .channel = cpu_to_le32(channel), + .gain_val = cpu_to_le32(gain), + }; + + if (force) + msg.channel |= cpu_to_le32(BIT(31)); + + skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); + return mt76x2_mcu_msg_send(dev, skb, CMD_INIT_GAIN_OP); +} + +int mt76x2_mcu_init(struct mt76x2_dev *dev) +{ + int ret; + + mutex_init(&dev->mcu.mutex); + + ret = mt76pci_load_rom_patch(dev); + if (ret) + return ret; + + ret = mt76pci_load_firmware(dev); + if (ret) + return ret; + + mt76x2_mcu_function_select(dev, Q_SELECT, 1); + return 0; +} + +int mt76x2_mcu_cleanup(struct mt76x2_dev *dev) +{ + struct sk_buff *skb; + + mt76_wr(dev, MT_MCU_INT_LEVEL, 1); + usleep_range(20000, 30000); + + while ((skb = skb_dequeue(&dev->mcu.res_q)) != NULL) + dev_kfree_skb(skb); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h new file mode 100644 index 000000000000..d7a7e83262ce --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_MCU_H +#define __MT76x2_MCU_H + +/* Register definitions */ +#define MT_MCU_CPU_CTL 0x0704 +#define MT_MCU_CLOCK_CTL 0x0708 +#define MT_MCU_RESET_CTL 0x070C +#define MT_MCU_INT_LEVEL 0x0718 +#define MT_MCU_COM_REG0 0x0730 +#define MT_MCU_COM_REG1 0x0734 +#define MT_MCU_COM_REG2 0x0738 +#define MT_MCU_COM_REG3 0x073C +#define MT_MCU_PCIE_REMAP_BASE1 0x0740 +#define MT_MCU_PCIE_REMAP_BASE2 0x0744 +#define MT_MCU_PCIE_REMAP_BASE3 0x0748 +#define MT_MCU_PCIE_REMAP_BASE4 0x074C + +#define MT_LED_CTRL 0x0770 +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) + +#define MT_LED_TX_BLINK_0 0x0774 +#define MT_LED_TX_BLINK_1 0x0778 + +#define MT_LED_S0_BASE 0x077C +#define MT_LED_S0(_n) (MT_LED_S0_BASE + 8 * (_n)) +#define MT_LED_S1_BASE 0x0780 +#define MT_LED_S1(_n) (MT_LED_S1_BASE + 8 * (_n)) +#define MT_LED_STATUS_OFF_MASK GENMASK(31, 24) +#define MT_LED_STATUS_OFF(_v) (((_v) << __ffs(MT_LED_STATUS_OFF_MASK)) & \ + MT_LED_STATUS_OFF_MASK) +#define MT_LED_STATUS_ON_MASK GENMASK(23, 16) +#define MT_LED_STATUS_ON(_v) (((_v) << __ffs(MT_LED_STATUS_ON_MASK)) & \ + MT_LED_STATUS_ON_MASK) +#define MT_LED_STATUS_DURATION_MASK GENMASK(15, 8) +#define MT_LED_STATUS_DURATION(_v) (((_v) << __ffs(MT_LED_STATUS_DURATION_MASK)) & \ + MT_LED_STATUS_DURATION_MASK) + +#define MT_MCU_SEMAPHORE_00 0x07B0 +#define MT_MCU_SEMAPHORE_01 0x07B4 +#define MT_MCU_SEMAPHORE_02 0x07B8 +#define MT_MCU_SEMAPHORE_03 0x07BC + +#define MT_MCU_ROM_PATCH_OFFSET 0x80000 +#define MT_MCU_ROM_PATCH_ADDR 0x90000 + +#define MT_MCU_ILM_OFFSET 0x80000 +#define MT_MCU_ILM_ADDR 0x80000 + +#define MT_MCU_DLM_OFFSET 0x100000 +#define MT_MCU_DLM_ADDR 0x90000 +#define MT_MCU_DLM_ADDR_E3 0x90800 + +enum mcu_cmd { + CMD_FUN_SET_OP = 1, + CMD_LOAD_CR = 2, + CMD_INIT_GAIN_OP = 3, + CMD_DYNC_VGA_OP = 6, + CMD_TDLS_CH_SW = 7, + CMD_BURST_WRITE = 8, + CMD_READ_MODIFY_WRITE = 9, + CMD_RANDOM_READ = 10, + CMD_BURST_READ = 11, + CMD_RANDOM_WRITE = 12, + CMD_LED_MODE_OP = 16, + CMD_POWER_SAVING_OP = 20, + CMD_WOW_CONFIG = 21, + CMD_WOW_QUERY = 22, + CMD_WOW_FEATURE = 24, + CMD_CARRIER_DETECT_OP = 28, + CMD_RADOR_DETECT_OP = 29, + CMD_SWITCH_CHANNEL_OP = 30, + CMD_CALIBRATION_OP = 31, + CMD_BEACON_OP = 32, + CMD_ANTENNA_OP = 33, +}; + +enum mcu_function { + Q_SELECT = 1, + BW_SETTING = 2, + USB2_SW_DISCONNECT = 2, + USB3_SW_DISCONNECT = 3, + LOG_FW_DEBUG_MSG = 4, + GET_FW_VERSION = 5, +}; + +enum mcu_power_mode { + RADIO_OFF = 0x30, + RADIO_ON = 0x31, + RADIO_OFF_AUTO_WAKEUP = 0x32, + RADIO_OFF_ADVANCE = 0x33, + RADIO_ON_ADVANCE = 0x34, +}; + +enum mcu_calibration { + MCU_CAL_R = 1, + MCU_CAL_TEMP_SENSOR, + MCU_CAL_RXDCOC, + MCU_CAL_RC, + MCU_CAL_SX_LOGEN, + MCU_CAL_LC, + MCU_CAL_TX_LOFT, + MCU_CAL_TXIQ, + MCU_CAL_TSSI, + MCU_CAL_TSSI_COMP, + MCU_CAL_DPD, + MCU_CAL_RXIQC_FI, + MCU_CAL_RXIQC_FD, + MCU_CAL_PWRON, + MCU_CAL_TX_SHAPING, +}; + +enum mt76x2_mcu_cr_mode { + MT_RF_CR, + MT_BBP_CR, + MT_RF_BBP_CR, + MT_HL_TEMP_CR_UPDATE, +}; + +struct mt76x2_tssi_comp { + u8 pa_mode; + u8 cal_mode; + u16 pad; + + u8 slope0; + u8 slope1; + u8 offset0; + u8 offset1; +} __packed __aligned(4); + +int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, + u32 param); +int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, struct mt76x2_tssi_comp *tssi_data); +int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, + bool force); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c new file mode 100644 index 000000000000..e66f047ea448 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include +#include + +#include "mt76x2.h" +#include "mt76x2_trace.h" + +static const struct pci_device_id mt76pci_device_table[] = { + { PCI_DEVICE(0x14c3, 0x7662) }, + { PCI_DEVICE(0x14c3, 0x7612) }, + { PCI_DEVICE(0x14c3, 0x7602) }, + { }, +}; + +static int +mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct mt76x2_dev *dev; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + dev = mt76x2_alloc_device(&pdev->dev); + if (!dev) + return -ENOMEM; + + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); + + dev->mt76.rev = mt76_rr(dev, MT_ASIC_VERSION); + dev_info(dev->mt76.dev, "ASIC revision: %08x\n", dev->mt76.rev); + + ret = devm_request_irq(dev->mt76.dev, pdev->irq, mt76x2_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt76x2_register_device(dev); + if (ret) + goto error; + + /* Fix up ASPM configuration */ + + /* RG_SSUSB_G1_CDR_BIR_LTR = 0x9 */ + mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9); + + /* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */ + mt76_rmw_field(dev, 0x15a0c, 0xf << 28, 0xf); + + /* RG_SSUSB_CDR_BR_PE1D = 0x3 */ + mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3); + + return 0; + +error: + ieee80211_free_hw(mt76_hw(dev)); + return ret; +} + +static void +mt76pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + + mt76_unregister_device(mdev); + mt76x2_cleanup(dev); + ieee80211_free_hw(mdev->hw); +} + +MODULE_DEVICE_TABLE(pci, mt76pci_device_table); +MODULE_FIRMWARE(MT7662_FIRMWARE); +MODULE_FIRMWARE(MT7662_ROM_PATCH); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct pci_driver mt76pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76pci_device_table, + .probe = mt76pci_probe, + .remove = mt76pci_remove, +}; + +module_pci_driver(mt76pci_driver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c new file mode 100644 index 000000000000..126497172284 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c @@ -0,0 +1,758 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include "mt76x2.h" +#include "mt76x2_mcu.h" +#include "mt76x2_eeprom.h" + +static void +mt76x2_adjust_high_lna_gain(struct mt76x2_dev *dev, int reg, s8 offset) +{ + s8 gain; + + gain = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, mt76_rr(dev, MT_BBP(AGC, reg))); + gain -= offset / 2; + mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_LNA_HIGH_GAIN, gain); +} + +static void +mt76x2_adjust_agc_gain(struct mt76x2_dev *dev, int reg, s8 offset) +{ + s8 gain; + + gain = FIELD_GET(MT_BBP_AGC_GAIN, mt76_rr(dev, MT_BBP(AGC, reg))); + gain += offset; + mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_GAIN, gain); +} + +static void +mt76x2_apply_gain_adj(struct mt76x2_dev *dev) +{ + s8 *gain_adj = dev->cal.rx.high_gain; + + mt76x2_adjust_high_lna_gain(dev, 4, gain_adj[0]); + mt76x2_adjust_high_lna_gain(dev, 5, gain_adj[1]); + + mt76x2_adjust_agc_gain(dev, 8, gain_adj[0]); + mt76x2_adjust_agc_gain(dev, 9, gain_adj[1]); +} + +static u32 +mt76x2_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4) +{ + u32 val = 0; + + val |= (v1 & (BIT(6) - 1)) << 0; + val |= (v2 & (BIT(6) - 1)) << 8; + val |= (v3 & (BIT(6) - 1)) << 16; + val |= (v4 & (BIT(6) - 1)) << 24; + return val; +} + +int mt76x2_phy_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain) +{ + struct mt76x2_rx_freq_cal *cal = &dev->cal.rx; + + rssi += cal->rssi_offset[chain]; + rssi -= cal->lna_gain; + + return rssi; +} + +static u8 +mt76x2_txpower_check(int value) +{ + if (value < 0) + return 0; + if (value > 0x2f) + return 0x2f; + return value; +} + +static void +mt76x2_add_rate_power_offset(struct mt76_rate_power *r, int offset) +{ + int i; + + for (i = 0; i < sizeof(r->all); i++) + r->all[i] += offset; +} + +static void +mt76x2_limit_rate_power(struct mt76_rate_power *r, int limit) +{ + int i; + + for (i = 0; i < sizeof(r->all); i++) + if (r->all[i] > limit) + r->all[i] = limit; +} + +static int +mt76x2_get_max_power(struct mt76_rate_power *r) +{ + int i; + s8 ret = 0; + + for (i = 0; i < sizeof(r->all); i++) + ret = max(ret, r->all[i]); + + return ret; +} + +void mt76x2_phy_set_txpower(struct mt76x2_dev *dev) +{ + enum nl80211_chan_width width = dev->mt76.chandef.width; + struct mt76x2_tx_power_info txp; + int txp_0, txp_1, delta = 0; + struct mt76_rate_power t = {}; + + mt76x2_get_power_info(dev, &txp); + + if (width == NL80211_CHAN_WIDTH_40) + delta = txp.delta_bw40; + else if (width == NL80211_CHAN_WIDTH_80) + delta = txp.delta_bw80; + + if (txp.target_power > dev->txpower_conf) + delta -= txp.target_power - dev->txpower_conf; + + mt76x2_get_rate_power(dev, &t); + mt76x2_add_rate_power_offset(&t, txp.chain[0].target_power + + txp.chain[0].delta); + mt76x2_limit_rate_power(&t, dev->txpower_conf); + dev->txpower_cur = mt76x2_get_max_power(&t); + mt76x2_add_rate_power_offset(&t, -(txp.chain[0].target_power + + txp.chain[0].delta + delta)); + dev->target_power = txp.chain[0].target_power; + dev->target_power_delta[0] = txp.chain[0].delta + delta; + dev->target_power_delta[1] = txp.chain[1].delta + delta; + dev->rate_power = t; + + txp_0 = mt76x2_txpower_check(txp.chain[0].target_power + + txp.chain[0].delta + delta); + + txp_1 = mt76x2_txpower_check(txp.chain[1].target_power + + txp.chain[1].delta + delta); + + mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0); + mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1); + + mt76_wr(dev, MT_TX_PWR_CFG_0, + mt76x2_tx_power_mask(t.cck[0], t.cck[2], t.ofdm[0], t.ofdm[2])); + mt76_wr(dev, MT_TX_PWR_CFG_1, + mt76x2_tx_power_mask(t.ofdm[4], t.ofdm[6], t.ht[0], t.ht[2])); + mt76_wr(dev, MT_TX_PWR_CFG_2, + mt76x2_tx_power_mask(t.ht[4], t.ht[6], t.ht[8], t.ht[10])); + mt76_wr(dev, MT_TX_PWR_CFG_3, + mt76x2_tx_power_mask(t.ht[12], t.ht[14], t.ht[0], t.ht[2])); + mt76_wr(dev, MT_TX_PWR_CFG_4, + mt76x2_tx_power_mask(t.ht[4], t.ht[6], 0, 0)); + mt76_wr(dev, MT_TX_PWR_CFG_7, + mt76x2_tx_power_mask(t.ofdm[6], t.vht[8], t.ht[6], t.vht[8])); + mt76_wr(dev, MT_TX_PWR_CFG_8, + mt76x2_tx_power_mask(t.ht[14], t.vht[8], t.vht[8], 0)); + mt76_wr(dev, MT_TX_PWR_CFG_9, + mt76x2_tx_power_mask(t.ht[6], t.vht[8], t.vht[8], 0)); +} + +static bool +mt76x2_channel_silent(struct mt76x2_dev *dev) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + + return ((chan->flags & IEEE80211_CHAN_RADAR) && + chan->dfs_state != NL80211_DFS_AVAILABLE); +} + +static bool +mt76x2_phy_tssi_init_cal(struct mt76x2_dev *dev) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + u32 flag = 0; + + if (!mt76x2_tssi_enabled(dev)) + return false; + + if (mt76x2_channel_silent(dev)) + return false; + + if (chan->band == NL80211_BAND_2GHZ) + flag |= BIT(0); + + if (mt76x2_ext_pa_enabled(dev, chan->band)) + flag |= BIT(8); + + mt76x2_mcu_calibrate(dev, MCU_CAL_TSSI, flag); + dev->cal.tssi_cal_done = true; + return true; +} + +static void +mt76x2_phy_channel_calibrate(struct mt76x2_dev *dev, bool mac_stopped) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + bool is_5ghz = chan->band == NL80211_BAND_5GHZ; + + if (dev->cal.channel_cal_done) + return; + + if (mt76x2_channel_silent(dev)) + return; + + if (!dev->cal.tssi_cal_done) + mt76x2_phy_tssi_init_cal(dev); + + if (!mac_stopped) + mt76x2_mac_stop(dev, false); + + if (is_5ghz) + mt76x2_mcu_calibrate(dev, MCU_CAL_LC, 0); + + mt76x2_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz); + mt76x2_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); + mt76x2_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz); + mt76x2_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0); + mt76x2_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0); + + if (!mac_stopped) + mt76x2_mac_resume(dev); + + mt76x2_apply_gain_adj(dev); + + dev->cal.channel_cal_done = true; +} + +static void +mt76x2_phy_set_txpower_regs(struct mt76x2_dev *dev, enum nl80211_band band) +{ + u32 pa_mode[2]; + u32 pa_mode_adj; + + if (band == NL80211_BAND_2GHZ) { + pa_mode[0] = 0x010055ff; + pa_mode[1] = 0x00550055; + + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x35160a00); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x35160a06); + + if (mt76x2_ext_pa_enabled(dev, band)) { + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0x0000ec00); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0x0000ec00); + } else { + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0xf4000200); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0xfa000200); + } + } else { + pa_mode[0] = 0x0000ffff; + pa_mode[1] = 0x00ff00ff; + + if (mt76x2_ext_pa_enabled(dev, band)) { + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x2f0f0400); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x2f0f0476); + } else { + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x1b0f0400); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x1b0f0476); + } + mt76_wr(dev, MT_TX_ALC_CFG_4, 0); + + if (mt76x2_ext_pa_enabled(dev, band)) + pa_mode_adj = 0x04000000; + else + pa_mode_adj = 0; + + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, pa_mode_adj); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, pa_mode_adj); + } + + mt76_wr(dev, MT_BB_PA_MODE_CFG0, pa_mode[0]); + mt76_wr(dev, MT_BB_PA_MODE_CFG1, pa_mode[1]); + mt76_wr(dev, MT_RF_PA_MODE_CFG0, pa_mode[0]); + mt76_wr(dev, MT_RF_PA_MODE_CFG1, pa_mode[1]); + + if (mt76x2_ext_pa_enabled(dev, band)) { + u32 val; + + if (band == NL80211_BAND_2GHZ) + val = 0x3c3c023c; + else + val = 0x363c023c; + + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00001818); + } else { + if (band == NL80211_BAND_2GHZ) { + u32 val = 0x0f3c3c3c; + + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00000606); + } else { + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x383c023c); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, 0x24282e28); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0); + } + } +} + +static void +mt76x2_configure_tx_delay(struct mt76x2_dev *dev, enum nl80211_band band, u8 bw) +{ + u32 cfg0, cfg1; + + if (mt76x2_ext_pa_enabled(dev, band)) { + cfg0 = bw ? 0x000b0c01 : 0x00101101; + cfg1 = 0x00011414; + } else { + cfg0 = bw ? 0x000b0b01 : 0x00101001; + cfg1 = 0x00021414; + } + mt76_wr(dev, MT_TX_SW_CFG0, cfg0); + mt76_wr(dev, MT_TX_SW_CFG1, cfg1); + + mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_CCK_SIFS, + 13 + (bw ? 1 : 0)); +} + +static void +mt76x2_phy_set_bw(struct mt76x2_dev *dev, int width, u8 ctrl) +{ + int core_val, agc_val; + + switch (width) { + case NL80211_CHAN_WIDTH_80: + core_val = 3; + agc_val = 7; + break; + case NL80211_CHAN_WIDTH_40: + core_val = 2; + agc_val = 3; + break; + default: + core_val = 0; + agc_val = 1; + break; + } + + mt76_rmw_field(dev, MT_BBP(CORE, 1), MT_BBP_CORE_R1_BW, core_val); + mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_BW, agc_val); + mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_CTRL_CHAN, ctrl); + mt76_rmw_field(dev, MT_BBP(TXBE, 0), MT_BBP_TXBE_R0_CTRL_CHAN, ctrl); +} + +static void +mt76x2_phy_set_band(struct mt76x2_dev *dev, int band, bool primary_upper) +{ + switch (band) { + case NL80211_BAND_2GHZ: + mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G); + mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G); + break; + case NL80211_BAND_5GHZ: + mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G); + mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G); + break; + } + + mt76_rmw_field(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_UPPER_40M, + primary_upper); +} + +static void +mt76x2_set_rx_chains(struct mt76x2_dev *dev) +{ + u32 val; + + val = mt76_rr(dev, MT_BBP(AGC, 0)); + val &= ~(BIT(3) | BIT(4)); + + if (dev->chainmask & BIT(1)) + val |= BIT(3); + + mt76_wr(dev, MT_BBP(AGC, 0), val); +} + +static void +mt76x2_set_tx_dac(struct mt76x2_dev *dev) +{ + if (dev->chainmask & BIT(1)) + mt76_set(dev, MT_BBP(TXBE, 5), 3); + else + mt76_clear(dev, MT_BBP(TXBE, 5), 3); +} + +static void +mt76x2_get_agc_gain(struct mt76x2_dev *dev, u8 *dest) +{ + dest[0] = mt76_get_field(dev, MT_BBP(AGC, 8), MT_BBP_AGC_GAIN); + dest[1] = mt76_get_field(dev, MT_BBP(AGC, 9), MT_BBP_AGC_GAIN); +} + +static int +mt76x2_get_rssi_gain_thresh(struct mt76x2_dev *dev) +{ + switch (dev->mt76.chandef.width) { + case NL80211_CHAN_WIDTH_80: + return -62; + case NL80211_CHAN_WIDTH_40: + return -65; + default: + return -68; + } +} + +static int +mt76x2_get_low_rssi_gain_thresh(struct mt76x2_dev *dev) +{ + switch (dev->mt76.chandef.width) { + case NL80211_CHAN_WIDTH_80: + return -76; + case NL80211_CHAN_WIDTH_40: + return -79; + default: + return -82; + } +} + +static void +mt76x2_phy_set_gain_val(struct mt76x2_dev *dev) +{ + u32 val; + u8 gain_val[2]; + + gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust; + gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust; + + if (dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40) + val = 0x1e42 << 16; + else + val = 0x1836 << 16; + + val |= 0xf8; + + mt76_wr(dev, MT_BBP(AGC, 8), + val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[0])); + mt76_wr(dev, MT_BBP(AGC, 9), + val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[1])); + + if (dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR) + mt76x2_dfs_adjust_agc(dev); +} + +static void +mt76x2_phy_adjust_vga_gain(struct mt76x2_dev *dev) +{ + u32 false_cca; + u8 limit = dev->cal.low_gain > 1 ? 4 : 16; + + false_cca = FIELD_GET(MT_RX_STAT_1_CCA_ERRORS, mt76_rr(dev, MT_RX_STAT_1)); + if (false_cca > 800 && dev->cal.agc_gain_adjust < limit) + dev->cal.agc_gain_adjust += 2; + else if (false_cca < 10 && dev->cal.agc_gain_adjust > 0) + dev->cal.agc_gain_adjust -= 2; + else + return; + + mt76x2_phy_set_gain_val(dev); +} + +static void +mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev) +{ + u32 val = mt76_rr(dev, MT_BBP(AGC, 20)); + int rssi0 = (s8) FIELD_GET(MT_BBP_AGC20_RSSI0, val); + int rssi1 = (s8) FIELD_GET(MT_BBP_AGC20_RSSI1, val); + u8 *gain = dev->cal.agc_gain_init; + u8 gain_delta; + int low_gain; + + dev->cal.avg_rssi[0] = (dev->cal.avg_rssi[0] * 15) / 16 + (rssi0 << 8); + dev->cal.avg_rssi[1] = (dev->cal.avg_rssi[1] * 15) / 16 + (rssi1 << 8); + dev->cal.avg_rssi_all = (dev->cal.avg_rssi[0] + + dev->cal.avg_rssi[1]) / 512; + + low_gain = (dev->cal.avg_rssi_all > mt76x2_get_rssi_gain_thresh(dev)) + + (dev->cal.avg_rssi_all > mt76x2_get_low_rssi_gain_thresh(dev)); + + if (dev->cal.low_gain == low_gain) { + mt76x2_phy_adjust_vga_gain(dev); + return; + } + + dev->cal.low_gain = low_gain; + + if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) + mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211); + else + mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423); + + if (low_gain) { + mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991); + mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808); + mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808); + if (mt76x2_has_ext_lna(dev)) + gain_delta = 10; + else + gain_delta = 14; + } else { + mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990); + if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) + mt76_wr(dev, MT_BBP(AGC, 35), 0x10101014); + else + mt76_wr(dev, MT_BBP(AGC, 35), 0x11111116); + mt76_wr(dev, MT_BBP(AGC, 37), 0x2121262C); + gain_delta = 0; + } + + dev->cal.agc_gain_cur[0] = gain[0] - gain_delta; + dev->cal.agc_gain_cur[1] = gain[1] - gain_delta; + dev->cal.agc_gain_adjust = 0; + mt76x2_phy_set_gain_val(dev); +} + +int mt76x2_phy_set_channel(struct mt76x2_dev *dev, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_channel *chan = chandef->chan; + bool scan = test_bit(MT76_SCANNING, &dev->mt76.state); + enum nl80211_band band = chan->band; + u8 channel; + + u32 ext_cca_chan[4] = { + [0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)), + [1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)), + [2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)), + [3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)), + }; + int ch_group_index; + u8 bw, bw_index; + int freq, freq1; + int ret; + u8 sifs = 13; + + dev->cal.channel_cal_done = false; + freq = chandef->chan->center_freq; + freq1 = chandef->center_freq1; + channel = chan->hw_value; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = 1; + if (freq1 > freq) { + bw_index = 1; + ch_group_index = 0; + } else { + bw_index = 3; + ch_group_index = 1; + } + channel += 2 - ch_group_index * 4; + break; + case NL80211_CHAN_WIDTH_80: + ch_group_index = (freq - freq1 + 30) / 20; + if (WARN_ON(ch_group_index < 0 || ch_group_index > 3)) + ch_group_index = 0; + bw = 2; + bw_index = ch_group_index; + channel += 6 - ch_group_index * 4; + break; + default: + bw = 0; + bw_index = 0; + ch_group_index = 0; + break; + } + + mt76x2_read_rx_gain(dev); + mt76x2_phy_set_txpower_regs(dev, band); + mt76x2_configure_tx_delay(dev, band, bw); + mt76x2_phy_set_txpower(dev); + + mt76x2_set_rx_chains(dev); + mt76x2_phy_set_band(dev, chan->band, ch_group_index & 1); + mt76x2_phy_set_bw(dev, chandef->width, ch_group_index); + mt76x2_set_tx_dac(dev); + + mt76_rmw(dev, MT_EXT_CCA_CFG, + (MT_EXT_CCA_CFG_CCA0 | + MT_EXT_CCA_CFG_CCA1 | + MT_EXT_CCA_CFG_CCA2 | + MT_EXT_CCA_CFG_CCA3 | + MT_EXT_CCA_CFG_CCA_MASK), + ext_cca_chan[ch_group_index]); + + if (chandef->width >= NL80211_CHAN_WIDTH_40) + sifs++; + + mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, sifs); + + ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan); + if (ret) + return ret; + + mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true); + + /* Enable LDPC Rx */ + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + mt76_set(dev, MT_BBP(RXO, 13), BIT(10)); + + if (!dev->cal.init_cal_done) { + u8 val = mt76x2_eeprom_get(dev, MT_EE_BT_RCAL_RESULT); + + if (val != 0xff) + mt76x2_mcu_calibrate(dev, MCU_CAL_R, 0); + } + + mt76x2_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel); + + /* Rx LPF calibration */ + if (!dev->cal.init_cal_done) + mt76x2_mcu_calibrate(dev, MCU_CAL_RC, 0); + + dev->cal.init_cal_done = true; + + mt76_wr(dev, MT_BBP(AGC, 61), 0xFF64A4E2); + mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010); + mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404); + mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070); + mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x04101B3F); + + if (scan) + return 0; + + dev->cal.low_gain = -1; + mt76x2_phy_channel_calibrate(dev, true); + mt76x2_get_agc_gain(dev, dev->cal.agc_gain_init); + memcpy(dev->cal.agc_gain_cur, dev->cal.agc_gain_init, + sizeof(dev->cal.agc_gain_cur)); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); + + return 0; +} + +static void +mt76x2_phy_tssi_compensate(struct mt76x2_dev *dev) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + struct mt76x2_tx_power_info txp; + struct mt76x2_tssi_comp t = {}; + + if (!dev->cal.tssi_cal_done) + return; + + if (!dev->cal.tssi_comp_pending) { + /* TSSI trigger */ + t.cal_mode = BIT(0); + mt76x2_mcu_tssi_comp(dev, &t); + dev->cal.tssi_comp_pending = true; + } else { + if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)) + return; + + dev->cal.tssi_comp_pending = false; + mt76x2_get_power_info(dev, &txp); + + if (mt76x2_ext_pa_enabled(dev, chan->band)) + t.pa_mode = 1; + + t.cal_mode = BIT(1); + t.slope0 = txp.chain[0].tssi_slope; + t.offset0 = txp.chain[0].tssi_offset; + t.slope1 = txp.chain[1].tssi_slope; + t.offset1 = txp.chain[1].tssi_offset; + mt76x2_mcu_tssi_comp(dev, &t); + + if (t.pa_mode || dev->cal.dpd_cal_done) + return; + + usleep_range(10000, 20000); + mt76x2_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value); + dev->cal.dpd_cal_done = true; + } +} + +static void +mt76x2_phy_temp_compensate(struct mt76x2_dev *dev) +{ + struct mt76x2_temp_comp t; + int temp, db_diff; + + if (mt76x2_get_temp_comp(dev, &t)) + return; + + temp = mt76_get_field(dev, MT_TEMP_SENSOR, MT_TEMP_SENSOR_VAL); + temp -= t.temp_25_ref; + temp = (temp * 1789) / 1000 + 25; + dev->cal.temp = temp; + + if (temp > 25) + db_diff = (temp - 25) / t.high_slope; + else + db_diff = (25 - temp) / t.low_slope; + + db_diff = min(db_diff, t.upper_bound); + db_diff = max(db_diff, t.lower_bound); + + mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, + db_diff * 2); + mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP, + db_diff * 2); +} + +void mt76x2_phy_calibrate(struct work_struct *work) +{ + struct mt76x2_dev *dev; + + dev = container_of(work, struct mt76x2_dev, cal_work.work); + mt76x2_phy_channel_calibrate(dev, false); + mt76x2_phy_tssi_compensate(dev); + mt76x2_phy_temp_compensate(dev); + mt76x2_phy_update_channel_gain(dev); + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); +} + +int mt76x2_phy_start(struct mt76x2_dev *dev) +{ + int ret; + + ret = mt76x2_mcu_set_radio_state(dev, true); + if (ret) + return ret; + + mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0); + + return ret; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h new file mode 100644 index 000000000000..ce3ab85c8b0f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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_REGS_H +#define __MT76x2_REGS_H + +#define MT_ASIC_VERSION 0x0000 + +#define MT76XX_REV_E3 0x22 +#define MT76XX_REV_E4 0x33 + +#define MT_CMB_CTRL 0x0020 +#define MT_CMB_CTRL_XTAL_RDY BIT(22) +#define MT_CMB_CTRL_PLL_LD BIT(23) + +#define MT_EFUSE_CTRL 0x0024 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_DATA_BASE 0x0028 +#define MT_EFUSE_DATA(_n) (MT_EFUSE_DATA_BASE + ((_n) << 2)) + +#define MT_COEXCFG0 0x0040 +#define MT_COEXCFG0_COEX_EN BIT(0) + +#define MT_WLAN_FUN_CTRL 0x0080 +#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0) +#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) +#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) + +#define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4) +#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5) +#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6) +#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7) + +#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) /* MT76x2 */ +#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_GPIO_IN GENMASK(15, 8) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */ + +#define MT_XO_CTRL0 0x0100 +#define MT_XO_CTRL1 0x0104 +#define MT_XO_CTRL2 0x0108 +#define MT_XO_CTRL3 0x010c +#define MT_XO_CTRL4 0x0110 + +#define MT_XO_CTRL5 0x0114 +#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8) + +#define MT_XO_CTRL6 0x0118 +#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8) + +#define MT_XO_CTRL7 0x011c + +#define MT_WLAN_MTC_CTRL 0x10148 +#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0) +#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12) +#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13) +#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16) +#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20) +#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21) +#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22) +#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24) +#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25) +#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26) +#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27) +#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28) + +#define MT_INT_SOURCE_CSR 0x0200 +#define MT_INT_MASK_CSR 0x0204 + +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(13, 4) +#define MT_INT_TX_DONE(_n) BIT(_n + 4) +#define MT_INT_RX_COHERENT BIT(16) +#define MT_INT_TX_COHERENT BIT(17) +#define MT_INT_ANY_COHERENT BIT(18) +#define MT_INT_MCU_CMD BIT(19) +#define MT_INT_TBTT BIT(20) +#define MT_INT_PRE_TBTT BIT(21) +#define MT_INT_TX_STAT BIT(22) +#define MT_INT_AUTO_WAKEUP BIT(23) +#define MT_INT_GPTIMER BIT(24) +#define MT_INT_RXDELAYINT BIT(26) +#define MT_INT_TXDELAYINT BIT(27) + +#define MT_WPDMA_GLO_CFG 0x0208 +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX 0x020c + +#define MT_WPDMA_DELAY_INT_CFG 0x0210 + +#define MT_WMM_AIFSN 0x0214 +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMIN 0x0218 +#define MT_WMM_CWMIN_MASK GENMASK(3, 0) +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMAX 0x021c +#define MT_WMM_CWMAX_MASK GENMASK(3, 0) +#define MT_WMM_CWMAX_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_TXOP_BASE 0x0220 +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + (((_n) / 2) << 2)) +#define MT_WMM_TXOP_SHIFT(_n) ((_n & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_TSO_CTRL 0x0250 +#define MT_HEADER_TRANS_CTRL_REG 0x0260 + +#define MT_TX_RING_BASE 0x0300 +#define MT_RX_RING_BASE 0x03c0 + +#define MT_TX_HW_QUEUE_MCU 8 +#define MT_TX_HW_QUEUE_MGMT 9 + +#define MT_PBF_SYS_CTRL 0x0400 +#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) +#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) +#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2) +#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3) +#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4) + +#define MT_PBF_CFG 0x0404 +#define MT_PBF_CFG_TX0Q_EN BIT(0) +#define MT_PBF_CFG_TX1Q_EN BIT(1) +#define MT_PBF_CFG_TX2Q_EN BIT(2) +#define MT_PBF_CFG_TX3Q_EN BIT(3) +#define MT_PBF_CFG_RX0Q_EN BIT(4) +#define MT_PBF_CFG_RX_DROP_EN BIT(8) + +#define MT_PBF_TX_MAX_PCNT 0x0408 +#define MT_PBF_RX_MAX_PCNT 0x040c + +#define MT_BCN_OFFSET_BASE 0x041c +#define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) + +#define MT_RF_BYPASS_0 0x0504 +#define MT_RF_BYPASS_1 0x0508 +#define MT_RF_SETTING_0 0x050c + +#define MT_RF_DATA_WRITE 0x0524 + +#define MT_RF_CTRL 0x0528 +#define MT_RF_CTRL_ADDR GENMASK(11, 0) +#define MT_RF_CTRL_WRITE BIT(12) +#define MT_RF_CTRL_BUSY BIT(13) +#define MT_RF_CTRL_IDX BIT(16) + +#define MT_RF_DATA_READ 0x052c + +#define MT_FCE_PSE_CTRL 0x0800 +#define MT_FCE_PARAMETERS 0x0804 +#define MT_FCE_CSO 0x0808 + +#define MT_FCE_L2_STUFF 0x080c +#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0) +#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1) +#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2) +#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3) +#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4) +#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5) +#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8) +#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16) +#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24) + +#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824 + +#define MT_PAUSE_ENABLE_CONTROL1 0x0a38 + +#define MT_MAC_CSR0 0x1000 + +#define MT_MAC_SYS_CTRL 0x1004 +#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0) +#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1) +#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2) +#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3) + +#define MT_MAC_ADDR_DW0 0x1008 +#define MT_MAC_ADDR_DW1 0x100c + +#define MT_MAC_BSSID_DW0 0x1010 +#define MT_MAC_BSSID_DW1 0x1014 +#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0) +#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16) +#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18) +#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21) +#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22) +#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23) +#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) + +#define MT_MAX_LEN_CFG 0x1018 + +#define MT_AMPDU_MAX_LEN_20M1S 0x1030 +#define MT_AMPDU_MAX_LEN_20M2S 0x1034 +#define MT_AMPDU_MAX_LEN_40M1S 0x1038 +#define MT_AMPDU_MAX_LEN_40M2S 0x103c +#define MT_AMPDU_MAX_LEN 0x1040 + +#define MT_WCID_DROP_BASE 0x106c +#define MT_WCID_DROP(_n) (MT_WCID_DROP_BASE + ((_n) >> 5) * 4) +#define MT_WCID_DROP_MASK(_n) BIT((_n) % 32) + +#define MT_BCN_BYPASS_MASK 0x108c + +#define MT_MAC_APC_BSSID_BASE 0x1090 +#define MT_MAC_APC_BSSID_L(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8)) +#define MT_MAC_APC_BSSID_H(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8 + 4)) +#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0) +#define MT_MAC_APC_BSSID0_H_EN BIT(16) + +#define MT_XIFS_TIME_CFG 0x1100 +#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0) +#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8) +#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16) +#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20) +#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29) + +#define MT_BKOFF_SLOT_CFG 0x1104 +#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0) +#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8) + +#define MT_CH_TIME_CFG 0x110c +#define MT_CH_TIME_CFG_TIMER_EN BIT(0) +#define MT_CH_TIME_CFG_TX_AS_BUSY BIT(1) +#define MT_CH_TIME_CFG_RX_AS_BUSY BIT(2) +#define MT_CH_TIME_CFG_NAV_AS_BUSY BIT(3) +#define MT_CH_TIME_CFG_EIFS_AS_BUSY BIT(4) +#define MT_CH_TIME_CFG_MDRDY_CNT_EN BIT(5) +#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) +#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17) +#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19) +#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20) +#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24) + +#define MT_TBTT_SYNC_CFG 0x1118 +#define MT_TBTT_TIMER_CFG 0x1124 + +#define MT_INT_TIMER_CFG 0x1128 +#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0) +#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16) + +#define MT_INT_TIMER_EN 0x112c +#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0) +#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1) + +#define MT_CH_IDLE 0x1130 +#define MT_CH_BUSY 0x1134 +#define MT_EXT_CH_BUSY 0x1138 +#define MT_ED_CCA_TIMER 0x1140 + +#define MT_MAC_STATUS 0x1200 +#define MT_MAC_STATUS_TX BIT(0) +#define MT_MAC_STATUS_RX BIT(1) + +#define MT_PWR_PIN_CFG 0x1204 +#define MT_AUX_CLK_CFG 0x120c + +#define MT_BB_PA_MODE_CFG0 0x1214 +#define MT_BB_PA_MODE_CFG1 0x1218 +#define MT_RF_PA_MODE_CFG0 0x121c +#define MT_RF_PA_MODE_CFG1 0x1220 + +#define MT_RF_PA_MODE_ADJ0 0x1228 +#define MT_RF_PA_MODE_ADJ1 0x122c + +#define MT_DACCLK_EN_DLY_CFG 0x1264 + +#define MT_EDCA_CFG_BASE 0x1300 +#define MT_EDCA_CFG_AC(_n) (MT_EDCA_CFG_BASE + ((_n) << 2)) +#define MT_EDCA_CFG_TXOP GENMASK(7, 0) +#define MT_EDCA_CFG_AIFSN GENMASK(11, 8) +#define MT_EDCA_CFG_CWMIN GENMASK(15, 12) +#define MT_EDCA_CFG_CWMAX GENMASK(19, 16) + +#define MT_TX_PWR_CFG_0 0x1314 +#define MT_TX_PWR_CFG_1 0x1318 +#define MT_TX_PWR_CFG_2 0x131c +#define MT_TX_PWR_CFG_3 0x1320 +#define MT_TX_PWR_CFG_4 0x1324 + +#define MT_TX_BAND_CFG 0x132c +#define MT_TX_BAND_CFG_UPPER_40M BIT(0) +#define MT_TX_BAND_CFG_5G BIT(1) +#define MT_TX_BAND_CFG_2G BIT(2) + +#define MT_HT_FBK_TO_LEGACY 0x1384 +#define MT_TX_MPDU_ADJ_INT 0x1388 + +#define MT_TX_PWR_CFG_7 0x13d4 +#define MT_TX_PWR_CFG_8 0x13d8 +#define MT_TX_PWR_CFG_9 0x13dc + +#define MT_TX_SW_CFG0 0x1330 +#define MT_TX_SW_CFG1 0x1334 +#define MT_TX_SW_CFG2 0x1338 + +#define MT_TXOP_CTRL_CFG 0x1340 + +#define MT_TX_RTS_CFG 0x1344 +#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) +#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8) +#define MT_TX_RTS_FALLBACK BIT(24) + +#define MT_TX_TIMEOUT_CFG 0x1348 +#define MT_TX_TIMEOUT_CFG_ACKTO GENMASK(15, 8) + +#define MT_TX_RETRY_CFG 0x134c +#define MT_VHT_HT_FBK_CFG1 0x1358 + +#define MT_PROT_CFG_RATE GENMASK(15, 0) +#define MT_PROT_CFG_CTRL GENMASK(17, 16) +#define MT_PROT_CFG_NAV GENMASK(19, 18) +#define MT_PROT_CFG_TXOP_ALLOW GENMASK(25, 20) +#define MT_PROT_CFG_RTS_THRESH BIT(26) + +#define MT_CCK_PROT_CFG 0x1364 +#define MT_OFDM_PROT_CFG 0x1368 +#define MT_MM20_PROT_CFG 0x136c +#define MT_MM40_PROT_CFG 0x1370 +#define MT_GF20_PROT_CFG 0x1374 +#define MT_GF40_PROT_CFG 0x1378 + +#define MT_EXP_ACK_TIME 0x1380 + +#define MT_TX_PWR_CFG_0_EXT 0x1390 +#define MT_TX_PWR_CFG_1_EXT 0x1394 + +#define MT_TX_FBK_LIMIT 0x1398 +#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0) +#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8) +#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16) +#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17) +#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18) + +#define MT_TX0_RF_GAIN_CORR 0x13a0 +#define MT_TX1_RF_GAIN_CORR 0x13a4 + +#define MT_TX_ALC_CFG_0 0x13b0 +#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) +#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8) +#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16) +#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24) + +#define MT_TX_ALC_CFG_1 0x13b4 +#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_2 0x13a8 +#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_3 0x13ac +#define MT_TX_ALC_CFG_4 0x13c0 +#define MT_TX_ALC_CFG_4_LOWGAIN_CH_EN BIT(31) + +#define MT_TX_ALC_VGA3 0x13c8 + +#define MT_TX_PROT_CFG6 0x13e0 +#define MT_TX_PROT_CFG7 0x13e4 +#define MT_TX_PROT_CFG8 0x13e8 + +#define MT_PIFS_TX_CFG 0x13ec + +#define MT_RX_FILTR_CFG 0x1400 + +#define MT_RX_FILTR_CFG_CRC_ERR BIT(0) +#define MT_RX_FILTR_CFG_PHY_ERR BIT(1) +#define MT_RX_FILTR_CFG_PROMISC BIT(2) +#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3) +#define MT_RX_FILTR_CFG_VER_ERR BIT(4) +#define MT_RX_FILTR_CFG_MCAST BIT(5) +#define MT_RX_FILTR_CFG_BCAST BIT(6) +#define MT_RX_FILTR_CFG_DUP BIT(7) +#define MT_RX_FILTR_CFG_CFACK BIT(8) +#define MT_RX_FILTR_CFG_CFEND BIT(9) +#define MT_RX_FILTR_CFG_ACK BIT(10) +#define MT_RX_FILTR_CFG_CTS BIT(11) +#define MT_RX_FILTR_CFG_RTS BIT(12) +#define MT_RX_FILTR_CFG_PSPOLL BIT(13) +#define MT_RX_FILTR_CFG_BA BIT(14) +#define MT_RX_FILTR_CFG_BAR BIT(15) +#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) + +#define MT_LEGACY_BASIC_RATE 0x1408 +#define MT_HT_BASIC_RATE 0x140c + +#define MT_HT_CTRL_CFG 0x1410 + +#define MT_EXT_CCA_CFG 0x141c +#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) +#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2) +#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4) +#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6) +#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8) +#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12) + +#define MT_TX_SW_CFG3 0x1478 + +#define MT_PN_PAD_MODE 0x150c + +#define MT_TXOP_HLDR_ET 0x1608 + +#define MT_PROT_AUTO_TX_CFG 0x1648 +#define MT_PROT_AUTO_TX_CFG_PROT_PADJ GENMASK(11, 8) +#define MT_PROT_AUTO_TX_CFG_AUTO_PADJ GENMASK(27, 24) + +#define MT_RX_STAT_0 0x1700 +#define MT_RX_STAT_0_CRC_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_0_PHY_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_1 0x1704 +#define MT_RX_STAT_1_CCA_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_1_PLCP_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_2 0x1708 +#define MT_RX_STAT_2_DUP_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_2_OVERFLOW_ERRORS GENMASK(31, 16) + +#define MT_TX_STAT_FIFO 0x1718 +#define MT_TX_STAT_FIFO_VALID BIT(0) +#define MT_TX_STAT_FIFO_SUCCESS BIT(5) +#define MT_TX_STAT_FIFO_AGGR BIT(6) +#define MT_TX_STAT_FIFO_ACKREQ BIT(7) +#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) +#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) + +#define MT_TX_AGG_CNT_BASE0 0x1720 +#define MT_TX_AGG_CNT_BASE1 0x174c + +#define MT_TX_AGG_CNT(_id) ((_id) < 8 ? \ + MT_TX_AGG_CNT_BASE0 + ((_id) << 2) : \ + MT_TX_AGG_CNT_BASE1 + ((_id - 8) << 2)) + +#define MT_TX_STAT_FIFO_EXT 0x1798 +#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0) +#define MT_TX_STAT_FIFO_EXT_PKTID GENMASK(15, 8) + +#define MT_WCID_TX_RATE_BASE 0x1c00 +#define MT_WCID_TX_RATE(_i) (MT_WCID_TX_RATE_BASE + ((_i) << 3)) + +#define MT_BBP_CORE_BASE 0x2000 +#define MT_BBP_IBI_BASE 0x2100 +#define MT_BBP_AGC_BASE 0x2300 +#define MT_BBP_TXC_BASE 0x2400 +#define MT_BBP_RXC_BASE 0x2500 +#define MT_BBP_TXO_BASE 0x2600 +#define MT_BBP_TXBE_BASE 0x2700 +#define MT_BBP_RXFE_BASE 0x2800 +#define MT_BBP_RXO_BASE 0x2900 +#define MT_BBP_DFS_BASE 0x2a00 +#define MT_BBP_TR_BASE 0x2b00 +#define MT_BBP_CAL_BASE 0x2c00 +#define MT_BBP_DSC_BASE 0x2e00 +#define MT_BBP_PFMU_BASE 0x2f00 + +#define MT_BBP(_type, _n) (MT_BBP_##_type##_BASE + ((_n) << 2)) + +#define MT_BBP_CORE_R1_BW GENMASK(4, 3) + +#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8) +#define MT_BBP_AGC_R0_BW GENMASK(14, 12) + +/* AGC, R4/R5 */ +#define MT_BBP_AGC_LNA_HIGH_GAIN GENMASK(21, 16) +#define MT_BBP_AGC_LNA_MID_GAIN GENMASK(13, 8) +#define MT_BBP_AGC_LNA_LOW_GAIN GENMASK(5, 0) + +/* AGC, R6/R7 */ +#define MT_BBP_AGC_LNA_ULOW_GAIN GENMASK(5, 0) + +/* AGC, R8/R9 */ +#define MT_BBP_AGC_LNA_GAIN_MODE GENMASK(7, 6) +#define MT_BBP_AGC_GAIN GENMASK(14, 8) + +#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0) +#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8) + +#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0) + +#define MT_WCID_ADDR_BASE 0x1800 +#define MT_WCID_ADDR(_n) (MT_WCID_ADDR_BASE + (_n) * 8) + +#define MT_SRAM_BASE 0x4000 + +#define MT_WCID_KEY_BASE 0x8000 +#define MT_WCID_KEY(_n) (MT_WCID_KEY_BASE + (_n) * 32) + +#define MT_WCID_IV_BASE 0xa000 +#define MT_WCID_IV(_n) (MT_WCID_IV_BASE + (_n) * 8) + +#define MT_WCID_ATTR_BASE 0xa800 +#define MT_WCID_ATTR(_n) (MT_WCID_ATTR_BASE + (_n) * 4) + +#define MT_WCID_ATTR_PAIRWISE BIT(0) +#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1) +#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4) +#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7) +#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10) +#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11) +#define MT_WCID_ATTR_WAPI_MCBC BIT(15) +#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24) + +#define MT_SKEY_BASE_0 0xac00 +#define MT_SKEY_BASE_1 0xb400 +#define MT_SKEY_0(_bss, _idx) (MT_SKEY_BASE_0 + (4 * (_bss) + _idx) * 32) +#define MT_SKEY_1(_bss, _idx) (MT_SKEY_BASE_1 + (4 * ((_bss) & 7) + _idx) * 32) +#define MT_SKEY(_bss, _idx) ((_bss & 8) ? MT_SKEY_1(_bss, _idx) : MT_SKEY_0(_bss, _idx)) + +#define MT_SKEY_MODE_BASE_0 0xb000 +#define MT_SKEY_MODE_BASE_1 0xb3f0 +#define MT_SKEY_MODE_0(_bss) (MT_SKEY_MODE_BASE_0 + ((_bss / 2) << 2)) +#define MT_SKEY_MODE_1(_bss) (MT_SKEY_MODE_BASE_1 + ((((_bss) & 7) / 2) << 2)) +#define MT_SKEY_MODE(_bss) ((_bss & 8) ? MT_SKEY_MODE_1(_bss) : MT_SKEY_MODE_0(_bss)) +#define MT_SKEY_MODE_MASK GENMASK(3, 0) +#define MT_SKEY_MODE_SHIFT(_bss, _idx) (4 * ((_idx) + 4 * (_bss & 1))) + +#define MT_BEACON_BASE 0xc000 + +#define MT_TEMP_SENSOR 0x1d000 +#define MT_TEMP_SENSOR_VAL GENMASK(6, 0) + +struct mt76_wcid_addr { + u8 macaddr[6]; + __le16 ba_mask; +} __packed __aligned(4); + +struct mt76_wcid_key { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed __aligned(4); + +enum mt76x2_cipher_type { + MT_CIPHER_NONE, + MT_CIPHER_WEP40, + MT_CIPHER_WEP104, + MT_CIPHER_TKIP, + MT_CIPHER_AES_CCMP, + MT_CIPHER_CKIP40, + MT_CIPHER_CKIP104, + MT_CIPHER_CKIP128, + MT_CIPHER_WAPI, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_trace.c b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.c new file mode 100644 index 000000000000..a09f117848d6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "mt76x2_trace.h" + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h new file mode 100644 index 000000000000..4cd424148d4b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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. + */ + +#if !defined(__MT76x2_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT76x2_TRACE_H + +#include +#include "mt76x2.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt76x2 + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(mt76_hw(dev)->wiphy), MAXNAME) +#define DEV_PR_FMT "%s" +#define DEV_PR_ARG __entry->wiphy_name + +#define TXID_ENTRY __field(u8, wcid) __field(u8, pktid) +#define TXID_ASSIGN __entry->wcid = wcid; __entry->pktid = pktid +#define TXID_PR_FMT " [%d:%d]" +#define TXID_PR_ARG __entry->wcid, __entry->pktid + +DECLARE_EVENT_CLASS(dev_evt, + TP_PROTO(struct mt76x2_dev *dev), + TP_ARGS(dev), + TP_STRUCT__entry( + DEV_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + ), + TP_printk(DEV_PR_FMT, DEV_PR_ARG) +); + +DECLARE_EVENT_CLASS(dev_txid_evt, + TP_PROTO(struct mt76x2_dev *dev, u8 wcid, u8 pktid), + TP_ARGS(dev, wcid, pktid), + TP_STRUCT__entry( + DEV_ENTRY + TXID_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + TXID_ASSIGN; + ), + TP_printk( + DEV_PR_FMT TXID_PR_FMT, + DEV_PR_ARG, TXID_PR_ARG + ) +); + +DEFINE_EVENT(dev_evt, mac_txstat_poll, + TP_PROTO(struct mt76x2_dev *dev), + TP_ARGS(dev) +); + +DEFINE_EVENT(dev_txid_evt, mac_txdone_add, + TP_PROTO(struct mt76x2_dev *dev, u8 wcid, u8 pktid), + TP_ARGS(dev, wcid, pktid) +); + +TRACE_EVENT(mac_txstat_fetch, + TP_PROTO(struct mt76x2_dev *dev, + struct mt76x2_tx_status *stat), + + TP_ARGS(dev, stat), + + TP_STRUCT__entry( + DEV_ENTRY + TXID_ENTRY + __field(bool, success) + __field(bool, aggr) + __field(bool, ack_req) + __field(u16, rate) + __field(u8, retry) + ), + + TP_fast_assign( + DEV_ASSIGN; + __entry->success = stat->success; + __entry->aggr = stat->aggr; + __entry->ack_req = stat->ack_req; + __entry->wcid = stat->wcid; + __entry->pktid = stat->pktid; + __entry->rate = stat->rate; + __entry->retry = stat->retry; + ), + + TP_printk( + DEV_PR_FMT TXID_PR_FMT + " success:%d aggr:%d ack_req:%d" + " rate:%04x retry:%d", + DEV_PR_ARG, TXID_PR_ARG, + __entry->success, __entry->aggr, __entry->ack_req, + __entry->rate, __entry->retry + ) +); + + +TRACE_EVENT(dev_irq, + TP_PROTO(struct mt76x2_dev *dev, u32 val, u32 mask), + + TP_ARGS(dev, val, mask), + + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, val) + __field(u32, mask) + ), + + TP_fast_assign( + DEV_ASSIGN; + __entry->val = val; + __entry->mask = mask; + ), + + TP_printk( + DEV_PR_FMT " %08x & %08x", + DEV_PR_ARG, __entry->val, __entry->mask + ) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mt76x2_trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c new file mode 100644 index 000000000000..1a32e1fb8743 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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" +#include "mt76x2_dma.h" + +struct beacon_bc_data { + struct mt76x2_dev *dev; + struct sk_buff_head q; + struct sk_buff *tail[8]; +}; + +void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76x2_dev *dev = hw->priv; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->global_wcid; + + if (control->sta) { + struct mt76x2_sta *msta; + + msta = (struct mt76x2_sta *) control->sta->drv_priv; + wcid = &msta->wcid; + } else if (vif) { + struct mt76x2_vif *mvif; + + mvif = (struct mt76x2_vif *) vif->drv_priv; + wcid = &mvif->group_wcid; + } + + mt76_tx(&dev->mt76, control->sta, wcid, skb); +} + +void mt76x2_tx_complete(struct mt76x2_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + ieee80211_free_txskb(mt76_hw(dev), skb); + } else { + ieee80211_tx_info_clear_status(info); + info->status.rates[0].idx = -1; + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status(mt76_hw(dev), skb); + } +} + +s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev, + const struct ieee80211_tx_rate *rate) +{ + s8 max_txpwr; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + u8 mcs = ieee80211_rate_get_vht_mcs(rate); + + if (mcs == 8 || mcs == 9) { + max_txpwr = dev->rate_power.vht[8]; + } else { + u8 nss, idx; + + nss = ieee80211_rate_get_vht_nss(rate); + idx = ((nss - 1) << 3) + mcs; + max_txpwr = dev->rate_power.ht[idx & 0xf]; + } + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + max_txpwr = dev->rate_power.ht[rate->idx & 0xf]; + } else { + enum nl80211_band band = dev->mt76.chandef.chan->band; + + if (band == NL80211_BAND_2GHZ) { + const struct ieee80211_rate *r; + struct wiphy *wiphy = mt76_hw(dev)->wiphy; + struct mt76_rate_power *rp = &dev->rate_power; + + r = &wiphy->bands[band]->bitrates[rate->idx]; + if (r->flags & IEEE80211_RATE_SHORT_PREAMBLE) + max_txpwr = rp->cck[r->hw_value & 0x3]; + else + max_txpwr = rp->ofdm[r->hw_value & 0x7]; + } else { + max_txpwr = dev->rate_power.ofdm[rate->idx & 0x7]; + } + } + + return max_txpwr; +} + +s8 mt76x2_tx_get_txpwr_adj(struct mt76x2_dev *dev, s8 txpwr, s8 max_txpwr_adj) +{ + txpwr = min_t(s8, txpwr, dev->txpower_conf); + txpwr -= (dev->target_power + dev->target_power_delta[0]); + txpwr = min_t(s8, txpwr, max_txpwr_adj); + + if (!dev->enable_tpc) + return 0; + else if (txpwr >= 0) + return min_t(s8, txpwr, 7); + else + return (txpwr < -16) ? 8 : (txpwr + 32) / 2; +} + +void mt76x2_tx_set_txpwr_auto(struct mt76x2_dev *dev, s8 txpwr) +{ + s8 txpwr_adj; + + txpwr_adj = mt76x2_tx_get_txpwr_adj(dev, txpwr, + dev->rate_power.ofdm[4]); + mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, + MT_PROT_AUTO_TX_CFG_PROT_PADJ, txpwr_adj); + mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, + MT_PROT_AUTO_TX_CFG_AUTO_PADJ, txpwr_adj); +} + +static int mt76x2_insert_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + + if (len % 4 == 0) + return 0; + + skb_push(skb, 2); + memmove(skb->data, skb->data + 2, len); + + skb->data[len] = 0; + skb->data[len + 1] = 0; + return 2; +} + +int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info) +{ + struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int qsel = MT_QSEL_EDCA; + int ret; + + if (q == &dev->mt76.q_tx[MT_TXQ_PSD] && wcid && wcid->idx < 128) + mt76x2_mac_wcid_set_drop(dev, wcid->idx, false); + + mt76x2_mac_write_txwi(dev, txwi, skb, wcid, sta); + + ret = mt76x2_insert_hdr_pad(skb); + if (ret < 0) + return ret; + + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + qsel = MT_QSEL_MGMT; + + *tx_info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | + MT_TXD_INFO_80211; + + if (!wcid || wcid->hw_key_idx == 0xff) + *tx_info |= MT_TXD_INFO_WIV; + + return 0; +} + +static void +mt76x2_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76x2_dev *dev = (struct mt76x2_dev *) priv; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct sk_buff *skb = NULL; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_beacon_get(mt76_hw(dev), vif); + if (!skb) + return; + + mt76x2_mac_set_beacon(dev, mvif->idx, skb); +} + +static void +mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt76x2_dev *dev = data->dev; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + mt76_skb_set_moredata(skb, true); + __skb_queue_tail(&data->q, skb); + data->tail[mvif->idx] = skb; +} + +void mt76x2_pre_tbtt_tasklet(unsigned long arg) +{ + struct mt76x2_dev *dev = (struct mt76x2_dev *) arg; + struct mt76_queue *q = &dev->mt76.q_tx[MT_TXQ_PSD]; + struct beacon_bc_data data = {}; + struct sk_buff *skb; + int i, nframes; + + data.dev = dev; + __skb_queue_head_init(&data.q); + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x2_update_beacon_iter, dev); + + do { + nframes = skb_queue_len(&data.q); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x2_add_buffered_bc, &data); + } while (nframes != skb_queue_len(&data.q)); + + if (!nframes) + return; + + for (i = 0; i < ARRAY_SIZE(data.tail); i++) { + if (!data.tail[i]) + continue; + + mt76_skb_set_moredata(data.tail[i], false); + } + + spin_lock_bh(&q->lock); + while ((skb = __skb_dequeue(&data.q)) != NULL) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + + mt76_tx_queue_skb(&dev->mt76, q, skb, &mvif->group_wcid, NULL); + } + spin_unlock_bh(&q->lock); +} +