* [PATCH] nl80211: add HT/VHT capabilities to AP parameters
From: Luca Coelho @ 2017-02-07 20:40 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless, Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
For the benefit of drivers that rebuild IEs in firmware, parse the
IEs for HT/VHT capabilities and the respective membership selector
in the (extended) supported rates. This avoids duplicating the same
code into all drivers that need this information.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/linux/ieee80211.h | 3 ++-
include/net/cfg80211.h | 8 ++++++++
net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 02768de209d6..0dd9498c694f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1043,8 +1043,9 @@ struct ieee80211_mgmt {
} u;
} __packed __aligned(2);
-/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */
+/* Supported rates membership selectors */
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
/* mgmt header + 1 byte category code */
#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 870549480e9b..5cfd2806a078 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -748,6 +748,10 @@ struct cfg80211_bitrate_mask {
* @pbss: If set, start as a PCP instead of AP. Relevant for DMG
* networks.
* @beacon_rate: bitrate to be used for beacons
+ * @ht_cap: HT capabilities (or %NULL if HT isn't enabled)
+ * @vht_cap: VHT capabilities (or %NULL if VHT isn't enabled)
+ * @ht_required: stations must support HT
+ * @vht_required: stations must support VHT
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@@ -768,6 +772,10 @@ struct cfg80211_ap_settings {
const struct cfg80211_acl_data *acl;
bool pbss;
struct cfg80211_bitrate_mask beacon_rate;
+
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct ieee80211_vht_cap *vht_cap;
+ bool ht_required, vht_required;
};
/**
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 63dfa60a29ef..e023b55477ec 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3,7 +3,7 @@
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright 2015-2016 Intel Deutschland GmbH
+ * Copyright 2015-2017 Intel Deutschland GmbH
*/
#include <linux/if.h>
@@ -3743,6 +3743,49 @@ static int nl80211_parse_beacon(struct nlattr *attrs[],
return 0;
}
+static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params,
+ const u8 *rates)
+{
+ int i;
+
+ if (!rates)
+ return;
+
+ for (i = 0; i < rates[1]; i++) {
+ if (rates[2 + i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+ params->ht_required = true;
+ if (rates[2 + i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
+ params->vht_required = true;
+ }
+}
+
+/*
+ * Since the nl80211 API didn't include, from the beginning, attributes about
+ * HT/VHT requirements/capabilities, we parse them out of the IEs for the
+ * benefit of drivers that rebuild IEs in the firmware.
+ */
+static void nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
+{
+ const struct cfg80211_beacon_data *bcn = ¶ms->beacon;
+ size_t ies_len = bcn->beacon_ies_len;
+ const u8 *ies = bcn->beacon_ies;
+ const u8 *rates;
+ const u8 *cap;
+
+ rates = cfg80211_find_ie(WLAN_EID_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(params, rates);
+
+ rates = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(params, rates);
+
+ cap = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+ if (cap && cap[1] >= sizeof(*params->ht_cap))
+ params->ht_cap = (void *)(cap + 2);
+ cap = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, ies, ies_len);
+ if (cap && cap[1] >= sizeof(*params->vht_cap))
+ params->vht_cap = (void *)(cap + 2);
+}
+
static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
struct cfg80211_ap_settings *params)
{
@@ -3971,6 +4014,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.acl);
}
+ nl80211_calculate_ap_params(¶ms);
+
wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, ¶ms);
if (!err) {
--
2.11.0
^ permalink raw reply related
* [PATCH v5 3/3] mt76: add driver code for MT76x2e
From: Felix Fietkau @ 2017-02-07 20:20 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, sgruszka
In-Reply-To: <20170207202035.64224-1-nbd@nbd.name>
This is a 2x2 PCIe 802.11ac chipset by MediaTek
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
---
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 | 223 ++++++
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 | 184 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_dma.h | 68 ++
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 644 +++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h | 181 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 784 +++++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mac.c | 738 +++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mac.h | 189 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 534 ++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c | 452 ++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h | 136 ++++
drivers/net/wireless/mediatek/mt76/mt76x2_pci.c | 109 +++
drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 691 ++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_regs.h | 566 +++++++++++++++
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 | 255 +++++++
25 files changed, 6742 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..6311eaa88165
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/kfifo.h>
+
+#define MT7662_FIRMWARE "mt7662.bin"
+#define MT7662_ROM_PATCH "mt7662_rom_patch.bin"
+#define MT7662_EEPROM_SIZE 512
+
+#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];
+ int avg_rssi[MT_MAX_CHAINS];
+ int avg_rssi_all;
+
+ 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[256 / 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[254 - 8];
+
+ 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 *dev, void *txwi_ptr,
+ 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 data);
+
+void mt76x2_rx_poll_complete(struct mt76_dev *dev, 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 *dev);
+
+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 <nbd@nbd.name>
+ *
+ * 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 <linux/delay.h>
+#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 <nbd@nbd.name>
+ *
+ * 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 <linux/debugfs.h>
+#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..07bd3c7f5fa7
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+ *
+ * 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;
+}
+
+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_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 <lorenzo.bianconi83@gmail.com>
+ *
+ * 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 <linux/types.h>
+#include <linux/nl80211.h>
+
+#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..06e027ad0c4f
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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;
+ int idx;
+
+ 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);
+ idx = 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, MT_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 <nbd@nbd.name>
+ *
+ * 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..bbfcd361469e
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <asm/unaligned.h>
+#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);
+
+ 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(val, 6);
+}
+
+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 = false;
+ 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..bbd7a5b110c5
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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_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..fd7328d1b553
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/delay.h>
+#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; 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),
+ }
+};
+
+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);
+
+ 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..0dcc493a8cc2
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/delay.h>
+#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_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;
+
+ 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)
+ goto out;
+
+ mt76x2_mac_fill_tx_status(dev, &info, &msta->status,
+ msta->n_frames);
+
+ msta->status = *stat;
+ if (*update == 1) {
+ msta->n_frames = 1;
+ *update = 0;
+ } else {
+ msta->n_frames = 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);
+
+ 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..301288094666
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 *rxwi);
+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);
+
+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..bb6b3a753b31
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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);
+ mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+ 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]);
+ mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+ 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:
+ mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+ mt76_stop_tx_queues(&dev->mt76, sta, true);
+ break;
+ case STA_NOTIFY_AWAKE:
+ mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+ 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;
+
+ 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..4c94f84646fb
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#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(struct mt76x2_dev *dev, 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(dev, &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(dev, &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(dev, &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(dev, &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(dev, &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(dev, &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(dev, &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(dev, &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..f5af7eca3c09
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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_MCU_LED_CTRL 0x0770
+#define MT_MCU_LED_TX_BLINK_0 0x0774
+#define MT_MCU_LED_TX_BLINK_1 0x0778
+#define MT_MCU_LED0_S0 0x077C
+#define MT_MCU_LED0_S1 0x0780
+#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 *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..e5b07c640b90
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "mt76x2.h"
+#include "mt76x2_trace.h"
+
+static const struct pci_device_id mt76pci_device_table[] = {
+ { PCI_DEVICE(0x14c3, 0x7662) },
+ { PCI_DEVICE(0x14c3, 0x7612) },
+ { },
+};
+
+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..1fca4a7fd509
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/delay.h>
+#include "mt76x2.h"
+#include "mt76x2_mcu.h"
+#include "mt76x2_eeprom.h"
+
+static void
+mt76x2_adjust_lna_gain(struct mt76x2_dev *dev, int reg, s8 offset)
+{
+ s8 gain;
+
+ gain = FIELD_GET(MT_BBP_AGC_LNA_GAIN, mt76_rr(dev, MT_BBP(AGC, reg)));
+ gain -= offset / 2;
+ mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_LNA_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_lna_gain(dev, 4, gain_adj[0]);
+ mt76x2_adjust_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;
+
+ 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 = 0x3c3c023c;
+
+ 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 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, gain_delta;
+ bool 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);
+ if (dev->cal.low_gain == low_gain)
+ return;
+
+ dev->cal.low_gain = low_gain;
+
+ if (dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40)
+ val = 0x1e42 << 16;
+ else
+ val = 0x1836 << 16;
+
+ val |= 0xf8;
+
+ if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
+ mt76_wr(dev, MT_BBP(RXO, 14), 0x00560411);
+ else
+ mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
+
+ if (low_gain) {
+ 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(AGC, 35), 0x11111116);
+ mt76_wr(dev, MT_BBP(AGC, 37), 0x1010161C);
+ gain_delta = 0;
+ }
+
+ mt76_wr(dev, MT_BBP(AGC, 8),
+ val | FIELD_PREP(MT_BBP_AGC_GAIN, gain[0] - gain_delta));
+ mt76_wr(dev, MT_BBP(AGC, 9),
+ val | FIELD_PREP(MT_BBP_AGC_GAIN, gain[1] - gain_delta));
+
+ if (dev->mt76.chandef.chan->flags & IEEE80211_CHAN_RADAR)
+ mt76x2_dfs_adjust_agc(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);
+
+ 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..07b30805057a
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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_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_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_GAIN GENMASK(21, 16)
+
+/* AGC, R8/R9 */
+#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 <nbd@nbd.name>
+ *
+ * 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 <linux/module.h>
+
+#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 <nbd@nbd.name>
+ *
+ * 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 <linux/tracepoint.h>
+#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 <trace/define_trace.h>
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..549f6b56d07e
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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;
+
+ 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);
+}
+
--
2.11.0
^ permalink raw reply related
* [PATCH v5 2/3] mt76: add common code shared between multiple chipsets
From: Felix Fietkau @ 2017-02-07 20:20 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, sgruszka
In-Reply-To: <20170207202035.64224-1-nbd@nbd.name>
This will be used by drivers for MT76x2e, MT7603e and MT7628
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/debugfs.c | 76 ++++
drivers/net/wireless/mediatek/mt76/dma.c | 451 +++++++++++++++++++++++
drivers/net/wireless/mediatek/mt76/dma.h | 38 ++
drivers/net/wireless/mediatek/mt76/eeprom.c | 112 ++++++
drivers/net/wireless/mediatek/mt76/mac80211.c | 344 +++++++++++++++++
drivers/net/wireless/mediatek/mt76/mmio.c | 61 +++
drivers/net/wireless/mediatek/mt76/mt76.h | 355 ++++++++++++++++++
drivers/net/wireless/mediatek/mt76/trace.c | 23 ++
drivers/net/wireless/mediatek/mt76/trace.h | 71 ++++
drivers/net/wireless/mediatek/mt76/tx.c | 511 ++++++++++++++++++++++++++
drivers/net/wireless/mediatek/mt76/util.c | 78 ++++
drivers/net/wireless/mediatek/mt76/util.h | 44 +++
12 files changed, 2164 insertions(+)
create mode 100644 drivers/net/wireless/mediatek/mt76/debugfs.c
create mode 100644 drivers/net/wireless/mediatek/mt76/dma.c
create mode 100644 drivers/net/wireless/mediatek/mt76/dma.h
create mode 100644 drivers/net/wireless/mediatek/mt76/eeprom.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mac80211.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mmio.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt76.h
create mode 100644 drivers/net/wireless/mediatek/mt76/trace.c
create mode 100644 drivers/net/wireless/mediatek/mt76/trace.h
create mode 100644 drivers/net/wireless/mediatek/mt76/tx.c
create mode 100644 drivers/net/wireless/mediatek/mt76/util.c
create mode 100644 drivers/net/wireless/mediatek/mt76/util.h
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
new file mode 100644
index 000000000000..cfe32e53efc1
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 "mt76.h"
+
+static int
+mt76_reg_set(void *data, u64 val)
+{
+ struct mt76_dev *dev = data;
+
+ dev->bus->wr(dev, dev->debugfs_reg, val);
+ return 0;
+}
+
+static int
+mt76_reg_get(void *data, u64 *val)
+{
+ struct mt76_dev *dev = data;
+
+ *val = dev->bus->rr(dev, dev->debugfs_reg);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n");
+
+static int
+mt76_queues_read(struct seq_file *s, void *data)
+{
+ struct mt76_dev *dev = dev_get_drvdata(s->private);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) {
+ struct mt76_queue *q = &dev->q_tx[i];
+
+ if (!q->ndesc)
+ continue;
+
+ seq_printf(s,
+ "%d: queued=%d head=%d tail=%d swq_queued=%d\n",
+ i, q->queued, q->head, q->tail, q->swq_queued);
+ }
+
+ return 0;
+}
+
+struct dentry *mt76_register_debugfs(struct mt76_dev *dev)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("mt76", dev->hw->wiphy->debugfsdir);
+ if (!dir)
+ return NULL;
+
+ debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
+ debugfs_create_file("regval", S_IRUSR | S_IWUSR, dir, dev,
+ &fops_regval);
+ debugfs_create_blob("eeprom", S_IRUSR, dir, &dev->eeprom);
+ if (dev->otp.data)
+ debugfs_create_blob("otp", S_IRUSR, dir, &dev->otp);
+ debugfs_create_devm_seqfile(dev->dev, "queues", dir, mt76_queues_read);
+
+ return dir;
+}
+EXPORT_SYMBOL_GPL(mt76_register_debugfs);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
new file mode 100644
index 000000000000..ec4880cdc610
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/dma-mapping.h>
+#include "mt76.h"
+#include "dma.h"
+
+#define DMA_DUMMY_TXWI ((void *) ~0)
+
+static int
+mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ int size;
+ int i;
+
+ spin_lock_init(&q->lock);
+ INIT_LIST_HEAD(&q->swq);
+
+ size = q->ndesc * sizeof(struct mt76_desc);
+ q->desc = dmam_alloc_coherent(dev->dev, size, &q->desc_dma, GFP_KERNEL);
+ if (!q->desc)
+ return -ENOMEM;
+
+ size = q->ndesc * sizeof(*q->entry);
+ q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
+ if (!q->entry)
+ return -ENOMEM;
+
+ /* clear descriptors */
+ for (i = 0; i < q->ndesc; i++)
+ q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+
+ iowrite32(q->desc_dma, &q->regs->desc_base);
+ iowrite32(0, &q->regs->cpu_idx);
+ iowrite32(0, &q->regs->dma_idx);
+ iowrite32(q->ndesc, &q->regs->ring_size);
+
+ return 0;
+}
+
+static int
+mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ struct mt76_queue_buf *buf, int nbufs, u32 info,
+ struct sk_buff *skb, void *txwi)
+{
+ struct mt76_desc *desc;
+ u32 ctrl;
+ int i, idx = -1;
+
+ if (txwi)
+ q->entry[q->head].txwi = DMA_DUMMY_TXWI;
+
+ for (i = 0; i < nbufs; i += 2, buf += 2) {
+ u32 buf0 = buf[0].addr, buf1 = 0;
+
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+ if (i < nbufs - 1) {
+ buf1 = buf[1].addr;
+ ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
+ }
+
+ if (i == nbufs - 1)
+ ctrl |= MT_DMA_CTL_LAST_SEC0;
+ else if (i == nbufs - 2)
+ ctrl |= MT_DMA_CTL_LAST_SEC1;
+
+ idx = q->head;
+ q->head = (q->head + 1) % q->ndesc;
+
+ desc = &q->desc[idx];
+
+ WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
+ WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
+ WRITE_ONCE(desc->info, cpu_to_le32(info));
+ WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+
+ q->queued++;
+ }
+
+ q->entry[idx].txwi = txwi;
+ q->entry[idx].skb = skb;
+
+ return idx;
+}
+
+static void
+mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ struct mt76_queue_entry *prev_e)
+{
+ struct mt76_queue_entry *e = &q->entry[idx];
+ __le32 __ctrl = READ_ONCE(q->desc[idx].ctrl);
+ u32 ctrl = le32_to_cpu(__ctrl);
+
+ if (!e->txwi || !e->skb) {
+ __le32 addr = READ_ONCE(q->desc[idx].buf0);
+ u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
+
+ dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
+ DMA_TO_DEVICE);
+ }
+
+ if (!(ctrl & MT_DMA_CTL_LAST_SEC0)) {
+ __le32 addr = READ_ONCE(q->desc[idx].buf1);
+ u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN1, ctrl);
+
+ dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
+ DMA_TO_DEVICE);
+ }
+
+ if (e->txwi == DMA_DUMMY_TXWI)
+ e->txwi = NULL;
+
+ *prev_e = *e;
+ memset(e, 0, sizeof(*e));
+}
+
+static void
+mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ q->head = ioread32(&q->regs->dma_idx);
+ q->tail = q->head;
+ iowrite32(q->head, &q->regs->cpu_idx);
+}
+
+static void
+mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
+{
+ struct mt76_queue *q = &dev->q_tx[qid];
+ struct mt76_queue_entry entry;
+ bool wake = false;
+ int last;
+
+ if (!q->ndesc)
+ return;
+
+ spin_lock_bh(&q->lock);
+ if (flush)
+ last = -1;
+ else
+ last = ioread32(&q->regs->dma_idx);
+
+ while (q->queued && q->tail != last) {
+ mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
+ if (entry.schedule)
+ q->swq_queued--;
+
+ if (entry.skb)
+ dev->drv->tx_complete_skb(dev, q, &entry, flush);
+
+ if (entry.txwi) {
+ mt76_put_txwi(dev, entry.txwi);
+ wake = true;
+ }
+
+ q->tail = (q->tail + 1) % q->ndesc;
+ q->queued--;
+
+ if (!flush && q->tail == last)
+ last = ioread32(&q->regs->dma_idx);
+ }
+
+ if (!flush)
+ mt76_txq_schedule(dev, q);
+ else
+ mt76_dma_sync_idx(dev, q);
+
+ wake = wake && qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
+ spin_unlock_bh(&q->lock);
+
+ if (wake)
+ ieee80211_wake_queue(dev->hw, qid);
+}
+
+static void *
+mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ int *len, u32 *info, bool *more)
+{
+ struct mt76_queue_entry *e = &q->entry[idx];
+ struct mt76_desc *desc = &q->desc[idx];
+ dma_addr_t buf_addr;
+ void *buf = e->buf;
+ int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
+
+ buf_addr = READ_ONCE(desc->buf0);
+ if (len) {
+ u32 ctl = READ_ONCE(desc->ctrl);
+ *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl);
+ *more = !(ctl & MT_DMA_CTL_LAST_SEC0);
+ }
+
+ if (info)
+ *info = le32_to_cpu(desc->info);
+
+ dma_unmap_single(dev->dev, buf_addr, buf_len, DMA_FROM_DEVICE);
+ e->buf = NULL;
+
+ return buf;
+}
+
+static void *
+mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ int *len, u32 *info, bool *more)
+{
+ int idx = q->tail;
+
+ *more = false;
+ if (!q->queued)
+ return NULL;
+
+ if (!flush && !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
+ return NULL;
+
+ q->tail = (q->tail + 1) % q->ndesc;
+ q->queued--;
+
+ return mt76_dma_get_buf(dev, q, idx, len, info, more);
+}
+
+static void
+mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ iowrite32(q->head, &q->regs->cpu_idx);
+}
+
+static int
+mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi)
+{
+ dma_addr_t addr;
+ void *buf;
+ int frames = 0;
+ int len = SKB_WITH_OVERHEAD(q->buf_size);
+ int offset = q->buf_offset;
+ int idx;
+ void *(*alloc)(unsigned int fragsz);
+
+ if (napi)
+ alloc = napi_alloc_frag;
+ else
+ alloc = netdev_alloc_frag;
+
+ spin_lock_bh(&q->lock);
+
+ while (q->queued < q->ndesc - 1) {
+ struct mt76_queue_buf qbuf;
+
+ buf = alloc(q->buf_size);
+ if (!buf)
+ break;
+
+ addr = dma_map_single(dev->dev, buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev->dev, addr)) {
+ skb_free_frag(buf);
+ break;
+ }
+
+ qbuf.addr = addr + offset;
+ qbuf.len = len - offset;
+ idx = mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL);
+ frames++;
+ }
+
+ if (frames)
+ mt76_dma_kick_queue(dev, q);
+
+ spin_unlock_bh(&q->lock);
+
+ return frames;
+}
+
+static void
+mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ void *buf;
+ bool more;
+
+ spin_lock_bh(&q->lock);
+ do {
+ buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more);
+ if (!buf)
+ break;
+
+ skb_free_frag(buf);
+ } while (1);
+ spin_unlock_bh(&q->lock);
+}
+
+static void
+mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+{
+ struct mt76_queue *q = &dev->q_rx[qid];
+ int i;
+
+ for (i = 0; i < q->ndesc; i++)
+ q->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+
+ mt76_dma_rx_cleanup(dev, q);
+ mt76_dma_sync_idx(dev, q);
+ mt76_dma_rx_fill(dev, q, false);
+}
+
+static void
+mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+ int len, bool more)
+{
+ struct page *page = virt_to_head_page(data);
+ int offset = data - page_address(page);
+ struct sk_buff *skb = q->rx_head;
+
+ offset += q->buf_offset;
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, len,
+ q->buf_size);
+
+ if (more)
+ return;
+
+ q->rx_head = NULL;
+ dev->drv->rx_skb(dev, q - dev->q_rx, skb);
+}
+
+static int
+mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+{
+ struct sk_buff *skb;
+ unsigned char *data;
+ int len;
+ int done = 0;
+ bool more;
+
+ while (done < budget) {
+ u32 info;
+
+ data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
+ if (!data)
+ break;
+
+ if (q->rx_head) {
+ mt76_add_fragment(dev, q, data, len, more);
+ continue;
+ }
+
+ skb = build_skb(data, q->buf_size);
+ if (!skb) {
+ skb_free_frag(data);
+ continue;
+ }
+
+ skb_reserve(skb, q->buf_offset);
+ if (skb->tail + len > skb->end) {
+ dev_kfree_skb(skb);
+ continue;
+ }
+
+ if (q == &dev->q_rx[MT_RXQ_MCU]) {
+ u32 *rxfce = (u32 *) skb->cb;
+ *rxfce = info;
+ }
+
+ __skb_put(skb, len);
+ done++;
+
+ if (more) {
+ q->rx_head = skb;
+ continue;
+ }
+
+ dev->drv->rx_skb(dev, q - dev->q_rx, skb);
+ }
+
+ mt76_dma_rx_fill(dev, q, true);
+ return done;
+}
+
+static int
+mt76_dma_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct mt76_dev *dev;
+ int qid, done;
+
+ dev = container_of(napi->dev, struct mt76_dev, napi_dev);
+ qid = napi - dev->napi;
+
+ done = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget);
+ if (done < budget) {
+ napi_complete(napi);
+ dev->drv->rx_poll_complete(dev, qid);
+ }
+ mt76_rx_complete(dev, qid);
+
+ return done;
+}
+
+static int
+mt76_dma_init(struct mt76_dev *dev)
+{
+ int i;
+
+ init_dummy_netdev(&dev->napi_dev);
+
+ for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
+ netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
+ 64);
+ mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
+ skb_queue_head_init(&dev->rx_skb[i]);
+ napi_enable(&dev->napi[i]);
+ }
+
+ return 0;
+}
+
+static const struct mt76_queue_ops mt76_dma_ops = {
+ .init = mt76_dma_init,
+ .alloc = mt76_dma_alloc_queue,
+ .add_buf = mt76_dma_add_buf,
+ .tx_cleanup = mt76_dma_tx_cleanup,
+ .rx_reset = mt76_dma_rx_reset,
+ .kick = mt76_dma_kick_queue,
+};
+
+int mt76_dma_attach(struct mt76_dev *dev)
+{
+ dev->queue_ops = &mt76_dma_ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_dma_attach);
+
+void mt76_dma_cleanup(struct mt76_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++)
+ mt76_dma_tx_cleanup(dev, i, true);
+
+ for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
+ netif_napi_del(&dev->napi[i]);
+ mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h
new file mode 100644
index 000000000000..1dad39697929
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/dma.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 __MT76_DMA_H
+#define __MT76_DMA_H
+
+#define MT_RING_SIZE 0x10
+
+#define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0)
+#define MT_DMA_CTL_LAST_SEC1 BIT(14)
+#define MT_DMA_CTL_BURST BIT(15)
+#define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16)
+#define MT_DMA_CTL_LAST_SEC0 BIT(30)
+#define MT_DMA_CTL_DMA_DONE BIT(31)
+
+struct mt76_desc {
+ __le32 buf0;
+ __le32 ctrl;
+ __le32 buf1;
+ __le32 info;
+} __packed __aligned(4);
+
+int mt76_dma_attach(struct mt76_dev *dev);
+void mt76_dma_cleanup(struct mt76_dev *dev);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
new file mode 100644
index 000000000000..530e5593765c
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/etherdevice.h>
+#include "mt76.h"
+
+static int
+mt76_get_of_eeprom(struct mt76_dev *dev, int len)
+{
+#if defined(CONFIG_OF) && defined(CONFIG_MTD)
+ struct device_node *np = dev->dev->of_node;
+ struct mtd_info *mtd;
+ const __be32 *list;
+ const char *part;
+ phandle phandle;
+ int offset = 0;
+ int size;
+ size_t retlen;
+ int ret;
+
+ if (!np)
+ return -ENOENT;
+
+ list = of_get_property(np, "mediatek,mtd-eeprom", &size);
+ if (!list)
+ return -ENOENT;
+
+ phandle = be32_to_cpup(list++);
+ if (!phandle)
+ return -ENOENT;
+
+ np = of_find_node_by_phandle(phandle);
+ if (!np)
+ return -EINVAL;
+
+ part = of_get_property(np, "label", NULL);
+ if (!part)
+ part = np->name;
+
+ mtd = get_mtd_device_nm(part);
+ if (IS_ERR(mtd))
+ return PTR_ERR(mtd);
+
+ if (size <= sizeof(*list))
+ return -EINVAL;
+
+ offset = be32_to_cpup(list);
+ ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data);
+ put_mtd_device(mtd);
+ if (ret)
+ return ret;
+
+ if (retlen < len)
+ return -EINVAL;
+
+ return 0;
+#else
+ return -ENOENT;
+#endif
+}
+
+void
+mt76_eeprom_override(struct mt76_dev *dev)
+{
+#ifdef CONFIG_OF
+ struct device_node *np = dev->dev->of_node;
+ const u8 *mac;
+
+ if (!np)
+ return;
+
+ mac = of_get_mac_address(np);
+ if (mac)
+ memcpy(dev->macaddr, mac, ETH_ALEN);
+#endif
+
+ if (!is_valid_ether_addr(dev->macaddr)) {
+ eth_random_addr(dev->macaddr);
+ dev_info(dev->dev,
+ "Invalid MAC address, using random address %pM\n",
+ dev->macaddr);
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_eeprom_override);
+
+int
+mt76_eeprom_init(struct mt76_dev *dev, int len)
+{
+ dev->eeprom.size = len;
+ dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
+ if (!dev->eeprom.data)
+ return -ENOMEM;
+
+ return !mt76_get_of_eeprom(dev, len);
+}
+EXPORT_SYMBOL_GPL(mt76_eeprom_init);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
new file mode 100644
index 000000000000..c757254c15d8
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 "mt76.h"
+
+#define CHAN2G(_idx, _freq) { \
+ .band = NL80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_idx, _freq) { \
+ .band = NL80211_BAND_5GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 30, \
+}
+
+static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ CHAN2G(1, 2412),
+ CHAN2G(2, 2417),
+ CHAN2G(3, 2422),
+ CHAN2G(4, 2427),
+ CHAN2G(5, 2432),
+ CHAN2G(6, 2437),
+ CHAN2G(7, 2442),
+ CHAN2G(8, 2447),
+ CHAN2G(9, 2452),
+ CHAN2G(10, 2457),
+ CHAN2G(11, 2462),
+ CHAN2G(12, 2467),
+ CHAN2G(13, 2472),
+ CHAN2G(14, 2484),
+};
+
+static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ CHAN5G(36, 5180),
+ CHAN5G(40, 5200),
+ CHAN5G(44, 5220),
+ CHAN5G(48, 5240),
+
+ CHAN5G(52, 5260),
+ CHAN5G(56, 5280),
+ CHAN5G(60, 5300),
+ CHAN5G(64, 5320),
+
+ CHAN5G(100, 5500),
+ CHAN5G(104, 5520),
+ CHAN5G(108, 5540),
+ CHAN5G(112, 5560),
+ CHAN5G(116, 5580),
+ CHAN5G(120, 5600),
+ CHAN5G(124, 5620),
+ CHAN5G(128, 5640),
+ CHAN5G(132, 5660),
+ CHAN5G(136, 5680),
+ CHAN5G(140, 5700),
+
+ CHAN5G(149, 5745),
+ CHAN5G(153, 5765),
+ CHAN5G(157, 5785),
+ CHAN5G(161, 5805),
+ CHAN5G(165, 5825),
+};
+
+static int
+mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
+ const struct ieee80211_channel *chan, int n_chan,
+ struct ieee80211_rate *rates, int n_rates, bool vht)
+{
+ struct ieee80211_supported_band *sband = &msband->sband;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ void *chanlist;
+ u16 mcs_map;
+ int size;
+
+ size = n_chan * sizeof(*chan);
+ chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
+ if (!chanlist)
+ return -ENOMEM;
+
+ msband->chan = devm_kzalloc(dev->dev, n_chan * sizeof(*msband->chan),
+ GFP_KERNEL);
+ if (!msband->chan)
+ return -ENOMEM;
+
+ sband->channels = chanlist;
+ sband->n_channels = n_chan;
+ sband->bitrates = rates;
+ sband->n_bitrates = n_rates;
+ dev->chandef.chan = &sband->channels[0];
+
+ ht_cap = &sband->ht_cap;
+ ht_cap->ht_supported = true;
+ ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_TX_STBC |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+ ht_cap->mcs.rx_mask[0] = 0xff;
+ ht_cap->mcs.rx_mask[1] = 0xff;
+ ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+
+ if (!vht)
+ return 0;
+
+ vht_cap = &sband->vht_cap;
+ vht_cap->vht_supported = true;
+
+ mcs_map = (IEEE80211_VHT_MCS_SUPPORT_0_9 << (0 * 2)) |
+ (IEEE80211_VHT_MCS_SUPPORT_0_9 << (1 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (2 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (3 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (4 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (5 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (6 * 2)) |
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (7 * 2));
+
+ vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+ vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+ vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_TXSTBC |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_SHORT_GI_80;
+
+ return 0;
+}
+
+static int
+mt76_init_sband_2g(struct mt76_dev *dev, struct ieee80211_rate *rates,
+ int n_rates)
+{
+ dev->hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->sband_2g.sband;
+
+ return mt76_init_sband(dev, &dev->sband_2g,
+ mt76_channels_2ghz,
+ ARRAY_SIZE(mt76_channels_2ghz),
+ rates, n_rates, false);
+}
+
+static int
+mt76_init_sband_5g(struct mt76_dev *dev, struct ieee80211_rate *rates,
+ int n_rates, bool vht)
+{
+ dev->hw->wiphy->bands[NL80211_BAND_5GHZ] = &dev->sband_5g.sband;
+
+ return mt76_init_sband(dev, &dev->sband_5g,
+ mt76_channels_5ghz,
+ ARRAY_SIZE(mt76_channels_5ghz),
+ rates, n_rates, vht);
+}
+
+static void
+mt76_check_sband(struct mt76_dev *dev, int band)
+{
+ struct ieee80211_supported_band *sband = dev->hw->wiphy->bands[band];
+ bool found = false;
+ int i;
+
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ found = true;
+ break;
+ }
+
+ if (found)
+ return;
+
+ sband->n_channels = 0;
+ dev->hw->wiphy->bands[band] = NULL;
+}
+
+int mt76_register_device(struct mt76_dev *dev, bool vht,
+ struct ieee80211_rate *rates, int n_rates)
+{
+ struct ieee80211_hw *hw = dev->hw;
+ struct wiphy *wiphy = hw->wiphy;
+ int ret;
+
+ dev_set_drvdata(dev->dev, dev);
+
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->cc_lock);
+ INIT_LIST_HEAD(&dev->txwi_cache);
+
+ SET_IEEE80211_DEV(hw, dev->dev);
+ SET_IEEE80211_PERM_ADDR(hw, dev->macaddr);
+
+ wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_ADHOC);
+
+ wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+
+ hw->txq_data_size = sizeof(struct mt76_txq);
+ hw->max_tx_fragments = 16;
+
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, PS_NULLFUNC_STACK);
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, TX_FRAG_LIST);
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+
+ if (dev->cap.has_2ghz) {
+ ret = mt76_init_sband_2g(dev, rates, n_rates);
+ if (ret)
+ return ret;
+ }
+
+ if (dev->cap.has_5ghz) {
+ ret = mt76_init_sband_5g(dev, rates + 4, n_rates - 4, vht);
+ if (ret)
+ return ret;
+ }
+
+ wiphy_read_of_freq_limits(dev->hw->wiphy);
+ mt76_check_sband(dev, NL80211_BAND_2GHZ);
+ mt76_check_sband(dev, NL80211_BAND_5GHZ);
+
+ return ieee80211_register_hw(hw);
+}
+EXPORT_SYMBOL_GPL(mt76_register_device);
+
+void mt76_unregister_device(struct mt76_dev *dev)
+{
+ struct ieee80211_hw *hw = dev->hw;
+
+ ieee80211_unregister_hw(hw);
+ mt76_tx_free(dev);
+}
+EXPORT_SYMBOL_GPL(mt76_unregister_device);
+
+void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+{
+ if (!test_bit(MT76_STATE_RUNNING, &dev->state)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ __skb_queue_tail(&dev->rx_skb[q], skb);
+}
+EXPORT_SYMBOL_GPL(mt76_rx);
+
+void mt76_set_channel(struct mt76_dev *dev)
+{
+ struct ieee80211_hw *hw = dev->hw;
+ struct cfg80211_chan_def *chandef = &hw->conf.chandef;
+ struct mt76_channel_state *state;
+ bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
+
+ if (dev->drv->update_survey)
+ dev->drv->update_survey(dev);
+
+ dev->chandef = *chandef;
+
+ if (!offchannel)
+ dev->main_chan = chandef->chan;
+
+ if (chandef->chan != dev->main_chan) {
+ state = mt76_channel_state(dev, chandef->chan);
+ memset(state, 0, sizeof(*state));
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_set_channel);
+
+int mt76_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct mt76_dev *dev = hw->priv;
+ struct mt76_sband *sband;
+ struct ieee80211_channel *chan;
+ struct mt76_channel_state *state;
+ int ret = 0;
+
+ if (idx == 0 && dev->drv->update_survey)
+ dev->drv->update_survey(dev);
+
+ sband = &dev->sband_2g;
+ if (idx >= sband->sband.n_channels) {
+ idx -= sband->sband.n_channels;
+ sband = &dev->sband_5g;
+ }
+
+ if (idx >= sband->sband.n_channels)
+ return -ENOENT;
+
+ chan = &sband->sband.channels[idx];
+ state = mt76_channel_state(dev, chan);
+
+ memset(survey, 0, sizeof(*survey));
+ survey->channel = chan;
+ survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
+ if (chan == dev->main_chan)
+ survey->filled |= SURVEY_INFO_IN_USE;
+
+ spin_lock_bh(&dev->cc_lock);
+ survey->time = div_u64(state->cc_active, 1000);
+ survey->time_busy = div_u64(state->cc_busy, 1000);
+ spin_unlock_bh(&dev->cc_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_get_survey);
+
+void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
+ ieee80211_rx_napi(dev->hw, NULL, skb, &dev->napi[q]);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c
new file mode 100644
index 000000000000..09a14dead6e3
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mmio.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 "mt76.h"
+#include "trace.h"
+
+static u32 mt76_mmio_rr(struct mt76_dev *dev, u32 offset)
+{
+ u32 val;
+
+ val = ioread32(dev->regs + offset);
+ trace_reg_rr(dev, offset, val);
+
+ return val;
+}
+
+static void mt76_mmio_wr(struct mt76_dev *dev, u32 offset, u32 val)
+{
+ trace_reg_wr(dev, offset, val);
+ iowrite32(val, dev->regs + offset);
+}
+
+static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val)
+{
+ val |= mt76_mmio_rr(dev, offset) & ~mask;
+ mt76_mmio_wr(dev, offset, val);
+ return val;
+}
+
+static void mt76_mmio_copy(struct mt76_dev *dev, u32 offset, const void *data,
+ int len)
+{
+ __iowrite32_copy(dev->regs + offset, data, len >> 2);
+}
+
+void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs)
+{
+ static const struct mt76_bus_ops mt76_mmio_ops = {
+ .rr = mt76_mmio_rr,
+ .rmw = mt76_mmio_rmw,
+ .wr = mt76_mmio_wr,
+ .copy = mt76_mmio_copy,
+ };
+
+ dev->bus = &mt76_mmio_ops;
+ dev->regs = regs;
+}
+EXPORT_SYMBOL_GPL(mt76_mmio_init);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
new file mode 100644
index 000000000000..f94f78a85106
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 __MT76_H
+#define __MT76_H
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <net/mac80211.h>
+#include "util.h"
+
+#define MT_RX_RING_SIZE 128
+#define MT_TX_RING_SIZE 256
+#define MT_MCU_RING_SIZE 32
+#define MT_RX_BUF_SIZE 2048
+
+struct mt76_dev;
+
+struct mt76_bus_ops {
+ u32 (*rr)(struct mt76_dev *dev, u32 offset);
+ void (*wr)(struct mt76_dev *dev, u32 offset, u32 val);
+ u32 (*rmw)(struct mt76_dev *dev, u32 offset, u32 mask, u32 val);
+ void (*copy)(struct mt76_dev *dev, u32 offset, const void *data,
+ int len);
+};
+
+enum mt76_txq_id {
+ MT_TXQ_VO = IEEE80211_AC_VO,
+ MT_TXQ_VI = IEEE80211_AC_VI,
+ MT_TXQ_BE = IEEE80211_AC_BE,
+ MT_TXQ_BK = IEEE80211_AC_BK,
+ MT_TXQ_PSD,
+ MT_TXQ_MCU,
+ MT_TXQ_BEACON,
+ MT_TXQ_CAB,
+ __MT_TXQ_MAX
+};
+
+enum mt76_rxq_id {
+ MT_RXQ_MAIN,
+ MT_RXQ_MCU,
+ __MT_RXQ_MAX
+};
+
+struct mt76_queue_buf {
+ dma_addr_t addr;
+ int len;
+};
+
+struct mt76_queue_entry {
+ union {
+ void *buf;
+ struct sk_buff *skb;
+ };
+ struct mt76_txwi_cache *txwi;
+ bool schedule;
+};
+
+struct mt76_queue_regs {
+ u32 desc_base;
+ u32 ring_size;
+ u32 cpu_idx;
+ u32 dma_idx;
+} __packed __aligned(4);
+
+struct mt76_queue {
+ struct mt76_queue_regs __iomem *regs;
+
+ spinlock_t lock;
+ struct mt76_queue_entry *entry;
+ struct mt76_desc *desc;
+
+ struct list_head swq;
+ int swq_queued;
+
+ u16 head;
+ u16 tail;
+ int ndesc;
+ int queued;
+ int buf_size;
+
+ u8 buf_offset;
+ u8 hw_idx;
+
+ dma_addr_t desc_dma;
+ struct sk_buff *rx_head;
+};
+
+struct mt76_queue_ops {
+ int (*init)(struct mt76_dev *dev);
+
+ int (*alloc)(struct mt76_dev *dev, struct mt76_queue *q);
+
+ int (*add_buf)(struct mt76_dev *dev, struct mt76_queue *q,
+ struct mt76_queue_buf *buf, int nbufs, u32 info,
+ struct sk_buff *skb, void *txwi);
+
+ void *(*dequeue)(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ int *len, u32 *info, bool *more);
+
+ void (*rx_reset)(struct mt76_dev *dev, enum mt76_rxq_id qid);
+
+ void (*tx_cleanup)(struct mt76_dev *dev, enum mt76_txq_id qid,
+ bool flush);
+
+ void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+};
+
+struct mt76_wcid {
+ u8 idx;
+ u8 hw_key_idx;
+
+ __le16 tx_rate;
+ bool tx_rate_set;
+ u8 tx_rate_nss;
+ s8 max_txpwr_adj;
+};
+
+struct mt76_txq {
+ struct list_head list;
+ struct mt76_queue *hwq;
+ struct mt76_wcid *wcid;
+
+ struct sk_buff_head retry_q;
+
+ u16 agg_ssn;
+ bool send_bar;
+ bool aggr;
+};
+
+struct mt76_txwi_cache {
+ u32 txwi[8];
+ dma_addr_t dma_addr;
+ struct list_head list;
+};
+
+enum {
+ MT76_STATE_INITIALIZED,
+ MT76_STATE_RUNNING,
+ MT76_SCANNING,
+ MT76_RESET,
+};
+
+struct mt76_hw_cap {
+ bool has_2ghz;
+ bool has_5ghz;
+};
+
+struct mt76_driver_ops {
+ u16 txwi_size;
+
+ void (*update_survey)(struct mt76_dev *dev);
+
+ int (*tx_prepare_skb)(struct mt76_dev *dev, void *txwi_ptr,
+ struct sk_buff *skb, struct mt76_queue *q,
+ struct mt76_wcid *wcid,
+ struct ieee80211_sta *sta, u32 *tx_info);
+
+ void (*tx_complete_skb)(struct mt76_dev *dev, struct mt76_queue *q,
+ struct mt76_queue_entry *e, bool flush);
+
+ void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q,
+ struct sk_buff *skb);
+
+ void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);
+};
+
+struct mt76_channel_state {
+ u64 cc_active;
+ u64 cc_busy;
+};
+
+struct mt76_sband {
+ struct ieee80211_supported_band sband;
+ struct mt76_channel_state *chan;
+};
+
+struct mt76_dev {
+ struct ieee80211_hw *hw;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *main_chan;
+
+ spinlock_t lock;
+ spinlock_t cc_lock;
+ const struct mt76_bus_ops *bus;
+ const struct mt76_driver_ops *drv;
+ void __iomem *regs;
+ struct device *dev;
+
+ struct net_device napi_dev;
+ struct napi_struct napi[__MT_RXQ_MAX];
+ struct sk_buff_head rx_skb[__MT_RXQ_MAX];
+
+ struct list_head txwi_cache;
+ struct mt76_queue q_tx[__MT_TXQ_MAX];
+ struct mt76_queue q_rx[__MT_RXQ_MAX];
+ const struct mt76_queue_ops *queue_ops;
+
+ u8 macaddr[ETH_ALEN];
+ u32 rev;
+ unsigned long state;
+
+ struct mt76_sband sband_2g;
+ struct mt76_sband sband_5g;
+ struct debugfs_blob_wrapper eeprom;
+ struct debugfs_blob_wrapper otp;
+ struct mt76_hw_cap cap;
+
+ u32 debugfs_reg;
+};
+
+enum mt76_phy_type {
+ MT_PHY_TYPE_CCK,
+ MT_PHY_TYPE_OFDM,
+ MT_PHY_TYPE_HT,
+ MT_PHY_TYPE_HT_GF,
+ MT_PHY_TYPE_VHT,
+};
+
+struct mt76_rate_power {
+ union {
+ struct {
+ s8 cck[4];
+ s8 ofdm[8];
+ s8 ht[16];
+ s8 vht[10];
+ };
+ s8 all[38];
+ };
+};
+
+#define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
+#define mt76_wr(dev, ...) (dev)->mt76.bus->wr(&((dev)->mt76), __VA_ARGS__)
+#define mt76_rmw(dev, ...) (dev)->mt76.bus->rmw(&((dev)->mt76), __VA_ARGS__)
+#define mt76_wr_copy(dev, ...) (dev)->mt76.bus->copy(&((dev)->mt76), __VA_ARGS__)
+
+#define mt76_set(dev, offset, val) mt76_rmw(dev, offset, 0, val)
+#define mt76_clear(dev, offset, val) mt76_rmw(dev, offset, val, 0)
+
+#define mt76_get_field(_dev, _reg, _field) \
+ FIELD_GET(_field, mt76_rr(dev, _reg))
+
+#define mt76_rmw_field(_dev, _reg, _field, _val) \
+ mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val))
+
+#define mt76_hw(dev) (dev)->mt76.hw
+
+bool __mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
+ int timeout);
+
+#define mt76_poll(dev, ...) __mt76_poll(&((dev)->mt76), __VA_ARGS__)
+
+bool __mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
+ int timeout);
+
+#define mt76_poll_msec(dev, ...) __mt76_poll_msec(&((dev)->mt76), __VA_ARGS__)
+
+void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs);
+
+static inline u16 mt76_chip(struct mt76_dev *dev)
+{
+ return dev->rev >> 16;
+}
+
+static inline u16 mt76_rev(struct mt76_dev *dev)
+{
+ return dev->rev & 0xffff;
+}
+
+#define mt76xx_chip(dev) mt76_chip(&((dev)->mt76))
+#define mt76xx_rev(dev) mt76_rev(&((dev)->mt76))
+
+#define mt76_init_queues(dev) (dev)->mt76.queue_ops->init(&((dev)->mt76))
+#define mt76_queue_alloc(dev, ...) (dev)->mt76.queue_ops->alloc(&((dev)->mt76), __VA_ARGS__)
+#define mt76_queue_add_buf(dev, ...) (dev)->mt76.queue_ops->add_buf(&((dev)->mt76), __VA_ARGS__)
+#define mt76_queue_rx_reset(dev, ...) (dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__)
+#define mt76_queue_tx_cleanup(dev, ...) (dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
+#define mt76_queue_kick(dev, ...) (dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__)
+
+static inline struct mt76_channel_state *
+mt76_channel_state(struct mt76_dev *dev, struct ieee80211_channel *c)
+{
+ struct mt76_sband *msband;
+ int idx;
+
+ if (c->band == NL80211_BAND_2GHZ)
+ msband = &dev->sband_2g;
+ else
+ msband = &dev->sband_5g;
+
+ idx = c - &msband->sband.channels[0];
+ return &msband->chan[idx];
+}
+
+int mt76_register_device(struct mt76_dev *dev, bool vht,
+ struct ieee80211_rate *rates, int n_rates);
+void mt76_unregister_device(struct mt76_dev *dev);
+
+struct dentry *mt76_register_debugfs(struct mt76_dev *dev);
+
+int mt76_eeprom_init(struct mt76_dev *dev, int len);
+void mt76_eeprom_override(struct mt76_dev *dev);
+
+static inline struct ieee80211_txq *
+mtxq_to_txq(struct mt76_txq *mtxq)
+{
+ void *ptr = mtxq;
+
+ return container_of(ptr, struct ieee80211_txq, drv_priv);
+}
+
+int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_sta *sta);
+
+void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb);
+void mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
+ struct mt76_wcid *wcid, struct sk_buff *skb);
+void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq);
+void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq);
+void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
+void mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta,
+ bool send_bar);
+void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq);
+void mt76_txq_schedule_all(struct mt76_dev *dev);
+void mt76_release_buffered_frames(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u16 tids, int nframes,
+ enum ieee80211_frame_release_type reason,
+ bool more_data);
+void mt76_set_channel(struct mt76_dev *dev);
+int mt76_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey);
+
+/* internal */
+void mt76_tx_free(struct mt76_dev *dev);
+void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/trace.c b/drivers/net/wireless/mediatek/mt76/trace.c
new file mode 100644
index 000000000000..ea4ab8729ae4
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/trace.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/module.h>
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/trace.h b/drivers/net/wireless/mediatek/mt76/trace.h
new file mode 100644
index 000000000000..ea30895933c5
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/trace.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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(__MT76_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __MT76_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "mt76.h"
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mt76
+
+#define MAXNAME 32
+#define DEV_ENTRY __array(char, wiphy_name, 32)
+#define DEV_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(dev->hw->wiphy), MAXNAME)
+#define DEV_PR_FMT "%s"
+#define DEV_PR_ARG __entry->wiphy_name
+
+#define REG_ENTRY __field(u32, reg) __field(u32, val)
+#define REG_ASSIGN __entry->reg = reg; __entry->val = val
+#define REG_PR_FMT " %04x=%08x"
+#define REG_PR_ARG __entry->reg, __entry->val
+
+DECLARE_EVENT_CLASS(dev_reg_evt,
+ TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
+ TP_ARGS(dev, reg, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ REG_ENTRY
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ REG_ASSIGN;
+ ),
+ TP_printk(
+ DEV_PR_FMT REG_PR_FMT,
+ DEV_PR_ARG, REG_PR_ARG
+ )
+);
+
+DEFINE_EVENT(dev_reg_evt, reg_rr,
+ TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
+ TP_ARGS(dev, reg, val)
+);
+
+DEFINE_EVENT(dev_reg_evt, reg_wr,
+ TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
+ TP_ARGS(dev, reg, val)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
new file mode 100644
index 000000000000..45bf0b17f7cf
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 "mt76.h"
+
+static struct mt76_txwi_cache *
+mt76_alloc_txwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t;
+ dma_addr_t addr;
+ int size;
+
+ size = (sizeof(*t) + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1);
+ t = devm_kzalloc(dev->dev, size, GFP_ATOMIC);
+ if (!t)
+ return NULL;
+
+ addr = dma_map_single(dev->dev, &t->txwi, sizeof(t->txwi),
+ DMA_TO_DEVICE);
+ t->dma_addr = addr;
+
+ return t;
+}
+
+static struct mt76_txwi_cache *
+__mt76_get_txwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t = NULL;
+
+ spin_lock_bh(&dev->lock);
+ if (!list_empty(&dev->txwi_cache)) {
+ t = list_first_entry(&dev->txwi_cache, struct mt76_txwi_cache,
+ list);
+ list_del(&t->list);
+ }
+ spin_unlock_bh(&dev->lock);
+
+ return t;
+}
+
+static struct mt76_txwi_cache *
+mt76_get_txwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t = __mt76_get_txwi(dev);
+
+ if (t)
+ return t;
+
+ return mt76_alloc_txwi(dev);
+}
+
+void
+mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+{
+ if (!t)
+ return;
+
+ spin_lock_bh(&dev->lock);
+ list_add(&t->list, &dev->txwi_cache);
+ spin_unlock_bh(&dev->lock);
+}
+
+void mt76_tx_free(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t;
+
+ while ((t = __mt76_get_txwi(dev)) != NULL)
+ dma_unmap_single(dev->dev, t->dma_addr, sizeof(t->txwi),
+ DMA_TO_DEVICE);
+}
+
+static int
+mt76_txq_get_qid(struct ieee80211_txq *txq)
+{
+ if (!txq->sta)
+ return MT_TXQ_BE;
+
+ return txq->ac;
+}
+
+int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_sta *sta)
+{
+ struct mt76_queue_entry e;
+ struct mt76_txwi_cache *t;
+ struct mt76_queue_buf buf[32];
+ struct sk_buff *iter;
+ dma_addr_t addr;
+ int len;
+ u32 tx_info = 0;
+ int n, ret;
+
+ t = mt76_get_txwi(dev);
+ if (!t) {
+ ieee80211_free_txskb(dev->hw, skb);
+ return -ENOMEM;
+ }
+
+ dma_sync_single_for_cpu(dev->dev, t->dma_addr, sizeof(t->txwi),
+ DMA_TO_DEVICE);
+ ret = dev->drv->tx_prepare_skb(dev, &t->txwi, skb, q, wcid, sta,
+ &tx_info);
+ dma_sync_single_for_device(dev->dev, t->dma_addr, sizeof(t->txwi),
+ DMA_TO_DEVICE);
+ if (ret < 0)
+ goto free;
+
+ len = skb->len - skb->data_len;
+ addr = dma_map_single(dev->dev, skb->data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev->dev, addr)) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ n = 0;
+ buf[n].addr = t->dma_addr;
+ buf[n++].len = dev->drv->txwi_size;
+ buf[n].addr = addr;
+ buf[n++].len = len;
+
+ skb_walk_frags(skb, iter) {
+ if (n == ARRAY_SIZE(buf))
+ goto unmap;
+
+ addr = dma_map_single(dev->dev, iter->data, iter->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev->dev, addr))
+ goto unmap;
+
+ buf[n].addr = addr;
+ buf[n++].len = iter->len;
+ }
+
+ if (q->queued + (n + 1) / 2 >= q->ndesc - 1)
+ goto unmap;
+
+ return dev->queue_ops->add_buf(dev, q, buf, n, tx_info, skb, t);
+
+unmap:
+ ret = -ENOMEM;
+ for (n--; n > 0; n--)
+ dma_unmap_single(dev->dev, buf[n].addr, buf[n].len,
+ DMA_TO_DEVICE);
+
+free:
+ e.skb = skb;
+ e.txwi = t;
+ dev->drv->tx_complete_skb(dev, q, &e, true);
+ mt76_put_txwi(dev, t);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_tx_queue_skb);
+
+void
+mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
+ struct mt76_wcid *wcid, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct mt76_queue *q;
+ int qid = skb_get_queue_mapping(skb);
+
+ if (WARN_ON(qid >= MT_TXQ_PSD)) {
+ qid = MT_TXQ_BE;
+ skb_set_queue_mapping(skb, qid);
+ }
+
+ if (!wcid->tx_rate_set)
+ ieee80211_get_tx_rates(info->control.vif, sta, skb,
+ info->control.rates, 1);
+
+ q = &dev->q_tx[qid];
+
+ spin_lock_bh(&q->lock);
+ mt76_tx_queue_skb(dev, q, skb, wcid, sta);
+ dev->queue_ops->kick(dev, q);
+
+ if (q->queued > q->ndesc - 8)
+ ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
+ spin_unlock_bh(&q->lock);
+}
+EXPORT_SYMBOL_GPL(mt76_tx);
+
+static struct sk_buff *
+mt76_txq_dequeue(struct mt76_dev *dev, struct mt76_txq *mtxq, bool ps)
+{
+ struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&mtxq->retry_q);
+ if (skb) {
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+ if (ps && skb_queue_empty(&mtxq->retry_q))
+ ieee80211_sta_set_buffered(txq->sta, tid, false);
+
+ return skb;
+ }
+
+ skb = ieee80211_tx_dequeue(dev->hw, txq);
+ if (!skb)
+ return NULL;
+
+ return skb;
+}
+
+static void
+mt76_check_agg_ssn(struct mt76_txq *mtxq, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
+ mtxq->agg_ssn = le16_to_cpu(hdr->seq_ctrl) + 0x10;
+}
+
+static void
+mt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta,
+ struct sk_buff *skb, bool last)
+{
+ struct mt76_wcid *wcid = (struct mt76_wcid *) sta->drv_priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD];
+
+ info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
+ if (last)
+ info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+ mt76_skb_set_moredata(skb, !last);
+ mt76_tx_queue_skb(dev, hwq, skb, wcid, sta);
+}
+
+void
+mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ u16 tids, int nframes,
+ enum ieee80211_frame_release_type reason,
+ bool more_data)
+{
+ struct mt76_dev *dev = hw->priv;
+ struct sk_buff *last_skb = NULL;
+ struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD];
+ int i;
+
+ spin_lock_bh(&hwq->lock);
+ for (i = 0; tids && nframes; i++, tids >>= 1) {
+ struct ieee80211_txq *txq = sta->txq[i];
+ struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
+ struct sk_buff *skb;
+
+ if (!(tids & 1))
+ continue;
+
+ do {
+ skb = mt76_txq_dequeue(dev, mtxq, true);
+ if (!skb)
+ break;
+
+ if (mtxq->aggr)
+ mt76_check_agg_ssn(mtxq, skb);
+
+ nframes--;
+ if (last_skb)
+ mt76_queue_ps_skb(dev, sta, last_skb, false);
+
+ last_skb = skb;
+ } while (nframes);
+ }
+
+ if (last_skb) {
+ mt76_queue_ps_skb(dev, sta, last_skb, true);
+ dev->queue_ops->kick(dev, hwq);
+ }
+ spin_unlock_bh(&hwq->lock);
+}
+EXPORT_SYMBOL_GPL(mt76_release_buffered_frames);
+
+static int
+mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
+ struct mt76_txq *mtxq, bool *empty)
+{
+ struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
+ struct ieee80211_tx_info *info;
+ struct mt76_wcid *wcid = mtxq->wcid;
+ struct sk_buff *skb = NULL;
+ int n_frames = 1, limit;
+ struct ieee80211_tx_rate tx_rate;
+ bool ampdu;
+ bool probe;
+ int idx;
+
+ skb = mt76_txq_dequeue(dev, mtxq, false);
+ if (!skb) {
+ *empty = true;
+ return 0;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ if (!wcid->tx_rate_set)
+ ieee80211_get_tx_rates(txq->vif, txq->sta, skb,
+ info->control.rates, 1);
+ tx_rate = info->control.rates[0];
+
+ probe = (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+ ampdu = IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU;
+ limit = ampdu ? 16 : 3;
+
+ if (ampdu)
+ mt76_check_agg_ssn(mtxq, skb);
+
+ idx = mt76_tx_queue_skb(dev, hwq, skb, wcid, txq->sta);
+
+ if (idx < 0)
+ return idx;
+
+ do {
+ bool cur_ampdu;
+
+ if (probe)
+ break;
+
+ skb = mt76_txq_dequeue(dev, mtxq, false);
+ if (!skb) {
+ *empty = true;
+ break;
+ }
+
+ cur_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
+
+ if (ampdu != cur_ampdu ||
+ (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) {
+ skb_queue_tail(&mtxq->retry_q, skb);
+ break;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ info->control.rates[0] = tx_rate;
+
+ if (cur_ampdu)
+ mt76_check_agg_ssn(mtxq, skb);
+
+ idx = mt76_tx_queue_skb(dev, hwq, skb, wcid, txq->sta);
+ if (idx < 0)
+ return idx;
+
+ n_frames++;
+ } while (n_frames < limit);
+
+ if (!probe) {
+ hwq->swq_queued++;
+ hwq->entry[idx].schedule = true;
+ }
+
+ dev->queue_ops->kick(dev, hwq);
+
+ return n_frames;
+}
+
+static int
+mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
+{
+ struct mt76_txq *mtxq, *mtxq_last;
+ int len = 0;
+
+restart:
+ mtxq_last = list_last_entry(&hwq->swq, struct mt76_txq, list);
+ while (!list_empty(&hwq->swq)) {
+ bool empty = false;
+ int cur;
+
+ mtxq = list_first_entry(&hwq->swq, struct mt76_txq, list);
+ if (mtxq->send_bar && mtxq->aggr) {
+ struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
+ struct ieee80211_sta *sta = txq->sta;
+ struct ieee80211_vif *vif = txq->vif;
+ u16 agg_ssn = mtxq->agg_ssn;
+ u8 tid = txq->tid;
+
+ mtxq->send_bar = false;
+ spin_unlock_bh(&hwq->lock);
+ ieee80211_send_bar(vif, sta->addr, tid, agg_ssn);
+ spin_lock_bh(&hwq->lock);
+ goto restart;
+ }
+
+ list_del_init(&mtxq->list);
+
+ cur = mt76_txq_send_burst(dev, hwq, mtxq, &empty);
+ if (!empty)
+ list_add_tail(&mtxq->list, &hwq->swq);
+
+ if (cur < 0)
+ return cur;
+
+ len += cur;
+
+ if (mtxq == mtxq_last)
+ break;
+ }
+
+ return len;
+}
+
+void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq)
+{
+ int len;
+
+ if (test_bit(MT76_SCANNING, &dev->state) ||
+ test_bit(MT76_RESET, &dev->state))
+ return;
+
+ do {
+ if (hwq->swq_queued >= 4 || list_empty(&hwq->swq))
+ break;
+
+ len = mt76_txq_schedule_list(dev, hwq);
+ } while (len > 0);
+}
+EXPORT_SYMBOL_GPL(mt76_txq_schedule);
+
+void mt76_txq_schedule_all(struct mt76_dev *dev)
+{
+ int i;
+
+ for (i = 0; i <= MT_TXQ_BK; i++) {
+ struct mt76_queue *q = &dev->q_tx[i];
+
+ spin_lock_bh(&q->lock);
+ mt76_txq_schedule(dev, q);
+ spin_unlock_bh(&q->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_txq_schedule_all);
+
+void mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta,
+ bool send_bar)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct ieee80211_txq *txq = sta->txq[i];
+ struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
+
+ spin_lock_bh(&mtxq->hwq->lock);
+ mtxq->send_bar = mtxq->aggr && send_bar;
+ if (!list_empty(&mtxq->list))
+ list_del_init(&mtxq->list);
+ spin_unlock_bh(&mtxq->hwq->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_stop_tx_queues);
+
+void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
+{
+ struct mt76_dev *dev = hw->priv;
+ struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
+ struct mt76_queue *hwq = mtxq->hwq;
+
+ spin_lock_bh(&hwq->lock);
+ if (list_empty(&mtxq->list))
+ list_add_tail(&mtxq->list, &hwq->swq);
+ mt76_txq_schedule(dev, hwq);
+ spin_unlock_bh(&hwq->lock);
+}
+EXPORT_SYMBOL_GPL(mt76_wake_tx_queue);
+
+void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
+{
+ struct mt76_txq *mtxq;
+ struct mt76_queue *hwq;
+ struct sk_buff *skb;
+
+ if (!txq)
+ return;
+
+ mtxq = (struct mt76_txq *) txq->drv_priv;
+ hwq = mtxq->hwq;
+
+ spin_lock_bh(&hwq->lock);
+ if (!list_empty(&mtxq->list))
+ list_del(&mtxq->list);
+ spin_unlock_bh(&hwq->lock);
+
+ while ((skb = skb_dequeue(&mtxq->retry_q)) != NULL)
+ ieee80211_free_txskb(dev->hw, skb);
+}
+EXPORT_SYMBOL_GPL(mt76_txq_remove);
+
+void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq)
+{
+ struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
+
+ INIT_LIST_HEAD(&mtxq->list);
+ skb_queue_head_init(&mtxq->retry_q);
+
+ mtxq->hwq = &dev->q_tx[mt76_txq_get_qid(txq)];
+}
+EXPORT_SYMBOL_GPL(mt76_txq_init);
diff --git a/drivers/net/wireless/mediatek/mt76/util.c b/drivers/net/wireless/mediatek/mt76/util.c
new file mode 100644
index 000000000000..0c35b8db58cd
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/util.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * 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 <linux/module.h>
+#include "mt76.h"
+
+bool __mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
+ int timeout)
+{
+ u32 cur;
+
+ timeout /= 10;
+ do {
+ cur = dev->bus->rr(dev, offset) & mask;
+ if (cur == val)
+ return true;
+
+ udelay(10);
+ } while (timeout-- > 0);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(__mt76_poll);
+
+bool __mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
+ int timeout)
+{
+ u32 cur;
+
+ timeout /= 10;
+ do {
+ cur = dev->bus->rr(dev, offset) & mask;
+ if (cur == val)
+ return true;
+
+ usleep_range(10000, 20000);
+ } while (timeout-- > 0);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(__mt76_poll_msec);
+
+int mt76_wcid_alloc(unsigned long *mask, int size)
+{
+ int i, idx = 0, cur;
+
+ for (i = 0; i < size / BITS_PER_LONG; i++) {
+ idx = ffs(~mask[i]);
+ if (!idx)
+ continue;
+
+ idx--;
+ cur = i * BITS_PER_LONG + idx;
+ if (cur >= size)
+ break;
+
+ mask[i] |= BIT(idx);
+ return cur;
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(mt76_wcid_alloc);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h
new file mode 100644
index 000000000000..018d475504a2
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/util.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT76_UTIL_H
+#define __MT76_UTIL_H
+
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+#define MT76_INCR(_var, _size) \
+ _var = (((_var) + 1) % _size)
+
+int mt76_wcid_alloc(unsigned long *mask, int size);
+
+static inline void
+mt76_wcid_free(unsigned long *mask, int idx)
+{
+ mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG);
+}
+
+static inline void
+mt76_skb_set_moredata(struct sk_buff *skb, bool enable)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (enable)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ else
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+}
+
+#endif
--
2.11.0
^ permalink raw reply related
* [PATCH v5 1/3] Documentation: dt: net: add mt76 wireless device binding
From: Felix Fietkau @ 2017-02-07 20:20 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, sgruszka, devicetree, Rob Herring
In-Reply-To: <20170207202035.64224-1-nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../bindings/net/wireless/mediatek,mt76.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
new file mode 100644
index 000000000000..19522ab97d62
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
@@ -0,0 +1,24 @@
+* MediaTek mt76xx devices
+
+This node provides properties for configuring the MediaTek mt76xx wireless
+device. The node is expected to be specified as a child node of the PCI
+controller to which the wireless chip is connected.
+
+Optional properties:
+
+- mac-address: See ethernet.txt in the parent directory
+- local-mac-address: See ethernet.txt in the parent directory
+- ieee80211-freq-limit: See ieee80211.txt
+- mediatek,mtd-eeprom: Specify a MTD partition + offset containing EEPROM data
+
+&pcie {
+ status = "okay";
+
+ pcie0 {
+ wifi@0,0 {
+ reg = <0x0000 0 0 0 0>;
+ ieee80211-freq-limit = <5000000 6000000>;
+ mediatek,mtd-eeprom = <&factory 0x8000>;
+ };
+ };
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v5 0/3] mt76: add new wireless driver for MediaTek MT76x2 PCIe chips
From: Felix Fietkau @ 2017-02-07 20:20 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, sgruszka
Changes since v4:
- Cleanups suggested by Stanislaw Gruszka
- Device tree fixes suggested by Rob Herring
- EEPROM MAC address parsing fix
Changes since v3:
- DFS fixes
- stability fixes
- use wiphy_read_of_freq_limits
Changes since v2:
- lots of checkpatch cleanups
- various tx path (and other) fixes
- use the new bitfield API
- documented device tree bindings
Felix Fietkau (3):
Documentation: dt: net: add mt76 wireless device binding
mt76: add common code shared between multiple chipsets
mt76: add driver code for MT76x2e
.../bindings/net/wireless/mediatek,mt76.txt | 24 +
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/debugfs.c | 76 ++
drivers/net/wireless/mediatek/mt76/dma.c | 451 ++++++++++++
drivers/net/wireless/mediatek/mt76/dma.h | 38 +
drivers/net/wireless/mediatek/mt76/eeprom.c | 112 +++
drivers/net/wireless/mediatek/mt76/mac80211.c | 344 +++++++++
drivers/net/wireless/mediatek/mt76/mmio.c | 61 ++
drivers/net/wireless/mediatek/mt76/mt76.h | 355 ++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2.h | 223 ++++++
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 | 184 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_dma.h | 68 ++
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 644 +++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h | 181 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 784 +++++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mac.c | 738 +++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mac.h | 189 +++++
drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 534 ++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c | 452 ++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h | 136 ++++
drivers/net/wireless/mediatek/mt76/mt76x2_pci.c | 109 +++
drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 691 ++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76x2_regs.h | 566 +++++++++++++++
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 | 255 +++++++
drivers/net/wireless/mediatek/mt76/trace.c | 23 +
drivers/net/wireless/mediatek/mt76/trace.h | 71 ++
drivers/net/wireless/mediatek/mt76/tx.c | 511 ++++++++++++++
drivers/net/wireless/mediatek/mt76/util.c | 78 ++
drivers/net/wireless/mediatek/mt76/util.h | 44 ++
38 files changed, 8930 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
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/debugfs.c
create mode 100644 drivers/net/wireless/mediatek/mt76/dma.c
create mode 100644 drivers/net/wireless/mediatek/mt76/dma.h
create mode 100644 drivers/net/wireless/mediatek/mt76/eeprom.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mac80211.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mmio.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt76.h
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
create mode 100644 drivers/net/wireless/mediatek/mt76/trace.c
create mode 100644 drivers/net/wireless/mediatek/mt76/trace.h
create mode 100644 drivers/net/wireless/mediatek/mt76/tx.c
create mode 100644 drivers/net/wireless/mediatek/mt76/util.c
create mode 100644 drivers/net/wireless/mediatek/mt76/util.h
--
2.11.0
^ permalink raw reply
* Re: [PATCH net] brcmfmac: clear skb head state on xmit
From: Arend Van Spriel @ 2017-02-07 19:23 UTC (permalink / raw)
To: Paolo Abeni, Kalle Valo
Cc: linux-wireless, brcm80211-dev-list.pdl, Franky Lin,
hante Meuleman
In-Reply-To: <e5bb3c8e8be4417cc648cfb53add2c89d2c89aa9.1486486128.git.pabeni@redhat.com>
On 7-2-2017 17:50, Paolo Abeni wrote:
> the skbs can be held by the driver for a long time, so we need
> to clear any state on xmit to avoid hanging other subsystems.
> The skbs are already orphaned later in cmsg code, so we just
> need to clear the nf/dst/secpath.
> Do it early, while the relevant entries are hopefully still
> hot in the cache.
What is this about really? A bit more background about the issue might
help understanding the need for this patch. Is this really specific to
brcmfmac. For instance is something similar already done in mac80211?
Regards,
Arend
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
> drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> index 9e6f60a..5a8d57b 100644
> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> @@ -22,6 +22,7 @@
> #include <net/rtnetlink.h>
> #include <net/addrconf.h>
> #include <net/ipv6.h>
> +#include <net/xfrm.h>
> #include <brcmu_utils.h>
> #include <brcmu_wifi.h>
>
> @@ -243,6 +244,13 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
> if ((skb->priority == 0) || (skb->priority > 7))
> skb->priority = cfg80211_classify8021d(skb, NULL);
>
> + /* we can keep the skb for a long time; avoid starving other
> + * subsystems
> + */
> + nf_reset(skb);
> + skb_dst_drop(skb);
> + secpath_reset(skb);
> +
> ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb);
> if (ret < 0)
> brcmf_txfinalize(ifp, skb, false);
>
^ permalink raw reply
* Re: [PATCH v4 1/3] Documentation: dt: net: add mt76 wireless device binding
From: Rob Herring @ 2017-02-07 18:43 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, kvalo, devicetree
In-Reply-To: <20170202115208.8614-2-nbd@nbd.name>
On Thu, Feb 02, 2017 at 12:52:06PM +0100, Felix Fietkau wrote:
> Signed-off-by: Felix Fietkau <nbd@nbd.name>
> ---
> .../bindings/net/wireless/mediatek,mt76.txt | 25 ++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
>
> diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
> new file mode 100644
> index 000000000000..2be480c0eab8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
> @@ -0,0 +1,25 @@
> +* MediaTek mt76xx devices
> +
> +This node provides properties for configuring the MediaTek mt76xx wireless
> +device. The node is expected to be specified as a child node of the PCI
> +controller to which the wireless chip is connected.
> +
> +Optional properties:
> +
> +- mac-address: See ethernet.txt in the parent directory
> +- local-mac-address: See ethernet.txt in the parent directory
> +- ieee80211-freq-limit: See ieee80211.txt
> +- mediatek,mtd-eeprom: Specify a MTD partition + offset containing EEPROM data
> +
> +&pcie {
> + status = "okay";
> +
> + pcie0 {
> + mt76@0,0 {
wifi@0,0
> + reg = <0x0000 0 0 0 0>;
> + device_type = "pci";
This only applies to PCI bridge nodes, so drop.
> + ieee80211-freq-limit = <5000000 6000000>;
> + mediatek,mtd-eeprom = <&factory 0x8000>;
> + };
> + };
> +};
> --
> 2.11.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v9] Add new mac80211 driver mwlwifi.
From: Steve deRosier @ 2017-02-07 19:12 UTC (permalink / raw)
To: David Lin
Cc: Kalle Valo, linux-wireless@vger.kernel.org, Johannes Berg,
Chor Teck Law, James Lin, Pete Hsieh
In-Reply-To: <260962939eeb4dbbb6e462cc010aac21@SC-EXCH02.marvell.com>
Hi David,
First off, I wanted to say thank-you for your work and effort in trying
to get mwlwifi upstream. My comments are in-line with my general notes
afterwards.
On Tue, Dec 20, 2016 at 8:11 PM, David Lin <dlin@marvell.com> wrote:
> diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> new file mode 100644
> index 0000000..b4c3eb3
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2006-2016, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License"). You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description: This file defines debug fs related functions. */
> +
> +#ifndef _MWL_DEBUGFS_H_
> +#define _MWL_DEBUGFS_H_
> +
> +void mwl_debugfs_init(struct ieee80211_hw *hw);
> +void mwl_debugfs_remove(struct ieee80211_hw *hw);
> +
You should guard these so they define to empty functions if CONFIG_DEBUG_FS
isn't enabled so they can be used by a caller without explicit guards.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/dev.h b/drivers/net/wireless/marvell/mwlwifi/dev.h
> new file mode 100644
> index 0000000..c7b10ac
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/dev.h
...
> +struct mwl_ampdu_stream {
> + struct ieee80211_sta *sta;
> + u8 tid;
> + u8 state;
> + u8 idx;
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +#define MAC_REG_ADDR_PCI(offset) ((priv->iobase1 + 0xA000) + (offset))
> +
> +#define MWL_ACCESS_MAC 1
> +#define MWL_ACCESS_RF 2
> +#define MWL_ACCESS_BBP 3
> +#define MWL_ACCESS_CAU 4
> +#define MWL_ACCESS_ADDR0 5
> +#define MWL_ACCESS_ADDR1 6
> +#define MWL_ACCESS_ADDR 7
> +#endif
OK, I get that you're only using the above in the debugfs functions, but
as for generic register access functions, these would be valid no matter if
CONFIG_DEBUG_FS is defined. Having the guard here just seems to complicate
things.
> +
> +struct mwl_priv {
> + struct ieee80211_hw *hw;
> + struct firmware *fw_ucode;
> + bool fw_device_pwrtbl;
> + bool forbidden_setting;
> + bool regulatory_set;
> + u32 fw_region_code;
> + char fw_alpha2[2];
> + u8 number_of_channels;
> + struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> + int chip_type;
> +
> + struct device_node *dt_node;
> + struct device_node *pwr_node;
> + bool disable_2g;
> + bool disable_5g;
> + int antenna_tx;
> + int antenna_rx;
> +
> + struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> + bool cdd;
> + u16 txantenna2;
> + u8 powinited;
> + u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */
> + u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers */
> +
> + struct pci_dev *pdev;
> + struct device *dev;
> + void __iomem *iobase0; /* MEM Base Address Register 0 */
> + void __iomem *iobase1; /* MEM Base Address Register 1 */
> + u32 next_bar_num;
> +
> + struct mutex fwcmd_mutex; /* for firmware command */
> + unsigned short *pcmd_buf; /* pointer to CmdBuf (virtual) */
> + dma_addr_t pphys_cmd_buf; /* pointer to CmdBuf (physical) */
> + bool in_send_cmd;
> +
> + int irq;
> + struct mwl_hw_data hw_data; /* Adapter HW specific info */
> +
> + /* various descriptor data */
> + /* for tx descriptor data */
> + spinlock_t tx_desc_lock ____cacheline_aligned_in_smp;
> + struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];
> + struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];
> + struct sk_buff_head delay_q;
> + /* number of descriptors owned by fw at any one time */
> + int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
> +
> + struct tasklet_struct tx_task;
> + struct tasklet_struct tx_done_task;
> + struct tasklet_struct rx_task;
> + struct tasklet_struct qe_task;
> + int txq_limit;
> + bool is_tx_done_schedule;
> + int recv_limit;
> + bool is_rx_schedule;
> + bool is_qe_schedule;
> + u32 qe_trigger_num;
> + unsigned long qe_trigger_time;
> +
> + struct timer_list period_timer;
> +
> + s8 noise; /* Most recently reported noise in dBm */
> + struct ieee80211_supported_band band_24;
> + struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];
> + struct ieee80211_rate rates_24[BAND_24_RATE_NUM];
> + struct ieee80211_supported_band band_50;
> + struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];
> + struct ieee80211_rate rates_50[BAND_50_RATE_NUM];
> +
> + u32 ap_macids_supported;
> + u32 sta_macids_supported;
> + u32 macids_used;
> + u32 running_bsses; /* bitmap of running BSSes */
> +
> + struct {
> + spinlock_t vif_lock; /* for private interface info */
> + struct list_head vif_list; /* List of interfaces. */
> + } ____cacheline_aligned_in_smp;
> +
> + struct {
> + spinlock_t sta_lock; /* for private sta info */
> + struct list_head sta_list; /* List of stations */
> + } ____cacheline_aligned_in_smp;
> +
> + bool radio_on;
> + bool radio_short_preamble;
> + bool wmm_enabled;
> + struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES];
> +
> + /* ampdu stream information */
> + /* for ampdu stream */
> + struct {
> + spinlock_t stream_lock; /* for BA stream */
> + struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];
> + } ____cacheline_aligned_in_smp;
> + struct work_struct watchdog_ba_handle;
> +
> + bool csa_active;
> + struct work_struct chnl_switch_handle;
> + enum nl80211_dfs_regions dfs_region;
> + u16 dfs_chirp_count_min;
> + u16 dfs_chirp_time_interval;
> + u16 dfs_pw_filter;
> + u16 dfs_min_num_radar;
> + u16 dfs_min_pri_count;
> +
> + struct thermal_cooling_device *cdev;
> + u32 throttle_state;
> + u32 quiet_period;
> + int temperature;
> +
> +#ifdef CONFIG_DEBUG_FS
> + struct dentry *debugfs_phy;
> + u32 reg_type;
> + u32 reg_offset;
> + u32 reg_value;
> + int tx_desc_num;
> +#endif
We're saving a few bytes here at the cost of some complexity. I could go
either way, but I hate when priv structures mutate.
> +/* Defined in mac80211.c. */
> +extern const struct ieee80211_ops mwl_mac80211_ops;
Does this need to be in dev.h?
> diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
> new file mode 100644
> index 0000000..9c3ccf9
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
> @@ -0,0 +1,2837 @@
> +/*
> + * Copyright (C) 2006-2016, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License"). You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description: This file implements firmware host command related
> + * functions.
> + */
> +
> +#include <linux/etherdevice.h>
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "fwcmd.h"
> +#include "hostcmd.h"
> +
> +#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000
> +#define MAX_WAIT_GET_HW_SPECS_ITERATONS 3
> +
> +struct cmd_header {
> + __le16 command;
> + __le16 len;
> +} __packed;
> +
> +static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv)
> +{
> + u32 regval;
> +
> + regval = readl(priv->iobase1 + MACREG_REG_INT_CODE);
Couldn't the above be one line?
> +
> +static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
> + u16 band, u16 width, u16 sub_ch)
> +{
> + struct hostcmd_cmd_802_11_tx_power *pcmd;
> + int i;
> +
> + pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
Often happens, so I probably won't catch them all, but this could likewise
be done in one line. Then again... line length issues, so 50/50 on that.
> +
> +static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel)
> +{
> + u8 act_primary = ACT_PRIMARY_CHAN_0;
> +
> + switch (channel) {
> + case 36:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 40:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 44:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 48:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 52:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 56:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 60:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 64:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 100:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 104:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 108:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 112:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 116:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 120:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 124:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 128:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 132:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 136:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 140:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 144:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 149:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 153:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 157:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 161:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + }
> +
> + return act_primary;
> +}
> +
Ignorance speaking here perhaps, but the above looks like something that
nearly every driver would need to deal with. Isn't there a helper function
in mac80211 or some better way to do this than explicitly doing a switch
logic lookup?
> +int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct mwl_priv *priv = hw->priv;
> + struct mwl_vif *mwl_vif;
> + struct hostcmd_cmd_set_new_stn *pcmd;
> + u32 rates;
> +
> + mwl_vif = mwl_dev_get_vif(vif);
> +
> + pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
> +
> + mutex_lock(&priv->fwcmd_mutex);
> +
> + memset(pcmd, 0x00, sizeof(*pcmd));
> + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
> + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> + pcmd->cmd_hdr.macid = mwl_vif->macid;
> +
> + pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + pcmd->aid = 0;
> + pcmd->stn_id = 0;
> + } else {
> + pcmd->aid = cpu_to_le16(sta->aid);
> + pcmd->stn_id = cpu_to_le16(sta->aid);
> + }
> + ether_addr_copy(pcmd->mac_addr, sta->addr);
> +
> + if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
> + rates = sta->supp_rates[NL80211_BAND_2GHZ];
> + else
> + rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
> + pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
> +
> + if (sta->ht_cap.ht_supported) {
> + pcmd->peer_info.ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
> + pcmd->peer_info.ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
> + pcmd->peer_info.ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
> + pcmd->peer_info.ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
> + pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
> + pcmd->peer_info.mac_ht_param_info =
> + (sta->ht_cap.ampdu_factor & 3) |
> + ((sta->ht_cap.ampdu_density & 7) << 2);
> + }
> +
> + if (sta->vht_cap.vht_supported) {
> + pcmd->peer_info.vht_max_rx_mcs =
> + cpu_to_le32(*((u32 *)
> + &sta->vht_cap.vht_mcs.rx_mcs_map));
> + pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
> + pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
> + }
> +
> + pcmd->is_qos_sta = sta->wme;
> + pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1));
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> +
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> + }
OK, time to ask. Why are we telling the firmware that we're connected to
ourselves? I would presume the firmware already knows our MAC address.
I noticed this originally because there's a nasty bug with
recycling the command buffer (for sdio, it's not relevant for this driver)
and in doing experiments I noticed that throughput significantly increases
in STA mode if we just leave out the entire clause.
In briefly examining the firmware source I see no reason to do this, but
there's a hidden chunk and I don't know what the hardware itself does with
the MAC table.
So, why is it necessary?
> +int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif, u8 *addr)
> +{
> + struct mwl_priv *priv = hw->priv;
> + struct mwl_vif *mwl_vif;
> + struct hostcmd_cmd_set_new_stn *pcmd;
> +
> + mwl_vif = mwl_dev_get_vif(vif);
> +
> + pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
> +
> + mutex_lock(&priv->fwcmd_mutex);
> +
> + memset(pcmd, 0x00, sizeof(*pcmd));
> + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
> + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> + pcmd->cmd_hdr.macid = mwl_vif->macid;
> +
> + pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE);
> + ether_addr_copy(pcmd->mac_addr, addr);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> +
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> + }
> +
Ditto.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
> new file mode 100644
> index 0000000..f4d5fa1
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
...
> +
> +static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv)
> +{
> + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
> +
> + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
> +
> + writel(MACREG_H2ARIC_BIT_DOOR_BELL,
> + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +}
> +
> +static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv)
> +{
> + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
> +
> + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
> +
> + writel(MACREG_H2ARIC_BIT_DOOR_BELL,
> + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +}
> +
Unless I'm mistaken the above two functions are 100% identical. In my version
I collapsed them to a single one and it works fine.
> +int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
> +{
...
> +
> + while (size_fw_downloaded < fw->size) {
> + len = readl(priv->iobase1 + 0xc40);
> +
> + if (!len)
> + break;
> +
> + /* this copies the next chunk of fw binary to be delivered */
> + memcpy((char *)&priv->pcmd_buf[0],
> + (fw->data + size_fw_downloaded), len);
> +
> + /* this function writes pdata to c10, then write 2 to c18 */
> + mwl_fwdl_trig_pcicmd_bootcode(priv);
> +
> + /* this is arbitrary per your platform; we use 0xffff */
> + curr_iteration = FW_MAX_NUM_CHECKS;
> +
> + /* NOTE: the following back to back checks on C1C is time
> + * sensitive, hence may need to be tweaked dependent on host
> + * processor. Time for SC2 to go from the write of event 2 to
> + * C1C == 2 is ~1300 nSec. Hence the checkings on host has to
> + * consider how efficient your code can be to meet this timing,
> + * or you can alternatively tweak this routines to fit your
> + * platform
> + */
> + do {
> + int_code = readl(priv->iobase1 + 0xc1c);
> + if (int_code != 0)
> + break;
> + cond_resched();
> + curr_iteration--;
> + } while (curr_iteration);
> +
There's something fishy with the above. Having to "tweak" driver timing based
on platform is a huge red flag. There's got to be something better than the
above.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
...
> +static int mwl_mac80211_config(struct ieee80211_hw *hw,
> + u32 changed)
> +{
> + struct ieee80211_conf *conf = &hw->conf;
> + int rc;
> +
> + wiphy_debug(hw->wiphy, "change: 0x%x\n", changed);
> +
> + if (conf->flags & IEEE80211_CONF_IDLE)
> + rc = mwl_fwcmd_radio_disable(hw);
> + else
> + rc = mwl_fwcmd_radio_enable(hw);
> +
> + if (rc)
> + goto out;
> +
> + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
> + int rate = 0;
> +
> + if (conf->chandef.chan->band == NL80211_BAND_2GHZ) {
Causes compile warning. Should be IEEE80211_BAND_2GHZ.
> + mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED);
> + mwl_fwcmd_set_linkadapt_cs_mode(hw,
> + LINK_CS_STATE_CONSERV);
> + rate = mwl_rates_24[0].hw_value;
> + } else if (conf->chandef.chan->band == NL80211_BAND_5GHZ) {
Causes compile warning. Should be IEEE80211_BAND_5GHZ.
> +static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_bss_conf *info,
> + u32 changed)
> +{
> + if (changed & BSS_CHANGED_ERP_PREAMBLE)
> + mwl_fwcmd_set_radio_preamble(hw,
> + vif->bss_conf.use_short_preamble);
> +
> + if (changed & BSS_CHANGED_BASIC_RATES) {
> + int idx;
> + int rate;
> +
> + /* Use lowest supported basic rate for multicasts
> + * and management frames (such as probe responses --
> + * beacons will always go out at 1 Mb/s).
> + */
> + idx = ffs(vif->bss_conf.basic_rates);
> + if (idx)
> + idx--;
> +
> + if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
Causes compile warning. Should be IEEE80211_BAND_2GHZ.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/main.c b/drivers/net/wireless/marvell/mwlwifi/main.c
> new file mode 100644
...
> +#include <linux/module.h>
> +#ifdef CONFIG_OF
> +#include <linux/of.h>
> +#endif
Isn't of.h internally guarded?
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "fwdl.h"
> +#include "fwcmd.h"
> +#include "tx.h"
> +#include "rx.h"
> +#include "isr.h"
> +#include "thermal.h"
> +#ifdef CONFIG_DEBUG_FS
> +#include "debugfs.h"
> +#endif
Your debugfs.h should be internally guarded. More later...
> +
> +#define MWL_DESC "Marvell 802.11ac Wireless Network Driver"
> +#define MWL_DEV_NAME "Marvell 802.11ac Adapter"
> +
> +#define FILE_PATH_LEN 64
> +#define CMD_BUF_SIZE 0x4000
> +
> +static struct pci_device_id mwl_pci_id_tbl[] = {
> + { PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, },
> + { PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, },
> + { },
> +};
> +
> +static struct mwl_chip_info mwl_chip_tbl[] = {
> + [MWL8864] = {
> + .part_name = "88W8864",
> + .fw_image = "mwlwifi/88W8864.bin",
> + .antenna_tx = ANTENNA_TX_4_AUTO,
> + .antenna_rx = ANTENNA_RX_4_AUTO,
> + },
> + [MWL8897] = {
> + .part_name = "88W8897",
> + .fw_image = "mwlwifi/88W8897.bin",
> + .antenna_tx = ANTENNA_TX_2,
> + .antenna_rx = ANTENNA_RX_2,
> + },
> +};
> +
> +static const struct ieee80211_channel mwl_channels_24[] = {
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },
> +};
> +
So, interesting thing... there's 62 uses of NL80211_BAND_x in this driver, but
only the few spots I mentioned cause warnings. I notice in the most recent
internal drop you've changed the above to IEEE80211_BAND_2GHZ. I wonder if that
is what should be done everywhere?
> +static const struct ieee80211_rate mwl_rates_24[] = {
> + { .bitrate = 10, .hw_value = 2, },
> + { .bitrate = 20, .hw_value = 4, },
> + { .bitrate = 55, .hw_value = 11, },
> + { .bitrate = 110, .hw_value = 22, },
> + { .bitrate = 220, .hw_value = 44, },
> + { .bitrate = 60, .hw_value = 12, },
> + { .bitrate = 90, .hw_value = 18, },
> + { .bitrate = 120, .hw_value = 24, },
> + { .bitrate = 180, .hw_value = 36, },
> + { .bitrate = 240, .hw_value = 48, },
> + { .bitrate = 360, .hw_value = 72, },
> + { .bitrate = 480, .hw_value = 96, },
> + { .bitrate = 540, .hw_value = 108, },
> +};
> +
> +static const struct ieee80211_channel mwl_channels_50[] = {
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
> +};
Ditto.
...
> +
> +static void mwl_process_of_dts(struct mwl_priv *priv)
> +{
> +#ifdef CONFIG_OF
So a more common idiom in the drivers is:
#ifdef CONFIG_OF
define process of_dts function {}
#else
define EMPTY of_dts function {}
#endif
I guess it's the same effect either way. I don't much care as long as
mwl_process_of_dts() can be called without guards, which it can. But I
thought I should mention it incase anyone else cares. And this is not
consistent with how you have done the same thing for CONFIG_DEBUG_FS
> + struct property *prop;
> + u32 prop_value;
> +
> + priv->dt_node =
> + of_find_node_by_name(pci_bus_to_OF_node(priv->pdev->bus),
> + "mwlwifi");
> + if (!priv->dt_node)
> + return;
> +
> + /* look for all matching property names */
> + for_each_property_of_node(priv->dt_node, prop) {
> + if (strcmp(prop->name, "marvell,2ghz") == 0)
> + priv->disable_2g = true;
> + if (strcmp(prop->name, "marvell,5ghz") == 0)
> + priv->disable_5g = true;
> + if (strcmp(prop->name, "marvell,chainmask") == 0) {
> + prop_value = be32_to_cpu(*((__be32 *)prop->value));
> + if (prop_value == 2)
> + priv->antenna_tx = ANTENNA_TX_2;
> +
> + prop_value = be32_to_cpu(*((__be32 *)
> + (prop->value + 4)));
> + if (prop_value == 2)
> + priv->antenna_rx = ANTENNA_RX_2;
> + }
> + }
> +
> + priv->pwr_node = of_find_node_by_name(priv->dt_node,
> + "marvell,powertable");
> +#endif
> +}
AFAICT, there's no documentation for these DT bindings. The mwlwifi node
and the marvell,powertable both need full documentation in
Documentation/devicetree/bindings/... .
Frankly I have a feeling I'm going to need these DT nodes and I'd like to not
have to guess-and-check based on the code.
...
> +static int mwl_wl_init(struct mwl_priv *priv)
> +{
> + struct ieee80211_hw *hw;
> + int rc;
> + int i;
> +
> + hw = priv->hw;
> +
> + hw->extra_tx_headroom = SYSADPT_MIN_BYTES_HEADROOM;
> + hw->queues = SYSADPT_TX_WMM_QUEUES;
> +
> + /* Set rssi values to dBm */
> + ieee80211_hw_set(hw, SIGNAL_DBM);
> + ieee80211_hw_set(hw, HAS_RATE_CONTROL);
> +
> + /* Ask mac80211 not to trigger PS mode
> + * based on PM bit of incoming frames.
> + */
> + ieee80211_hw_set(hw, AP_LINK_PS);
> +
> + ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK);
> + ieee80211_hw_set(hw, MFP_CAPABLE);
> +
> + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
> + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
> +
> + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
> +
> + hw->vif_data_size = sizeof(struct mwl_vif);
> + hw->sta_data_size = sizeof(struct mwl_sta);
> +
> + priv->ap_macids_supported = 0x0000ffff;
> + priv->sta_macids_supported = 0x00010000;
How about we document what these magic numbers are? A nice named constant
at least would be nice.
> +static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
...
> + wiphy_info(priv->hw->wiphy, "%s TX antennas, %s RX antennas\n",
> + (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? "4" : "2",
> + (priv->antenna_rx == ANTENNA_RX_4_AUTO) ? "4" : "2");
> +
> +#ifdef CONFIG_DEBUG_FS
> + mwl_debugfs_init(hw);
The guards should be internal to mwl_debugfs_init() so we don't have to
guard it when we call it. Much like mwl_process_of_dts() is able to be called
and compiles out if CONFIG_OF isn't defined, mwl_debugfs_init() should have
the guards internal to debugfs.h/debugfs.c and we shouldn't need to worry
about it when we call it.
> +static void mwl_remove(struct pci_dev *pdev)
> +{
> + struct ieee80211_hw *hw = pci_get_drvdata(pdev);
> + struct mwl_priv *priv;
> +
> + if (!hw)
> + return;
> +
> + priv = hw->priv;
> +
> + mwl_wl_deinit(priv);
> + pci_set_drvdata(pdev, NULL);
> + ieee80211_free_hw(hw);
> + pci_disable_device(pdev);
> +
> +#ifdef CONFIG_DEBUG_FS
> + mwl_debugfs_remove(hw);
> +#endif
As previously commented on.
> +++ b/drivers/net/wireless/marvell/mwlwifi/thermal.c
...
> +static SENSOR_DEVICE_ATTR(temp1_input, 0444, mwl_thermal_show_temp,
> + NULL, 0);
> +
Should use S_IRUGO instead of numeric 0444.
OK, that's it for specifics. I know a number of them are just nits.
A few general comments:
* I saw it it quite a bit, but didn't comment on it every time: there's
many places where a variable declaration can be combined with its
initial assignment.
* I happen to concur with Johannes' comments regarding the IEs and
your beacon processing. This is a significant issue, with potential for big
bugs down the road. At the very least, it's a maintenance headache.
>From my perspective, I'd consider it a firmware bug if there's no way
around it. Is the firmware going to strip the IEs that hostapd happens
to add to the beacons? Is there some "passthrough" or some other way
that it can be reconciled?
I strongly suspect there's better ways to handle it, even without
changing the firmware, but I haven't yet taken a look to see if there is.
In any case, while there's stuff I wouldn't mind seeing changed, I rather
see it go in sooner rather than later so I and others can contribute
on top of it, instead of waiting to see it "perfect" first.
Please add my reviewed-by. If we're waiting on a v10,
do you have an ETA?
Thanks,
- Steve
^ permalink raw reply
* Re: rtlwifi: rtl8192c_common: "BUG: KASAN: slab-out-of-bounds"
From: Dmitry Osipenko @ 2017-02-07 17:42 UTC (permalink / raw)
To: Tobias Guggenmos, Larry Finger; +Cc: linux-wireless, Linux Kernel Mailing List
In-Reply-To: <1685531.kB3gARbWZx@slartibartfas>
On 07.02.2017 20:22, Tobias Guggenmos wrote:
> Am Sonntag, 5. Februar 2017, 11:30:30 CET schrieb Larry Finger:
>> On 02/05/2017 05:34 AM, Dmitry Osipenko wrote:
>>> BTW, I have an issue with the 8192cu: WiFi stops to work after a while
>>> (3-15 minutes) if I enable WMM QoS on the AP. There is nothing suspicious
>>> in KMSG, connection is up but no packets go in/out. I tried to enable
>>> debug messages in the driver, so when the WiFi stops to work I see that
>>> some "temperature/led" notify still going on in the driver, but nothing
>>> happens when I try to initiate a transfer (say to open a web page) - the
>>> log is silent, like the requests are getting stuck/dropped somewhere
>>> before reaching the driver. Is it a known issue? With the QoS disabled
>>> everything works hunky-dory, however I get 2x-4x faster download speed
>>> with QoS enabled (while it works.)
>>>
>>> I noticed that rtl92c_init_edca_param() isn't wired in the driver, so I
>>> suppose the QoS isn't implemented yet, right?
>>>
>>> If it is an expected behaviour, I think at least printing a warning
>>> message in the KMSG like "QoS unimplemented, you may expect problems"
>>> should be good enough to avoid confusion.
>>
>> As you have already seen, I decided to defer the more invasive patch. When
>> backporting to stable, the smaller the change the better.
>>
>> I have no knowledge of the internals of the RTL8192CU chip. As a result, the
>> kinds of changes I can make are limited. I do know that the chip does
>> implement QoS. I also noticed that the set_qos() callback routine was very
>> different in rtl8192ce than in rtl8192cu. Attached is an untested patch to
>> make the CU routine look like the CE version. Please see if it makes a
>> difference.
>>
>> Driver rtl8192cu has never been maintained by Realtek, and it will likely be
>> removed from the kernel in the next few cycles. As you are running a new
>> kernel, I would recommend rtl8xxxu instead. That driver has high
>> reliability, and the speed is improving. Your other option would be a
>> driver offered by the vendor of your particular device. Realtek used to
>> have these drivers on their web site, but they now seem to have been
>> removed. If your vendor does not have a driver,
>> http://www.edimax.com/edimax/mw/cufiles/files/download/Driver_Utility/trans
>> fer/Wireless/NIC/EW-7811Un/EW-7811Un_Linux_driver_v1.0.0.5.zip should work.
>>
>> Larry
>
> On my Realtek RTL8188CE card (using the rtl8192ce driver) the patch seems to
> fix the Issue (on Kernel 4.9.0).
>
> In contrast to what Dmitry Osipenko experienced, before the patch was applied,
> the WIFI usually crashed already a few seconds instead of 3-15 minutes after
> connecting to a network.
>
The QoS issue is unrelated to the original bug. I think you are referring to the
"reorder_private_data.patch" here, it shouldn't affect anything other than the
USB. Maybe some other memory corruption is going on?
--
Dmitry
^ permalink raw reply
* Re: rtlwifi: rtl8192c_common: "BUG: KASAN: slab-out-of-bounds"
From: Tobias Guggenmos @ 2017-02-07 17:22 UTC (permalink / raw)
To: Larry Finger; +Cc: Dmitry Osipenko, linux-wireless, Linux Kernel Mailing List
In-Reply-To: <92f6dcb7-bbee-533a-7d49-21670286a3f3@lwfinger.net>
[-- Attachment #1: Type: text/plain, Size: 2649 bytes --]
Am Sonntag, 5. Februar 2017, 11:30:30 CET schrieb Larry Finger:
> On 02/05/2017 05:34 AM, Dmitry Osipenko wrote:
> > BTW, I have an issue with the 8192cu: WiFi stops to work after a while
> > (3-15 minutes) if I enable WMM QoS on the AP. There is nothing suspicious
> > in KMSG, connection is up but no packets go in/out. I tried to enable
> > debug messages in the driver, so when the WiFi stops to work I see that
> > some "temperature/led" notify still going on in the driver, but nothing
> > happens when I try to initiate a transfer (say to open a web page) - the
> > log is silent, like the requests are getting stuck/dropped somewhere
> > before reaching the driver. Is it a known issue? With the QoS disabled
> > everything works hunky-dory, however I get 2x-4x faster download speed
> > with QoS enabled (while it works.)
> >
> > I noticed that rtl92c_init_edca_param() isn't wired in the driver, so I
> > suppose the QoS isn't implemented yet, right?
> >
> > If it is an expected behaviour, I think at least printing a warning
> > message in the KMSG like "QoS unimplemented, you may expect problems"
> > should be good enough to avoid confusion.
>
> As you have already seen, I decided to defer the more invasive patch. When
> backporting to stable, the smaller the change the better.
>
> I have no knowledge of the internals of the RTL8192CU chip. As a result, the
> kinds of changes I can make are limited. I do know that the chip does
> implement QoS. I also noticed that the set_qos() callback routine was very
> different in rtl8192ce than in rtl8192cu. Attached is an untested patch to
> make the CU routine look like the CE version. Please see if it makes a
> difference.
>
> Driver rtl8192cu has never been maintained by Realtek, and it will likely be
> removed from the kernel in the next few cycles. As you are running a new
> kernel, I would recommend rtl8xxxu instead. That driver has high
> reliability, and the speed is improving. Your other option would be a
> driver offered by the vendor of your particular device. Realtek used to
> have these drivers on their web site, but they now seem to have been
> removed. If your vendor does not have a driver,
> http://www.edimax.com/edimax/mw/cufiles/files/download/Driver_Utility/trans
> fer/Wireless/NIC/EW-7811Un/EW-7811Un_Linux_driver_v1.0.0.5.zip should work.
>
> Larry
On my Realtek RTL8188CE card (using the rtl8192ce driver) the patch seems to
fix the Issue (on Kernel 4.9.0).
In contrast to what Dmitry Osipenko experienced, before the patch was applied,
the WIFI usually crashed already a few seconds instead of 3-15 minutes after
connecting to a network.
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* Re: rtlwifi: rtl8192c_common: "BUG: KASAN: slab-out-of-bounds"
From: Dmitry Osipenko @ 2017-02-07 17:14 UTC (permalink / raw)
To: Tobias Guggenmos, Larry Finger
Cc: Johannes Berg, Chaoming Li, linux-wireless,
Linux Kernel Mailing List
In-Reply-To: <46454409.lUoPi3ZtJt@slartibartfas>
On 07.02.2017 19:45, Tobias Guggenmos wrote:
> Am Montag, 6. Februar 2017, 09:45:31 CET schrieb Larry Finger:
>> On 02/06/2017 04:29 AM, Johannes Berg wrote:
>>> On Sat, 2017-02-04 at 12:41 -0600, Larry Finger wrote:
>>>> On 02/04/2017 10:58 AM, Dmitry Osipenko wrote:
>>>>> Seems the problem is caused by rtl92c_dm_*() casting .priv to
>>>>> "struct
>>>>> rtl_pci_priv", while it is "struct rtl_usb_priv".
>>>>
>>>> Those routines are shared by rtl8192ce and rtl8192cu, thus we need to
>>>> make that
>>>> difference in cast to be immaterial. I think we need to move "struct
>>>> bt_coexist_info" to the beginning of both rtlpci_priv and
>>>> rtl_usb_priv. Then it
>>>> should not matter.
>>>
>>> I think you really should consider putting a struct rtl_common into
>>> that or something, and getting rid of all the casting that causes this
>>> problem to start with?
>>
>> The fix you suggest is prepared and will be submitted soon. As it is much
>> more invasive with ~150 insertions and ~160 deletions, I decided not to
>> have it be the one that is pushed to all stable kernels from 4.0 onward.
>>
>> Larry
>
> This is possibly related to the following Fedora Bug:
> https://bugzilla.redhat.com/show_bug.cgi?id=1391987
>
Bug only affects USB adapters (8192cu), PCIe (8192ce) should be fine. The Fedora
bug sounds like the one I have with the enabled AP QoS.
--
Dmitry
^ permalink raw reply
* [PATCH net] brcmfmac: clear skb head state on xmit
From: Paolo Abeni @ 2017-02-07 16:50 UTC (permalink / raw)
To: Kalle Valo
Cc: linux-wireless, brcm80211-dev-list.pdl, Arend van Spriel,
Franky Lin, hante Meuleman
the skbs can be held by the driver for a long time, so we need
to clear any state on xmit to avoid hanging other subsystems.
The skbs are already orphaned later in cmsg code, so we just
need to clear the nf/dst/secpath.
Do it early, while the relevant entries are hopefully still
hot in the cache.
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 9e6f60a..5a8d57b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -22,6 +22,7 @@
#include <net/rtnetlink.h>
#include <net/addrconf.h>
#include <net/ipv6.h>
+#include <net/xfrm.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
@@ -243,6 +244,13 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
if ((skb->priority == 0) || (skb->priority > 7))
skb->priority = cfg80211_classify8021d(skb, NULL);
+ /* we can keep the skb for a long time; avoid starving other
+ * subsystems
+ */
+ nf_reset(skb);
+ skb_dst_drop(skb);
+ secpath_reset(skb);
+
ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb);
if (ret < 0)
brcmf_txfinalize(ifp, skb, false);
--
2.9.3
^ permalink raw reply related
* Re: rtlwifi: rtl8192c_common: "BUG: KASAN: slab-out-of-bounds"
From: Tobias Guggenmos @ 2017-02-07 16:45 UTC (permalink / raw)
To: Larry Finger
Cc: Johannes Berg, Dmitry Osipenko, Chaoming Li, linux-wireless,
Linux Kernel Mailing List
In-Reply-To: <fbe5786e-54eb-b8a7-1540-64a913692361@lwfinger.net>
[-- Attachment #1: Type: text/plain, Size: 1169 bytes --]
Am Montag, 6. Februar 2017, 09:45:31 CET schrieb Larry Finger:
> On 02/06/2017 04:29 AM, Johannes Berg wrote:
> > On Sat, 2017-02-04 at 12:41 -0600, Larry Finger wrote:
> >> On 02/04/2017 10:58 AM, Dmitry Osipenko wrote:
> >>> Seems the problem is caused by rtl92c_dm_*() casting .priv to
> >>> "struct
> >>> rtl_pci_priv", while it is "struct rtl_usb_priv".
> >>
> >> Those routines are shared by rtl8192ce and rtl8192cu, thus we need to
> >> make that
> >> difference in cast to be immaterial. I think we need to move "struct
> >> bt_coexist_info" to the beginning of both rtlpci_priv and
> >> rtl_usb_priv. Then it
> >> should not matter.
> >
> > I think you really should consider putting a struct rtl_common into
> > that or something, and getting rid of all the casting that causes this
> > problem to start with?
>
> The fix you suggest is prepared and will be submitted soon. As it is much
> more invasive with ~150 insertions and ~160 deletions, I decided not to
> have it be the one that is pushed to all stable kernels from 4.0 onward.
>
> Larry
This is possibly related to the following Fedora Bug:
https://bugzilla.redhat.com/show_bug.cgi?id=1391987
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* [PATCH] rtlwifi: Move items out of rtl_pci_priv and rtl_usb_priv
From: Larry Finger @ 2017-02-07 15:14 UTC (permalink / raw)
To: kvalo; +Cc: linux-wireless, Larry Finger
In commit 6773386f977c ("rtlwifi: rtl8192c-common: Fix "BUG: KASAN:"),
a BUG detected when CONFIG_KASAN=y was fixed by reordering the layouts
of struct rtl_pci_priv, and struct rtl_usb_priv so that the variables
used by both PCI and USB drivers have the same offsets in both structs.
The better fix of relocating the critical variables into struct rtl_priv
was deferred as these changes do not have to be applied to stable
kernels.
This change also removes CamelCase variables with pLed0 => pled0.
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
---
.../net/wireless/realtek/rtlwifi/rtl8188ee/hw.c | 14 +-
.../net/wireless/realtek/rtlwifi/rtl8188ee/led.c | 19 +--
.../wireless/realtek/rtlwifi/rtl8192c/dm_common.c | 143 +++++++++---------
.../net/wireless/realtek/rtlwifi/rtl8192ce/hw.c | 166 ++++++++++-----------
.../net/wireless/realtek/rtlwifi/rtl8192ce/led.c | 19 +--
.../net/wireless/realtek/rtlwifi/rtl8192cu/hw.c | 3 +-
.../net/wireless/realtek/rtlwifi/rtl8192cu/led.c | 17 ++-
.../net/wireless/realtek/rtlwifi/rtl8192de/hw.c | 10 +-
.../net/wireless/realtek/rtlwifi/rtl8192de/led.c | 19 +--
.../net/wireless/realtek/rtlwifi/rtl8192ee/hw.c | 6 +-
.../net/wireless/realtek/rtlwifi/rtl8192ee/led.c | 14 +-
.../net/wireless/realtek/rtlwifi/rtl8192se/hw.c | 12 +-
.../net/wireless/realtek/rtlwifi/rtl8192se/led.c | 19 +--
.../net/wireless/realtek/rtlwifi/rtl8723ae/hw.c | 8 +-
.../net/wireless/realtek/rtlwifi/rtl8723ae/led.c | 19 +--
.../net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 8 +-
.../net/wireless/realtek/rtlwifi/rtl8723be/led.c | 15 +-
.../net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 10 +-
.../net/wireless/realtek/rtlwifi/rtl8821ae/led.c | 24 ++-
drivers/net/wireless/realtek/rtlwifi/usb.h | 1 -
drivers/net/wireless/realtek/rtlwifi/wifi.h | 1 +
21 files changed, 261 insertions(+), 286 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c
index 679e214..0ba26d2 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c
@@ -817,19 +817,18 @@ static bool _rtl88ee_llt_table_init(struct ieee80211_hw *hw)
static void _rtl88ee_gen_refresh_led_state(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpriv->rtlhal.up_first_time)
return;
if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
- rtl88ee_sw_led_on(hw, pLed0);
+ rtl88ee_sw_led_on(hw, pled0);
else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
- rtl88ee_sw_led_on(hw, pLed0);
+ rtl88ee_sw_led_on(hw, pled0);
else
- rtl88ee_sw_led_off(hw, pLed0);
+ rtl88ee_sw_led_off(hw, pled0);
}
static bool _rtl88ee_init_mac(struct ieee80211_hw *hw)
@@ -1931,14 +1930,13 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
static void _rtl88ee_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c
index 6ea7fd7..df3e214 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c
@@ -67,7 +67,6 @@ void rtl88ee_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl88ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
@@ -79,7 +78,7 @@ void rtl88ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
case LED_PIN_LED0:
ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain) {
+ if (rtlpriv->ledctl.led_opendrain) {
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(3) | BIT(5) | BIT(6)));
ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
@@ -104,24 +103,26 @@ void rtl88ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl88ee_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl88ee_init_led(hw, &pcipriv->ledctl.sw_led0, LED_PIN_LED0);
- _rtl88ee_init_led(hw, &pcipriv->ledctl.sw_led1, LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl88ee_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl88ee_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl88ee_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl88ee_sw_led_on(hw, pLed0);
+ rtl88ee_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl88ee_sw_led_off(hw, pLed0);
+ rtl88ee_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c
index bdc132b..0b5a06f 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c
@@ -638,7 +638,6 @@ EXPORT_SYMBOL(rtl92c_dm_init_edca_turbo);
static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
static u64 last_txok_cnt;
@@ -651,20 +650,20 @@ static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw)
u32 edca_be_dl = 0x5ea42b;
bool bt_change_edca = false;
- if ((last_bt_edca_ul != rtlpcipriv->bt_coexist.bt_edca_ul) ||
- (last_bt_edca_dl != rtlpcipriv->bt_coexist.bt_edca_dl)) {
+ if ((last_bt_edca_ul != rtlpriv->btcoexist.bt_edca_ul) ||
+ (last_bt_edca_dl != rtlpriv->btcoexist.bt_edca_dl)) {
rtlpriv->dm.current_turbo_edca = false;
- last_bt_edca_ul = rtlpcipriv->bt_coexist.bt_edca_ul;
- last_bt_edca_dl = rtlpcipriv->bt_coexist.bt_edca_dl;
+ last_bt_edca_ul = rtlpriv->btcoexist.bt_edca_ul;
+ last_bt_edca_dl = rtlpriv->btcoexist.bt_edca_dl;
}
- if (rtlpcipriv->bt_coexist.bt_edca_ul != 0) {
- edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_ul;
+ if (rtlpriv->btcoexist.bt_edca_ul != 0) {
+ edca_be_ul = rtlpriv->btcoexist.bt_edca_ul;
bt_change_edca = true;
}
- if (rtlpcipriv->bt_coexist.bt_edca_dl != 0) {
- edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_dl;
+ if (rtlpriv->btcoexist.bt_edca_dl != 0) {
+ edca_be_ul = rtlpriv->btcoexist.bt_edca_dl;
bt_change_edca = true;
}
@@ -673,7 +672,7 @@ static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw)
return;
}
- if ((!mac->ht_enable) && (!rtlpcipriv->bt_coexist.bt_coexistence)) {
+ if ((!mac->ht_enable) && (!rtlpriv->btcoexist.bt_coexistence)) {
if (!(edca_be_ul & 0xffff0000))
edca_be_ul |= 0x005e0000;
@@ -1471,7 +1470,6 @@ EXPORT_SYMBOL(rtl92c_dm_watchdog);
u8 rtl92c_bt_rssi_state_change(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
long undec_sm_pwdb;
u8 curr_bt_rssi_state = 0x00;
@@ -1510,8 +1508,8 @@ u8 rtl92c_bt_rssi_state_change(struct ieee80211_hw *hw)
else
curr_bt_rssi_state &= (~BT_RSSI_STATE_BG_EDCA_LOW);
- if (curr_bt_rssi_state != rtlpcipriv->bt_coexist.bt_rssi_state) {
- rtlpcipriv->bt_coexist.bt_rssi_state = curr_bt_rssi_state;
+ if (curr_bt_rssi_state != rtlpriv->btcoexist.bt_rssi_state) {
+ rtlpriv->btcoexist.bt_rssi_state = curr_bt_rssi_state;
return true;
} else {
return false;
@@ -1522,7 +1520,6 @@ EXPORT_SYMBOL(rtl92c_bt_rssi_state_change);
static bool rtl92c_bt_state_change(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
u32 polling, ratio_tx, ratio_pri;
u32 bt_tx, bt_pri;
@@ -1542,14 +1539,14 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw)
return false;
bt_state &= BIT_OFFSET_LEN_MASK_32(0, 1);
- if (bt_state != rtlpcipriv->bt_coexist.bt_cur_state) {
- rtlpcipriv->bt_coexist.bt_cur_state = bt_state;
+ if (bt_state != rtlpriv->btcoexist.bt_cur_state) {
+ rtlpriv->btcoexist.bt_cur_state = bt_state;
- if (rtlpcipriv->bt_coexist.reg_bt_sco == 3) {
- rtlpcipriv->bt_coexist.bt_service = BT_IDLE;
+ if (rtlpriv->btcoexist.reg_bt_sco == 3) {
+ rtlpriv->btcoexist.bt_service = BT_IDLE;
bt_state = bt_state |
- ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ?
+ ((rtlpriv->btcoexist.bt_ant_isolation == 1) ?
0 : BIT_OFFSET_LEN_MASK_32(1, 1)) |
BIT_OFFSET_LEN_MASK_32(2, 1);
rtl_write_byte(rtlpriv, 0x4fd, bt_state);
@@ -1559,10 +1556,10 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw)
ratio_tx = bt_tx * 1000 / polling;
ratio_pri = bt_pri * 1000 / polling;
- rtlpcipriv->bt_coexist.ratio_tx = ratio_tx;
- rtlpcipriv->bt_coexist.ratio_pri = ratio_pri;
+ rtlpriv->btcoexist.ratio_tx = ratio_tx;
+ rtlpriv->btcoexist.ratio_pri = ratio_pri;
- if (bt_state && rtlpcipriv->bt_coexist.reg_bt_sco == 3) {
+ if (bt_state && rtlpriv->btcoexist.reg_bt_sco == 3) {
if ((ratio_tx < 30) && (ratio_pri < 30))
cur_service_type = BT_IDLE;
@@ -1577,17 +1574,17 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw)
else
cur_service_type = BT_OTHER_ACTION;
- if (cur_service_type != rtlpcipriv->bt_coexist.bt_service) {
- rtlpcipriv->bt_coexist.bt_service = cur_service_type;
+ if (cur_service_type != rtlpriv->btcoexist.bt_service) {
+ rtlpriv->btcoexist.bt_service = cur_service_type;
bt_state = bt_state |
- ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ?
+ ((rtlpriv->btcoexist.bt_ant_isolation == 1) ?
0 : BIT_OFFSET_LEN_MASK_32(1, 1)) |
- ((rtlpcipriv->bt_coexist.bt_service != BT_IDLE) ?
+ ((rtlpriv->btcoexist.bt_service != BT_IDLE) ?
0 : BIT_OFFSET_LEN_MASK_32(2, 1));
/* Add interrupt migration when bt is not ini
* idle state (no traffic). */
- if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) {
+ if (rtlpriv->btcoexist.bt_service != BT_IDLE) {
rtl_write_word(rtlpriv, 0x504, 0x0ccc);
rtl_write_byte(rtlpriv, 0x506, 0x54);
rtl_write_byte(rtlpriv, 0x507, 0x54);
@@ -1626,80 +1623,77 @@ static bool rtl92c_bt_wifi_connect_change(struct ieee80211_hw *hw)
static void rtl92c_bt_set_normal(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
-
-
- if (rtlpcipriv->bt_coexist.bt_service == BT_OTHERBUSY) {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea72b;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea72b;
- } else if (rtlpcipriv->bt_coexist.bt_service == BT_BUSY) {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0x5eb82f;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0x5eb82f;
- } else if (rtlpcipriv->bt_coexist.bt_service == BT_SCO) {
- if (rtlpcipriv->bt_coexist.ratio_tx > 160) {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea72f;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea72f;
+
+ if (rtlpriv->btcoexist.bt_service == BT_OTHERBUSY) {
+ rtlpriv->btcoexist.bt_edca_ul = 0x5ea72b;
+ rtlpriv->btcoexist.bt_edca_dl = 0x5ea72b;
+ } else if (rtlpriv->btcoexist.bt_service == BT_BUSY) {
+ rtlpriv->btcoexist.bt_edca_ul = 0x5eb82f;
+ rtlpriv->btcoexist.bt_edca_dl = 0x5eb82f;
+ } else if (rtlpriv->btcoexist.bt_service == BT_SCO) {
+ if (rtlpriv->btcoexist.ratio_tx > 160) {
+ rtlpriv->btcoexist.bt_edca_ul = 0x5ea72f;
+ rtlpriv->btcoexist.bt_edca_dl = 0x5ea72f;
} else {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea32b;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea42b;
+ rtlpriv->btcoexist.bt_edca_ul = 0x5ea32b;
+ rtlpriv->btcoexist.bt_edca_dl = 0x5ea42b;
}
} else {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0;
+ rtlpriv->btcoexist.bt_edca_ul = 0;
+ rtlpriv->btcoexist.bt_edca_dl = 0;
}
- if ((rtlpcipriv->bt_coexist.bt_service != BT_IDLE) &&
- (rtlpriv->mac80211.mode == WIRELESS_MODE_G ||
+ if ((rtlpriv->btcoexist.bt_service != BT_IDLE) &&
+ (rtlpriv->mac80211.mode == WIRELESS_MODE_G ||
(rtlpriv->mac80211.mode == (WIRELESS_MODE_G | WIRELESS_MODE_B))) &&
- (rtlpcipriv->bt_coexist.bt_rssi_state &
+ (rtlpriv->btcoexist.bt_rssi_state &
BT_RSSI_STATE_BG_EDCA_LOW)) {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0x5eb82b;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0x5eb82b;
+ rtlpriv->btcoexist.bt_edca_ul = 0x5eb82b;
+ rtlpriv->btcoexist.bt_edca_dl = 0x5eb82b;
}
}
static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw, u8 tmp1byte)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
/* Only enable HW BT coexist when BT in "Busy" state. */
if (rtlpriv->mac80211.vendor == PEER_CISCO &&
- rtlpcipriv->bt_coexist.bt_service == BT_OTHER_ACTION) {
+ rtlpriv->btcoexist.bt_service == BT_OTHER_ACTION) {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
} else {
- if ((rtlpcipriv->bt_coexist.bt_service == BT_BUSY) &&
- (rtlpcipriv->bt_coexist.bt_rssi_state &
+ if ((rtlpriv->btcoexist.bt_service == BT_BUSY) &&
+ (rtlpriv->btcoexist.bt_rssi_state &
BT_RSSI_STATE_NORMAL_POWER)) {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
- } else if ((rtlpcipriv->bt_coexist.bt_service ==
+ } else if ((rtlpriv->btcoexist.bt_service ==
BT_OTHER_ACTION) && (rtlpriv->mac80211.mode <
WIRELESS_MODE_N_24G) &&
- (rtlpcipriv->bt_coexist.bt_rssi_state &
+ (rtlpriv->btcoexist.bt_rssi_state &
BT_RSSI_STATE_SPECIAL_LOW)) {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
- } else if (rtlpcipriv->bt_coexist.bt_service == BT_PAN) {
+ } else if (rtlpriv->btcoexist.bt_service == BT_PAN) {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
} else {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
}
}
- if (rtlpcipriv->bt_coexist.bt_service == BT_PAN)
+ if (rtlpriv->btcoexist.bt_service == BT_PAN)
rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x10100);
else
rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x0);
- if (rtlpcipriv->bt_coexist.bt_rssi_state &
+ if (rtlpriv->btcoexist.bt_rssi_state &
BT_RSSI_STATE_NORMAL_POWER) {
rtl92c_bt_set_normal(hw);
} else {
- rtlpcipriv->bt_coexist.bt_edca_ul = 0;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0;
+ rtlpriv->btcoexist.bt_edca_ul = 0;
+ rtlpriv->btcoexist.bt_edca_dl = 0;
}
- if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) {
+ if (rtlpriv->btcoexist.bt_service != BT_IDLE) {
rtlpriv->cfg->ops->set_rfreg(hw,
RF90_PATH_A,
0x1e,
@@ -1707,12 +1701,12 @@ static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw, u8 tmp1byte)
} else {
rtlpriv->cfg->ops->set_rfreg(hw,
RF90_PATH_A, 0x1e, 0xf0,
- rtlpcipriv->bt_coexist.bt_rfreg_origin_1e);
+ rtlpriv->btcoexist.bt_rfreg_origin_1e);
}
if (!rtlpriv->dm.dynamic_txpower_enable) {
- if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) {
- if (rtlpcipriv->bt_coexist.bt_rssi_state &
+ if (rtlpriv->btcoexist.bt_service != BT_IDLE) {
+ if (rtlpriv->btcoexist.bt_rssi_state &
BT_RSSI_STATE_TXPOWER_LOW) {
rtlpriv->dm.dynamic_txhighpower_lvl =
TXHIGHPWRLEVEL_BT2;
@@ -1732,37 +1726,34 @@ static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw, u8 tmp1byte)
static void rtl92c_check_bt_change(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
u8 tmp1byte = 0;
if (IS_81XXC_VENDOR_UMC_B_CUT(rtlhal->version) &&
- rtlpcipriv->bt_coexist.bt_coexistence)
+ rtlpriv->btcoexist.bt_coexistence)
tmp1byte |= BIT(5);
- if (rtlpcipriv->bt_coexist.bt_cur_state) {
- if (rtlpcipriv->bt_coexist.bt_ant_isolation)
+ if (rtlpriv->btcoexist.bt_cur_state) {
+ if (rtlpriv->btcoexist.bt_ant_isolation)
rtl92c_bt_ant_isolation(hw, tmp1byte);
} else {
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
rtlpriv->cfg->ops->set_rfreg(hw, RF90_PATH_A, 0x1e, 0xf0,
- rtlpcipriv->bt_coexist.bt_rfreg_origin_1e);
+ rtlpriv->btcoexist.bt_rfreg_origin_1e);
- rtlpcipriv->bt_coexist.bt_edca_ul = 0;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0;
+ rtlpriv->btcoexist.bt_edca_ul = 0;
+ rtlpriv->btcoexist.bt_edca_dl = 0;
}
}
void rtl92c_dm_bt_coexist(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
-
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
bool wifi_connect_change;
bool bt_state_change;
bool rssi_state_change;
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) {
-
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4)) {
wifi_connect_change = rtl92c_bt_wifi_connect_change(hw);
bt_state_change = rtl92c_bt_state_change(hw);
rssi_state_change = rtl92c_bt_rssi_state_change(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c
index 611987d..9956026 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c
@@ -148,7 +148,6 @@ void rtl92ce_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
@@ -276,8 +275,8 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
u8 *p_regtoset = NULL;
u8 index = 0;
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type ==
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type ==
BT_CSR_BC4))
p_regtoset = regtoset_bt;
else
@@ -655,26 +654,25 @@ static bool _rtl92ce_llt_table_init(struct ieee80211_hw *hw)
static void _rtl92ce_gen_refresh_led_state(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpci->up_first_time)
return;
if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
- rtl92ce_sw_led_on(hw, pLed0);
+ rtl92ce_sw_led_on(hw, pled0);
else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
- rtl92ce_sw_led_on(hw, pLed0);
+ rtl92ce_sw_led_on(hw, pled0);
else
- rtl92ce_sw_led_off(hw, pLed0);
+ rtl92ce_sw_led_off(hw, pled0);
}
static bool _rtl92ce_init_mac(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
@@ -683,7 +681,7 @@ static bool _rtl92ce_init_mac(struct ieee80211_hw *hw)
u16 retry;
rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00);
- if (rtlpcipriv->bt_coexist.bt_coexistence) {
+ if (rtlpriv->btcoexist.bt_coexistence) {
u32 value32;
value32 = rtl_read_dword(rtlpriv, REG_APS_FSMCO);
value32 |= (SOP_ABG | SOP_AMB | XOP_BTCK);
@@ -692,7 +690,7 @@ static bool _rtl92ce_init_mac(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b);
rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0F);
- if (rtlpcipriv->bt_coexist.bt_coexistence) {
+ if (rtlpriv->btcoexist.bt_coexistence) {
u32 u4b_tmp = rtl_read_dword(rtlpriv, REG_AFE_XTAL_CTRL);
u4b_tmp &= (~0x00024800);
@@ -726,7 +724,7 @@ static bool _rtl92ce_init_mac(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x82);
udelay(2);
- if (rtlpcipriv->bt_coexist.bt_coexistence) {
+ if (rtlpriv->btcoexist.bt_coexistence) {
bytetmp = rtl_read_byte(rtlpriv, REG_AFE_XTAL_CTRL+2) & 0xfd;
rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL+2, bytetmp);
}
@@ -798,7 +796,6 @@ static void _rtl92ce_hw_configure(struct ieee80211_hw *hw)
{
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
u8 reg_bw_opmode;
u32 reg_prsr;
@@ -828,8 +825,8 @@ static void _rtl92ce_hw_configure(struct ieee80211_hw *hw)
rtl_write_dword(rtlpriv, REG_RARFRC, 0x01000000);
rtl_write_dword(rtlpriv, REG_RARFRC + 4, 0x07060504);
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4))
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4))
rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x97427431);
else
rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb972a841);
@@ -848,8 +845,8 @@ static void _rtl92ce_hw_configure(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_PIFS, 0x1C);
rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16);
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) {
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4)) {
rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020);
rtl_write_word(rtlpriv, REG_PROT_MODE_CTRL, 0x0402);
} else {
@@ -857,8 +854,8 @@ static void _rtl92ce_hw_configure(struct ieee80211_hw *hw)
rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020);
}
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4))
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4))
rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x03086666);
else
rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x086666);
@@ -1313,7 +1310,6 @@ void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
u8 u1b_tmp;
u32 u4b_tmp;
@@ -1331,9 +1327,9 @@ static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00000000);
u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL);
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- ((rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) ||
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC8))) {
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ ((rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4) ||
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC8))) {
rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00F30000 |
(u1b_tmp << 8));
} else {
@@ -1345,7 +1341,7 @@ static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x80);
if (!IS_81XXC_VENDOR_UMC_B_CUT(rtlhal->version))
rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x23);
- if (rtlpcipriv->bt_coexist.bt_coexistence) {
+ if (rtlpriv->btcoexist.bt_coexistence) {
u4b_tmp = rtl_read_dword(rtlpriv, REG_AFE_XTAL_CTRL);
u4b_tmp |= 0x03824800;
rtl_write_dword(rtlpriv, REG_AFE_XTAL_CTRL, u4b_tmp);
@@ -1724,12 +1720,11 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
static void _rtl92ce_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
@@ -1782,7 +1777,6 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw,
struct ieee80211_sta *sta)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
struct rtl_phy *rtlphy = &(rtlpriv->phy);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
@@ -1838,12 +1832,12 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw,
break;
}
- if ((rtlpcipriv->bt_coexist.bt_coexistence) &&
- (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) &&
- (rtlpcipriv->bt_coexist.bt_cur_state) &&
- (rtlpcipriv->bt_coexist.bt_ant_isolation) &&
- ((rtlpcipriv->bt_coexist.bt_service == BT_SCO) ||
- (rtlpcipriv->bt_coexist.bt_service == BT_BUSY)))
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4) &&
+ (rtlpriv->btcoexist.bt_cur_state) &&
+ (rtlpriv->btcoexist.bt_ant_isolation) &&
+ ((rtlpriv->btcoexist.bt_service == BT_SCO) ||
+ (rtlpriv->btcoexist.bt_service == BT_BUSY)))
ratr_value &= 0x0fffcfc0;
else
ratr_value &= 0x0FFFFFFF;
@@ -2237,65 +2231,64 @@ void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index,
static void rtl8192ce_bt_var_init(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
-
- rtlpcipriv->bt_coexist.bt_coexistence =
- rtlpcipriv->bt_coexist.eeprom_bt_coexist;
- rtlpcipriv->bt_coexist.bt_ant_num =
- rtlpcipriv->bt_coexist.eeprom_bt_ant_num;
- rtlpcipriv->bt_coexist.bt_coexist_type =
- rtlpcipriv->bt_coexist.eeprom_bt_type;
-
- if (rtlpcipriv->bt_coexist.reg_bt_iso == 2)
- rtlpcipriv->bt_coexist.bt_ant_isolation =
- rtlpcipriv->bt_coexist.eeprom_bt_ant_isol;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->btcoexist.bt_coexistence =
+ rtlpriv->btcoexist.eeprom_bt_coexist;
+ rtlpriv->btcoexist.bt_ant_num =
+ rtlpriv->btcoexist.eeprom_bt_ant_num;
+ rtlpriv->btcoexist.bt_coexist_type =
+ rtlpriv->btcoexist.eeprom_bt_type;
+
+ if (rtlpriv->btcoexist.reg_bt_iso == 2)
+ rtlpriv->btcoexist.bt_ant_isolation =
+ rtlpriv->btcoexist.eeprom_bt_ant_isol;
else
- rtlpcipriv->bt_coexist.bt_ant_isolation =
- rtlpcipriv->bt_coexist.reg_bt_iso;
-
- rtlpcipriv->bt_coexist.bt_radio_shared_type =
- rtlpcipriv->bt_coexist.eeprom_bt_radio_shared;
-
- if (rtlpcipriv->bt_coexist.bt_coexistence) {
-
- if (rtlpcipriv->bt_coexist.reg_bt_sco == 1)
- rtlpcipriv->bt_coexist.bt_service = BT_OTHER_ACTION;
- else if (rtlpcipriv->bt_coexist.reg_bt_sco == 2)
- rtlpcipriv->bt_coexist.bt_service = BT_SCO;
- else if (rtlpcipriv->bt_coexist.reg_bt_sco == 4)
- rtlpcipriv->bt_coexist.bt_service = BT_BUSY;
- else if (rtlpcipriv->bt_coexist.reg_bt_sco == 5)
- rtlpcipriv->bt_coexist.bt_service = BT_OTHERBUSY;
+ rtlpriv->btcoexist.bt_ant_isolation =
+ rtlpriv->btcoexist.reg_bt_iso;
+
+ rtlpriv->btcoexist.bt_radio_shared_type =
+ rtlpriv->btcoexist.eeprom_bt_radio_shared;
+
+ if (rtlpriv->btcoexist.bt_coexistence) {
+ if (rtlpriv->btcoexist.reg_bt_sco == 1)
+ rtlpriv->btcoexist.bt_service = BT_OTHER_ACTION;
+ else if (rtlpriv->btcoexist.reg_bt_sco == 2)
+ rtlpriv->btcoexist.bt_service = BT_SCO;
+ else if (rtlpriv->btcoexist.reg_bt_sco == 4)
+ rtlpriv->btcoexist.bt_service = BT_BUSY;
+ else if (rtlpriv->btcoexist.reg_bt_sco == 5)
+ rtlpriv->btcoexist.bt_service = BT_OTHERBUSY;
else
- rtlpcipriv->bt_coexist.bt_service = BT_IDLE;
+ rtlpriv->btcoexist.bt_service = BT_IDLE;
- rtlpcipriv->bt_coexist.bt_edca_ul = 0;
- rtlpcipriv->bt_coexist.bt_edca_dl = 0;
- rtlpcipriv->bt_coexist.bt_rssi_state = 0xff;
+ rtlpriv->btcoexist.bt_edca_ul = 0;
+ rtlpriv->btcoexist.bt_edca_dl = 0;
+ rtlpriv->btcoexist.bt_rssi_state = 0xff;
}
}
void rtl8192ce_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
bool auto_load_fail, u8 *hwinfo)
{
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
u8 val;
if (!auto_load_fail) {
- rtlpcipriv->bt_coexist.eeprom_bt_coexist =
+ rtlpriv->btcoexist.eeprom_bt_coexist =
((hwinfo[RF_OPTION1] & 0xe0) >> 5);
val = hwinfo[RF_OPTION4];
- rtlpcipriv->bt_coexist.eeprom_bt_type = ((val & 0xe) >> 1);
- rtlpcipriv->bt_coexist.eeprom_bt_ant_num = (val & 0x1);
- rtlpcipriv->bt_coexist.eeprom_bt_ant_isol = ((val & 0x10) >> 4);
- rtlpcipriv->bt_coexist.eeprom_bt_radio_shared =
+ rtlpriv->btcoexist.eeprom_bt_type = ((val & 0xe) >> 1);
+ rtlpriv->btcoexist.eeprom_bt_ant_num = (val & 0x1);
+ rtlpriv->btcoexist.eeprom_bt_ant_isol = ((val & 0x10) >> 4);
+ rtlpriv->btcoexist.eeprom_bt_radio_shared =
((val & 0x20) >> 5);
} else {
- rtlpcipriv->bt_coexist.eeprom_bt_coexist = 0;
- rtlpcipriv->bt_coexist.eeprom_bt_type = BT_2WIRE;
- rtlpcipriv->bt_coexist.eeprom_bt_ant_num = ANT_X2;
- rtlpcipriv->bt_coexist.eeprom_bt_ant_isol = 0;
- rtlpcipriv->bt_coexist.eeprom_bt_radio_shared = BT_RADIO_SHARED;
+ rtlpriv->btcoexist.eeprom_bt_coexist = 0;
+ rtlpriv->btcoexist.eeprom_bt_type = BT_2WIRE;
+ rtlpriv->btcoexist.eeprom_bt_ant_num = ANT_X2;
+ rtlpriv->btcoexist.eeprom_bt_ant_isol = 0;
+ rtlpriv->btcoexist.eeprom_bt_radio_shared = BT_RADIO_SHARED;
}
rtl8192ce_bt_var_init(hw);
@@ -2303,14 +2296,14 @@ void rtl8192ce_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
/* 0:Low, 1:High, 2:From Efuse. */
- rtlpcipriv->bt_coexist.reg_bt_iso = 2;
+ rtlpriv->btcoexist.reg_bt_iso = 2;
/* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */
- rtlpcipriv->bt_coexist.reg_bt_sco = 3;
+ rtlpriv->btcoexist.reg_bt_sco = 3;
/* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */
- rtlpcipriv->bt_coexist.reg_bt_sco = 0;
+ rtlpriv->btcoexist.reg_bt_sco = 0;
}
@@ -2318,23 +2311,22 @@ void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_phy *rtlphy = &(rtlpriv->phy);
- struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
u8 u1_tmp;
- if (rtlpcipriv->bt_coexist.bt_coexistence &&
- ((rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) ||
- rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC8)) {
+ if (rtlpriv->btcoexist.bt_coexistence &&
+ ((rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4) ||
+ rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC8)) {
- if (rtlpcipriv->bt_coexist.bt_ant_isolation)
+ if (rtlpriv->btcoexist.bt_ant_isolation)
rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) &
BIT_OFFSET_LEN_MASK_32(0, 1);
u1_tmp = u1_tmp |
- ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ?
+ ((rtlpriv->btcoexist.bt_ant_isolation == 1) ?
0 : BIT_OFFSET_LEN_MASK_32(1, 1)) |
- ((rtlpcipriv->bt_coexist.bt_service == BT_SCO) ?
+ ((rtlpriv->btcoexist.bt_service == BT_SCO) ?
0 : BIT_OFFSET_LEN_MASK_32(2, 1));
rtl_write_byte(rtlpriv, 0x4fd, u1_tmp);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c
index bdaa848..7edf5af 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c
@@ -67,7 +67,6 @@ void rtl92ce_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n",
@@ -80,7 +79,7 @@ void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain)
+ if (rtlpriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(1) | BIT(5) | BIT(6)));
else
@@ -100,24 +99,26 @@ void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92ce_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
- _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl92ce_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl92ce_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl92ce_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl92ce_sw_led_on(hw, pLed0);
+ rtl92ce_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl92ce_sw_led_off(hw, pLed0);
+ rtl92ce_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
index 9db6ec6..f95a645 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
@@ -393,12 +393,11 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw)
static void _rtl92cu_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- usb_priv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c
index 70ea6c5..66d2784 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c
@@ -67,7 +67,6 @@ void rtl92cu_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n",
@@ -78,7 +77,7 @@ void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (usbpriv->ledctl.led_opendrain)
+ if (rtlpriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(1) | BIT(5) | BIT(6)));
else
@@ -99,16 +98,18 @@ void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92cu_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw);
- _rtl92cu_init_led(hw, &(usbpriv->ledctl.sw_led0), LED_PIN_LED0);
- _rtl92cu_init_led(hw, &(usbpriv->ledctl.sw_led1), LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl92cu_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl92cu_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
void rtl92cu_deinit_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw);
- _rtl92cu_deInit_led(&(usbpriv->ledctl.sw_led0));
- _rtl92cu_deInit_led(&(usbpriv->ledctl.sw_led1));
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl92cu_deInit_led(&rtlpriv->ledctl.sw_led0);
+ _rtl92cu_deInit_led(&rtlpriv->ledctl.sw_led1);
}
static void _rtl92cu_sw_led_control(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
index 1bd1893..cf28d25 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c
@@ -614,19 +614,19 @@ static bool _rtl92de_llt_table_init(struct ieee80211_hw *hw)
static void _rtl92de_gen_refresh_led_state(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpci->up_first_time)
return;
if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
- rtl92de_sw_led_on(hw, pLed0);
+ rtl92de_sw_led_on(hw, pled0);
else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
- rtl92de_sw_led_on(hw, pLed0);
+ rtl92de_sw_led_on(hw, pled0);
else
- rtl92de_sw_led_off(hw, pLed0);
+ rtl92de_sw_led_off(hw, pled0);
}
static bool _rtl92de_init_mac(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c
index 4be787e..8851038 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c
@@ -76,7 +76,6 @@ void rtl92de_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n",
@@ -89,7 +88,7 @@ void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain)
+ if (rtlpriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, REG_LEDCFG2,
(ledcfg | BIT(1) | BIT(5) | BIT(6)));
else
@@ -110,24 +109,26 @@ void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92de_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
- _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl92ce_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl92ce_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl92ce_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl92de_sw_led_on(hw, pLed0);
+ rtl92de_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl92de_sw_led_off(hw, pLed0);
+ rtl92de_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
index b44244a..56ca7f5 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
@@ -735,9 +735,8 @@ static bool _rtl92ee_llt_table_init(struct ieee80211_hw *hw)
static void _rtl92ee_gen_refresh_led_state(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pled0 = &pcipriv->ledctl.sw_led0;
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpriv->rtlhal.up_first_time)
return;
@@ -2166,10 +2165,9 @@ static void _rtl92ee_read_adapter_info(struct ieee80211_hw *hw)
static void _rtl92ee_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"RT Customized ID: 0x%02X\n", rtlhal->oem_id);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c
index 47da05d..96c6478 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c
@@ -99,26 +99,26 @@ void rtl92ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92ee_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
- _rtl92ee_init_led(hw, &pcipriv->ledctl.sw_led0, LED_PIN_LED0);
- _rtl92ee_init_led(hw, &pcipriv->ledctl.sw_led1, LED_PIN_LED1);
+ _rtl92ee_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl92ee_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl92ee_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &pcipriv->ledctl.sw_led0;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl92ee_sw_led_on(hw, pLed0);
+ rtl92ee_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl92ee_sw_led_off(hw, pLed0);
+ rtl92ee_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c
index d5e86b6..ba1bd78 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c
@@ -753,13 +753,12 @@ static void _rtl92se_macconfig_before_fwdownload(struct ieee80211_hw *hw)
/* After MACIO reset,we must refresh LED state. */
if ((ppsc->rfoff_reason == RF_CHANGE_BY_IPS) ||
(ppsc->rfoff_reason == 0)) {
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
enum rf_pwrstate rfpwr_state_toset;
rfpwr_state_toset = _rtl92se_rf_onoff_detect(hw);
if (rfpwr_state_toset == ERFON)
- rtl92se_sw_led_on(hw, pLed0);
+ rtl92se_sw_led_on(hw, pled0);
}
}
@@ -1395,16 +1394,15 @@ static void _rtl92se_gen_refreshledstate(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpci->up_first_time == 1)
return;
if (rtlpriv->psc.rfoff_reason == RF_CHANGE_BY_IPS)
- rtl92se_sw_led_on(hw, pLed0);
+ rtl92se_sw_led_on(hw, pled0);
else
- rtl92se_sw_led_off(hw, pLed0);
+ rtl92se_sw_led_off(hw, pled0);
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c
index c740aeb..33c307a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c
@@ -38,9 +38,10 @@ static void _rtl92se_init_led(struct ieee80211_hw *hw,
void rtl92se_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl92se_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
- _rtl92se_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl92se_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl92se_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
void rtl92se_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
@@ -73,7 +74,6 @@ void rtl92se_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv;
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
rtlpriv = rtl_priv(hw);
@@ -89,7 +89,7 @@ void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain)
+ if (rtlpriv->ledctl.led_opendrain)
rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(1)));
else
rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(3)));
@@ -109,16 +109,17 @@ void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
static void _rtl92se_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl92se_sw_led_on(hw, pLed0);
+ rtl92se_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl92se_sw_led_off(hw, pLed0);
+ rtl92se_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c
index bb9de2f..859c045 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c
@@ -664,9 +664,8 @@ static bool _rtl8723e_llt_table_init(struct ieee80211_hw *hw)
static void _rtl8723e_gen_refresh_led_state(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pled0 = &pcipriv->ledctl.sw_led0;
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpriv->rtlhal.up_first_time)
return;
@@ -1790,13 +1789,12 @@ static void _rtl8723e_read_adapter_info(struct ieee80211_hw *hw,
static void _rtl8723e_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c
index e1e6d24..d567b0d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c
@@ -68,7 +68,6 @@ void rtl8723e_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8723e_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
@@ -81,7 +80,7 @@ void rtl8723e_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain) {
+ if (rtlpriv->ledctl.led_opendrain) {
ledcfg &= 0x90; /* Set to software control. */
rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3)));
ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
@@ -109,24 +108,26 @@ void rtl8723e_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8723e_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl8723e_init_led(hw, &pcipriv->ledctl.sw_led0, LED_PIN_LED0);
- _rtl8723e_init_led(hw, &pcipriv->ledctl.sw_led1, LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl8723e_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl8723e_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl8723e_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
- rtl8723e_sw_led_on(hw, pLed0);
+ rtl8723e_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
- rtl8723e_sw_led_off(hw, pLed0);
+ rtl8723e_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
index e6e5e5b..1acbfb8 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
@@ -809,9 +809,8 @@ static bool _rtl8723be_llt_table_init(struct ieee80211_hw *hw)
static void _rtl8723be_gen_refresh_led_state(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
if (rtlpriv->rtlhal.up_first_time)
return;
@@ -2228,13 +2227,12 @@ static void _rtl8723be_read_adapter_info(struct ieee80211_hw *hw,
static void _rtl8723be_hal_customized_behavior(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c
index 8232e01..4f7890d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c
@@ -67,7 +67,6 @@ void rtl8723be_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
@@ -80,7 +79,7 @@ void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain) {
+ if (rtlpriv->ledctl.led_opendrain) {
ledcfg &= 0x90; /* Set to software control. */
rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3)));
ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
@@ -108,16 +107,18 @@ void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8723be_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
- _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ _rtl8723be_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl8723be_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl8723be_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
+
switch (ledaction) {
case LED_CTL_POWER_ON:
case LED_CTL_LINK:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index 4f83eee..363d2f2 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -890,9 +890,8 @@ static bool _rtl8821ae_llt_table_init(struct ieee80211_hw *hw)
static void _rtl8821ae_gen_refresh_led_state(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
- struct rtl_led *pled0 = &pcipriv->ledctl.sw_led0;
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
if (rtlpriv->rtlhal.up_first_time)
@@ -3098,7 +3097,6 @@ static void _rtl8821ae_read_adapter_info(struct ieee80211_hw *hw, bool b_pseudo_
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
int params[] = {RTL_EEPROM_ID, EEPROM_VID, EEPROM_DID,
EEPROM_SVID, EEPROM_SMID, EEPROM_MAC_ADDR,
EEPROM_CHANNELPLAN, EEPROM_VERSION, EEPROM_CUSTOMER_ID,
@@ -3193,7 +3191,7 @@ static void _rtl8821ae_read_adapter_info(struct ieee80211_hw *hw, bool b_pseudo_
"SWAS: bHwAntDiv = %x, TRxAntDivType = %x\n",
rtlefuse->antenna_div_cfg, rtlefuse->antenna_div_type);
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
if (rtlhal->oem_id == RT_CID_DEFAULT) {
switch (rtlefuse->eeprom_oemid) {
@@ -3224,10 +3222,10 @@ static void _rtl8821ae_read_adapter_info(struct ieee80211_hw *hw, bool b_pseudo_
struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
switch (rtlhal->oem_id) {
case RT_CID_819X_HP:
- pcipriv->ledctl.led_opendrain = true;
+ rtlpriv->ledctl.led_opendrain = true;
break;
case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c
index fcb3b28..405c754 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c
@@ -101,7 +101,6 @@ void rtl8812ae_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8821ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 ledcfg;
RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
@@ -114,7 +113,7 @@ void rtl8821ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
break;
case LED_PIN_LED0:
ledcfg &= 0xf0;
- if (pcipriv->ledctl.led_opendrain) {
+ if (rtlpriv->ledctl.led_opendrain) {
ledcfg &= 0x90; /* Set to software control. */
rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3)));
ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
@@ -143,7 +142,6 @@ void rtl8812ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
{
u16 ledreg = REG_LEDCFG1;
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
switch (pled->ledpin) {
case LED_PIN_LED0:
@@ -163,7 +161,7 @@ void rtl8812ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
"In SwLedOff,LedAddr:%X LEDPIN=%d\n",
ledreg, pled->ledpin);
/*Open-drain arrangement for controlling the LED*/
- if (pcipriv->ledctl.led_opendrain) {
+ if (rtlpriv->ledctl.led_opendrain) {
u8 ledcfg = rtl_read_byte(rtlpriv, ledreg);
ledreg &= 0xd0; /* Set to software control.*/
@@ -182,17 +180,17 @@ void rtl8812ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
void rtl8821ae_init_sw_leds(struct ieee80211_hw *hw)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
- _rtl8821ae_init_led(hw, &pcipriv->ledctl.sw_led0, LED_PIN_LED0);
- _rtl8821ae_init_led(hw, &pcipriv->ledctl.sw_led1, LED_PIN_LED1);
+ _rtl8821ae_init_led(hw, &rtlpriv->ledctl.sw_led0, LED_PIN_LED0);
+ _rtl8821ae_init_led(hw, &rtlpriv->ledctl.sw_led1, LED_PIN_LED1);
}
static void _rtl8821ae_sw_led_control(struct ieee80211_hw *hw,
enum led_ctl_mode ledaction)
{
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
- struct rtl_led *pLed0 = &pcipriv->ledctl.sw_led0;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_led *pled0 = &rtlpriv->ledctl.sw_led0;
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
switch (ledaction) {
@@ -200,15 +198,15 @@ static void _rtl8821ae_sw_led_control(struct ieee80211_hw *hw,
case LED_CTL_LINK:
case LED_CTL_NO_LINK:
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
- rtl8812ae_sw_led_on(hw, pLed0);
+ rtl8812ae_sw_led_on(hw, pled0);
else
- rtl8821ae_sw_led_on(hw, pLed0);
+ rtl8821ae_sw_led_on(hw, pled0);
break;
case LED_CTL_POWER_OFF:
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
- rtl8812ae_sw_led_off(hw, pLed0);
+ rtl8812ae_sw_led_off(hw, pled0);
else
- rtl8821ae_sw_led_off(hw, pLed0);
+ rtl8821ae_sw_led_off(hw, pled0);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h
index cdb9e06..c91cec0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.h
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.h
@@ -147,7 +147,6 @@ struct rtl_usb {
struct rtl_usb_priv {
struct bt_coexist_info bt_coexist;
- struct rtl_led_ctl ledctl;
struct rtl_usb dev;
};
diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h
index e464722..c0d2601 100644
--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h
+++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h
@@ -2587,6 +2587,7 @@ struct rtl_priv {
struct rtl_dm dm;
struct rtl_security sec;
struct rtl_efuse efuse;
+ struct rtl_led_ctl ledctl;
struct rtl_ps_ctl psc;
struct rate_adaptive ra;
--
2.10.2
^ permalink raw reply related
* Re: [PATCH 1/3] ath10k: remove ath10k_vif_to_arvif()
From: Ben Greear @ 2017-02-07 14:46 UTC (permalink / raw)
To: Valo, Kalle, Adrian Chadd
Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org,
Amadeusz Sławiński, ath10k@lists.infradead.org,
Linux Kernel Mailing List
In-Reply-To: <87y3xi4b28.fsf@qca.qualcomm.com>
On 02/07/2017 01:14 AM, Valo, Kalle wrote:
> Adrian Chadd <adrian@freebsd.org> writes:
>
>> Removing this method makes the diff to FreeBSD larger, as "vif" in
>> FreeBSD is a different pointer.
>>
>> (Yes, I have ath10k on freebsd working and I'd like to find a way to
>> reduce the diff moving forward.)
>
> I don't like this "(void *) vif->drv_priv" style that much either but
> apparently it's commonly used in Linux wireless code and already parts
> of ath10k. So this patch just unifies the coding style.
Surely the code compiles to the same thing, so why add a patch that
makes it more difficult for Adrian and makes the code no easier to read
for the rest of us?
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* [PATCH] wil6210: include moduleparam.h
From: Johannes Berg @ 2017-02-07 14:33 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This now declares a module parameter, so include the necessary
header file for that.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 6aa3ff4240a9..035ff5974058 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -15,6 +15,7 @@
*/
#include <linux/etherdevice.h>
+#include <linux/moduleparam.h>
#include "wil6210.h"
#include "wmi.h"
--
2.9.3
^ permalink raw reply related
* [patch] mac80211: check for allocation failure in debugfs code
From: Dan Carpenter @ 2017-02-07 13:20 UTC (permalink / raw)
To: Johannes Berg, Ben Greear; +Cc: linux-wireless, kernel-janitors
kmalloc() can fail. Also let's move the allocation out of the
declaration block so it's easier to read.
Fixes: 4a5eccaa9350 ("mac80211: Show pending txqlen in debugfs.")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index f62cd0e13c58..4689353aa263 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -249,12 +249,19 @@ static ssize_t misc_read(struct file *file, char __user *user_buf,
struct ieee80211_local *local = file->private_data;
/* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
- char *buf = kzalloc(bufsz, GFP_KERNEL);
- char *pos = buf, *end = buf + bufsz - 1;
+ char *buf;
+ char *pos, *end;
ssize_t rv;
int i;
int ln;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos = buf;
+ end = buf + bufsz - 1;
+
pos += scnprintf(pos, end - pos, "pending:\n");
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
^ permalink raw reply related
* ANNOUNCE: Netdev 2.1 Location and Hotel
From: Jamal Hadi Salim @ 2017-02-07 12:58 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: netfilter-devel, netfilter, info, linux-wireless, tech-committee,
David Miller, Stephen Jaworski, Rob Echlin, lwn
Two important announcements
1) We are pleased to announce a location for netdev 2.1:
Le Westin Montréal
270 Rue St. Antoine Ouest, Montreal, QC H2Y 0A3, Canada
Le Westin will also be the conference hotel. We have a speacial
arrangement for all attendees to get speacial rates at the hotel.
We would like to encourage attendees to book at this hotel.
If you can find a better deal for this hotel anywhere else
on the net for the conference days, please notify us immediately
at info@netdevconf.org and we'll contact the hotel.
The hotel registration is at:
https://www.starwoodmeeting.com/Book/netdev2017
The room rates are:
Traditional King: $189
Traditional 2 Queuen: $209
Premium Junior Suite: $269
Extra 3rd and 4th person sharing a room is
$30 CAD per person, per night.
All above rates are in Canadian $ excluding taxes.
Rates are available 3 days before and 3 days after events.
Rates will no longer be available after March 22, 2017.
The Call for Proposals is still open, submit early
to avoid the hazards of last minute traffic. Refer to:
http://netdevconf.org/2.1/submit-proposal.html
2) We would like to announce the registration is now open.
Please go ahead and register sooner than later. It helps us
plan better:
https://onlineregistrations.ca/netdev21/
cheers,
jamal
^ permalink raw reply
* Re: [PATCH v4 3/3] mt76: add driver code for MT76x2e
From: Felix Fietkau @ 2017-02-07 9:57 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, kvalo
In-Reply-To: <20170206131400.GA2716@redhat.com>
On 2017-02-06 14:14, Stanislaw Gruszka wrote:
> CCK can be used only on 5GHz? OFDM only on 2GHz? I thought any rates can
> be used on both bands. Or maybe this settings is just named wrongly
> i.e. CCK_SIFS mean sifs for 5GHz and OFDM_SIFS - sifs for 2GHz ?
OFDM is used on 2.4 and 5 GHz, CCK only on 2.4 GHz.
According to the datasheet, CCK_SIFS means SIFS after CCK transmission.
- Felix
^ permalink raw reply
* [PATCH] mac80211: add back lost debugfs files
From: Johannes Berg @ 2017-02-07 9:41 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Somehow these files were never present or lost, but the code
is there and they seem somewhat useful, so add them back.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/mac80211/debugfs.c | 1 +
net/mac80211/debugfs_sta.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index f62cd0e13c58..47bea0babe78 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -356,6 +356,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv);
+ DEBUGFS_ADD(rate_ctrl_alg);
DEBUGFS_ADD(queues);
DEBUGFS_ADD(misc);
#ifdef CONFIG_PM
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index f6003b8c2c33..42601820db20 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -522,6 +522,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
return;
DEBUGFS_ADD(flags);
+ DEBUGFS_ADD(aid);
DEBUGFS_ADD(num_ps_buf_frames);
DEBUGFS_ADD(last_seq_ctrl);
DEBUGFS_ADD(agg_status);
--
2.9.3
^ permalink raw reply related
* Re: [PATCH 1/3] ath10k: remove ath10k_vif_to_arvif()
From: Valo, Kalle @ 2017-02-07 9:14 UTC (permalink / raw)
To: Adrian Chadd
Cc: Amadeusz Sławiński, netdev@vger.kernel.org,
linux-wireless@vger.kernel.org, Linux Kernel Mailing List,
ath10k@lists.infradead.org
In-Reply-To: <CAJ-VmonrHpS3MArM9ohHgLvb6xspP_gnVVeJWA9F-pjUpJ+LZA@mail.gmail.com>
Adrian Chadd <adrian@freebsd.org> writes:
> Removing this method makes the diff to FreeBSD larger, as "vif" in
> FreeBSD is a different pointer.
>
> (Yes, I have ath10k on freebsd working and I'd like to find a way to
> reduce the diff moving forward.)
I don't like this "(void *) vif->drv_priv" style that much either but
apparently it's commonly used in Linux wireless code and already parts
of ath10k. So this patch just unifies the coding style.
--=20
Kalle Valo=
^ permalink raw reply
* Re: [v2,1/3] ath9k: rename tx_complete_work to hw_check_work
From: Kalle Valo @ 2017-02-07 9:01 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, kvalo
In-Reply-To: <20170202091452.11939-1-nbd@nbd.name>
Felix Fietkau <nbd@nbd.name> wrote:
> Also include common MAC alive check. This should make the hang checks
> more reliable for modes where beacons are not sent and is used as a
> starting point for further hang check improvements
>
> Signed-off-by: Felix Fietkau <nbd@nbd.name>
3 patches applied to ath-next branch of ath.git, thanks.
d63ffc45c5d3 ath9k: rename tx_complete_work to hw_check_work
a34d0a0da1ab ath9k_hw: check if the chip failed to wake up
3a5e969bb2f6 ath9k: fix race condition in enabling/disabling IRQs
--
https://patchwork.kernel.org/patch/9551439/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: wcn36xx: Implement cancel_hw_scan
From: Kalle Valo @ 2017-02-07 9:00 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Eugene Krasnikov, Kalle Valo, wcn36xx, linux-wireless, netdev,
linux-kernel, linux-arm-msm, Mart Raudsepp
In-Reply-To: <20170201152747.838-1-bjorn.andersson@linaro.org>
Bjorn Andersson <bjorn.andersson@linaro.org> wrote:
> In the event that the wcn36xx interface is brought down while a hw_scan
> is active we must abort and wait for the ongoing scan to signal
> completion to mac80211.
>
> Reported-by: Mart Raudsepp <leio@gentoo.org>
> Fixes: 886039036c20 ("wcn36xx: Implement firmware assisted scan")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> Tested-by: Mart Raudsepp <leio@gentoo.org>
Patch applied to ath-next branch of ath.git, thanks.
03c95dbef642 wcn36xx: Implement cancel_hw_scan
--
https://patchwork.kernel.org/patch/9550795/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: ath10k: fix reading sram contents for QCA4019
From: Kalle Valo @ 2017-02-07 8:58 UTC (permalink / raw)
To: Ashok Raj Nagarajan; +Cc: ath10k, Ashok Raj Nagarajan, arnagara, linux-wireless
In-Reply-To: <1485887811-23313-1-git-send-email-arnagara@qti.qualcomm.com>
Ashok Raj Nagarajan <arnagara@qti.qualcomm.com> wrote:
> With QCA4019 platform, SRAM address can be accessed directly from host but
> currently, we are assuming sram addresses cannot be accessed directly and
> hence we convert the addresses.
>
> While there, clean up growing hw checks during conversion of target CPU
> address to CE address. Now we have function pointer pertaining to different
> chips.
>
> Signed-off-by: Ashok Raj Nagarajan <arnagara@qti.qualcomm.com>
Patch applied to ath-next branch of ath.git, thanks.
7f622593cc5a ath10k: fix reading sram contents for QCA4019
--
https://patchwork.kernel.org/patch/9548043/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: [PATCHv3] ath10k: fix boot failure in UTF mode/testmode
From: Kalle Valo @ 2017-02-07 8:52 UTC (permalink / raw)
To: c_traja; +Cc: ath10k, Tamizh chelvam, tamizhchelvam, linux-wireless, stable
In-Reply-To: <1485838569-1027-1-git-send-email-c_traja@qti.qualcomm.com>
c_traja@qti.qualcomm.com wrote:
> From: Tamizh chelvam <c_traja@qti.qualcomm.com>
>
> Rx filter reset and the dynamic tx switch mode (EXT_RESOURCE_CFG)
> configuration are causing the following errors when UTF firmware
> is loaded to the target.
>
> Error message 1:
> [ 598.015629] ath10k_pci 0001:01:00.0: failed to ping firmware: -110
> [ 598.020828] ath10k_pci 0001:01:00.0: failed to reset rx filter: -110
> [ 598.141556] ath10k_pci 0001:01:00.0: failed to start core (testmode): -110
>
> Error message 2:
> [ 668.615839] ath10k_ahb a000000.wifi: failed to send ext resource cfg command : -95
> [ 668.618902] ath10k_ahb a000000.wifi: failed to start core (testmode): -95
>
> Avoiding these configurations while bringing the target in
> testmode is solving the problem.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Tamizh chelvam <c_traja@qti.qualcomm.com>
Patch applied to ath-next branch of ath.git, thanks.
cb4281528b62 ath10k: fix boot failure in UTF mode/testmode
--
https://patchwork.kernel.org/patch/9546451/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox