add mt7601u driver
authorJakub Kicinski <kubakici@wp.pl>
Tue, 26 May 2015 09:16:00 +0000 (11:16 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 28 May 2015 08:33:20 +0000 (11:33 +0300)
Add support for the simplest of MediaTek Wi-Fi devices - MT7601U.
It is a single stream bgn chip with no bells or whistles.
This driver is partially based on Felix's mt76 but IMHO it doesn't
make sense to merge the two right now because MT7601U is a design
somewhere between old Ralink devices and new Mediatek chips.  There
wouldn't be all that much code sharing with the devices mt76 supports.
Situation may obviously change when someone decides to extend m76 with
support for the more recent USB dongles.

The driver supports only station mode.  I'm hoping to add AP support
when time allows.

This driver sat on GitHub for quite a while and got some testing there:
http://github.com/kuba-moo/mt7601u

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
31 files changed:
MAINTAINERS
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/mediatek/Kconfig [new file with mode: 0644]
drivers/net/wireless/mediatek/Makefile [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/Kconfig [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/Makefile [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/core.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/debugfs.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/dma.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/dma.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/eeprom.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/eeprom.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/init.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/initvals.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/initvals_phy.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/mac.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/mac.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/main.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/mcu.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/mcu.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/mt7601u.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/phy.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/regs.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/trace.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/trace.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/tx.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/usb.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/usb.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/util.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt7601u/util.h [new file with mode: 0644]

index df106f8..c6f4576 100644 (file)
@@ -6365,6 +6365,12 @@ F:       include/uapi/linux/meye.h
 F:     include/uapi/linux/ivtv*
 F:     include/uapi/linux/uvcvideo.h
 
+MEDIATEK MT7601U WIRELESS LAN DRIVER
+M:     Jakub Kicinski <kubakici@wp.pl>
+L:     linux-wireless@vger.kernel.org
+S:     Maintained
+F:     drivers/net/wireless/mediatek/mt7601u/
+
 MEGARAID SCSI/SAS DRIVERS
 M:     Kashyap Desai <kashyap.desai@avagotech.com>
 M:     Sumit Saxena <sumit.saxena@avagotech.com>
index 16604bd..a63ab2e 100644 (file)
@@ -277,6 +277,7 @@ source "drivers/net/wireless/libertas/Kconfig"
 source "drivers/net/wireless/orinoco/Kconfig"
 source "drivers/net/wireless/p54/Kconfig"
 source "drivers/net/wireless/rt2x00/Kconfig"
+source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/rtlwifi/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
index 0c88916..6b9e729 100644 (file)
@@ -45,6 +45,8 @@ obj-$(CONFIG_IWLWIFI) += iwlwifi/
 obj-$(CONFIG_IWLEGACY) += iwlegacy/
 obj-$(CONFIG_RT2X00)   += rt2x00/
 
+obj-$(CONFIG_WL_MEDIATEK)      += mediatek/
+
 obj-$(CONFIG_P54_COMMON)       += p54/
 
 obj-$(CONFIG_ATH_CARDS)                += ath/
diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig
new file mode 100644 (file)
index 0000000..cba300c
--- /dev/null
@@ -0,0 +1,10 @@
+menuconfig WL_MEDIATEK
+       bool "Mediatek Wireless LAN support"
+       ---help---
+         Enable community drivers for MediaTek WiFi devices.
+         Those drivers make use of the Linux mac80211 stack.
+
+
+if WL_MEDIATEK
+source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
+endif # WL_MEDIATEK
diff --git a/drivers/net/wireless/mediatek/Makefile b/drivers/net/wireless/mediatek/Makefile
new file mode 100644 (file)
index 0000000..9d5f182
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_MT7601U)  += mt7601u/
diff --git a/drivers/net/wireless/mediatek/mt7601u/Kconfig b/drivers/net/wireless/mediatek/mt7601u/Kconfig
new file mode 100644 (file)
index 0000000..f46bed9
--- /dev/null
@@ -0,0 +1,6 @@
+config MT7601U
+       tristate "MediaTek MT7601U (USB) support"
+       depends on MAC80211
+       depends on USB
+       ---help---
+         This adds support for MT7601U-based wireless USB dongles.
diff --git a/drivers/net/wireless/mediatek/mt7601u/Makefile b/drivers/net/wireless/mediatek/mt7601u/Makefile
new file mode 100644 (file)
index 0000000..ea9ed8a
--- /dev/null
@@ -0,0 +1,9 @@
+ccflags-y += -D__CHECK_ENDIAN__
+
+obj-$(CONFIG_MT7601U)  += mt7601u.o
+
+mt7601u-objs   = \
+       usb.o init.o main.o mcu.o trace.o dma.o core.o eeprom.o phy.o \
+       mac.o util.o debugfs.o tx.o
+
+CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/mediatek/mt7601u/core.c b/drivers/net/wireless/mediatek/mt7601u/core.c
new file mode 100644 (file)
index 0000000..0aabd79
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+
+int mt7601u_wait_asic_ready(struct mt7601u_dev *dev)
+{
+       int i = 100;
+       u32 val;
+
+       do {
+               if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+                       return -EIO;
+
+               val = mt7601u_rr(dev, MT_MAC_CSR0);
+               if (val && ~val)
+                       return 0;
+
+               udelay(10);
+       } while (i--);
+
+       return -EIO;
+}
+
+bool mt76_poll(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
+              int timeout)
+{
+       u32 cur;
+
+       timeout /= 10;
+       do {
+               if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+                       return false;
+
+               cur = mt7601u_rr(dev, offset) & mask;
+               if (cur == val)
+                       return true;
+
+               udelay(10);
+       } while (timeout-- > 0);
+
+       dev_err(dev->dev, "Error: Time out with reg %08x\n", offset);
+
+       return false;
+}
+
+bool mt76_poll_msec(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
+                   int timeout)
+{
+       u32 cur;
+
+       timeout /= 10;
+       do {
+               if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+                       return false;
+
+               cur = mt7601u_rr(dev, offset) & mask;
+               if (cur == val)
+                       return true;
+
+               msleep(10);
+       } while (timeout-- > 0);
+
+       dev_err(dev->dev, "Error: Time out with reg %08x\n", offset);
+
+       return false;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/debugfs.c b/drivers/net/wireless/mediatek/mt7601u/debugfs.c
new file mode 100644 (file)
index 0000000..fc00847
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include <linux/debugfs.h>
+
+#include "mt7601u.h"
+#include "eeprom.h"
+
+static int
+mt76_reg_set(void *data, u64 val)
+{
+       struct mt7601u_dev *dev = data;
+
+       mt76_wr(dev, dev->debugfs_reg, val);
+       return 0;
+}
+
+static int
+mt76_reg_get(void *data, u64 *val)
+{
+       struct mt7601u_dev *dev = data;
+
+       *val = mt76_rr(dev, dev->debugfs_reg);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n");
+
+static int
+mt7601u_ampdu_stat_read(struct seq_file *file, void *data)
+{
+       struct mt7601u_dev *dev = file->private;
+       int i, j;
+
+#define stat_printf(grp, off, name)                                    \
+       seq_printf(file, #name ":\t%llu\n", dev->stats.grp[off])
+
+       stat_printf(rx_stat, 0, rx_crc_err);
+       stat_printf(rx_stat, 1, rx_phy_err);
+       stat_printf(rx_stat, 2, rx_false_cca);
+       stat_printf(rx_stat, 3, rx_plcp_err);
+       stat_printf(rx_stat, 4, rx_fifo_overflow);
+       stat_printf(rx_stat, 5, rx_duplicate);
+
+       stat_printf(tx_stat, 0, tx_fail_cnt);
+       stat_printf(tx_stat, 1, tx_bcn_cnt);
+       stat_printf(tx_stat, 2, tx_success);
+       stat_printf(tx_stat, 3, tx_retransmit);
+       stat_printf(tx_stat, 4, tx_zero_len);
+       stat_printf(tx_stat, 5, tx_underflow);
+
+       stat_printf(aggr_stat, 0, non_aggr_tx);
+       stat_printf(aggr_stat, 1, aggr_tx);
+
+       stat_printf(zero_len_del, 0, tx_zero_len_del);
+       stat_printf(zero_len_del, 1, rx_zero_len_del);
+#undef stat_printf
+
+       seq_puts(file, "Aggregations stats:\n");
+       for (i = 0; i < 4; i++) {
+               for (j = 0; j < 8; j++)
+                       seq_printf(file, "%08llx ",
+                                  dev->stats.aggr_n[i * 8 + j]);
+               seq_putc(file, '\n');
+       }
+
+       seq_printf(file, "recent average AMPDU len: %d\n",
+                  atomic_read(&dev->avg_ampdu_len));
+
+       return 0;
+}
+
+static int
+mt7601u_ampdu_stat_open(struct inode *inode, struct file *f)
+{
+       return single_open(f, mt7601u_ampdu_stat_read, inode->i_private);
+}
+
+static const struct file_operations fops_ampdu_stat = {
+       .open = mt7601u_ampdu_stat_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int
+mt7601u_eeprom_param_read(struct seq_file *file, void *data)
+{
+       struct mt7601u_dev *dev = file->private;
+       struct mt7601u_rate_power *rp = &dev->ee->power_rate_table;
+       struct tssi_data *td = &dev->ee->tssi_data;
+       int i;
+
+       seq_printf(file, "RF freq offset: %hhx\n", dev->ee->rf_freq_off);
+       seq_printf(file, "RSSI offset: %hhx %hhx\n",
+                  dev->ee->rssi_offset[0], dev->ee->rssi_offset[1]);
+       seq_printf(file, "Reference temp: %hhx\n", dev->ee->ref_temp);
+       seq_printf(file, "LNA gain: %hhx\n", dev->ee->lna_gain);
+       seq_printf(file, "Reg channels: %hhu-%hhu\n", dev->ee->reg.start,
+                  dev->ee->reg.start + dev->ee->reg.num - 1);
+
+       seq_puts(file, "Per rate power:\n");
+       for (i = 0; i < 2; i++)
+               seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
+                          rp->cck[i].raw, rp->cck[i].bw20, rp->cck[i].bw40);
+       for (i = 0; i < 4; i++)
+               seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
+                          rp->ofdm[i].raw, rp->ofdm[i].bw20, rp->ofdm[i].bw40);
+       for (i = 0; i < 4; i++)
+               seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
+                          rp->ht[i].raw, rp->ht[i].bw20, rp->ht[i].bw40);
+
+       seq_puts(file, "Per channel power:\n");
+       for (i = 0; i < 7; i++)
+               seq_printf(file, "\t tx_power  ch%u:%02hhx ch%u:%02hhx\n",
+                          i * 2 + 1, dev->ee->chan_pwr[i * 2],
+                          i * 2 + 2, dev->ee->chan_pwr[i * 2 + 1]);
+
+       if (!dev->ee->tssi_enabled)
+               return 0;
+
+       seq_puts(file, "TSSI:\n");
+       seq_printf(file, "\t slope:%02hhx\n", td->slope);
+       seq_printf(file, "\t offset=%02hhx %02hhx %02hhx\n",
+                  td->offset[0], td->offset[1], td->offset[2]);
+       seq_printf(file, "\t delta_off:%08x\n", td->tx0_delta_offset);
+
+       return 0;
+}
+
+static int
+mt7601u_eeprom_param_open(struct inode *inode, struct file *f)
+{
+       return single_open(f, mt7601u_eeprom_param_read, inode->i_private);
+}
+
+static const struct file_operations fops_eeprom_param = {
+       .open = mt7601u_eeprom_param_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+void mt7601u_init_debugfs(struct mt7601u_dev *dev)
+{
+       struct dentry *dir;
+
+       dir = debugfs_create_dir("mt7601u", dev->hw->wiphy->debugfsdir);
+       if (!dir)
+               return;
+
+       debugfs_create_u8("temperature", S_IRUSR, dir, &dev->raw_temp);
+       debugfs_create_u32("temp_mode", S_IRUSR, dir, &dev->temp_mode);
+
+       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_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
+       debugfs_create_file("eeprom_param", S_IRUSR, dir, dev,
+                           &fops_eeprom_param);
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
new file mode 100644 (file)
index 0000000..9c9e128
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "dma.h"
+#include "usb.h"
+#include "trace.h"
+
+static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev,
+                                struct mt7601u_dma_buf_rx *e, gfp_t gfp);
+
+static unsigned int ieee80211_get_hdrlen_from_buf(const u8 *data, unsigned len)
+{
+       const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)data;
+       unsigned int hdrlen;
+
+       if (unlikely(len < 10))
+               return 0;
+       hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       if (unlikely(hdrlen > len))
+               return 0;
+       return hdrlen;
+}
+
+static struct sk_buff *
+mt7601u_rx_skb_from_seg(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi,
+                       u8 *data, u32 seg_len)
+{
+       struct sk_buff *skb;
+       u32 true_len;
+
+       if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
+               seg_len -= 2;
+
+       skb = alloc_skb(seg_len, GFP_ATOMIC);
+       if (!skb)
+               return NULL;
+
+       if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) {
+               int hdr_len = ieee80211_get_hdrlen_from_buf(data, seg_len);
+
+               memcpy(skb_put(skb, hdr_len), data, hdr_len);
+               data += hdr_len + 2;
+               seg_len -= hdr_len;
+       }
+
+       memcpy(skb_put(skb, seg_len), data, seg_len);
+
+       true_len = mt76_mac_process_rx(dev, skb, skb->data, rxwi);
+       skb_trim(skb, true_len);
+
+       return skb;
+}
+
+static struct sk_buff *
+mt7601u_rx_skb_from_seg_paged(struct mt7601u_dev *dev,
+                             struct mt7601u_rxwi *rxwi, void *data,
+                             u32 seg_len, u32 truesize, struct page *p)
+{
+       unsigned int hdr_len = ieee80211_get_hdrlen_from_buf(data, seg_len);
+       unsigned int true_len, copy, frag;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(128, GFP_ATOMIC);
+       if (!skb)
+               return NULL;
+
+       true_len = mt76_mac_process_rx(dev, skb, data, rxwi);
+
+       if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) {
+               memcpy(skb_put(skb, hdr_len), data, hdr_len);
+               data += hdr_len + 2;
+               true_len -= hdr_len;
+               hdr_len = 0;
+       }
+
+       copy = (true_len <= skb_tailroom(skb)) ? true_len : hdr_len + 8;
+       frag = true_len - copy;
+
+       memcpy(skb_put(skb, copy), data, copy);
+       data += copy;
+
+       if (frag) {
+               skb_add_rx_frag(skb, 0, p, data - page_address(p),
+                               frag, truesize);
+               get_page(p);
+       }
+
+       return skb;
+}
+
+static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data,
+                                  u32 seg_len, struct page *p, bool paged)
+{
+       struct sk_buff *skb;
+       struct mt7601u_rxwi *rxwi;
+       u32 fce_info, truesize = seg_len;
+
+       /* DMA_INFO field at the beginning of the segment contains only some of
+        * the information, we need to read the FCE descriptor from the end.
+        */
+       fce_info = get_unaligned_le32(data + seg_len - MT_FCE_INFO_LEN);
+       seg_len -= MT_FCE_INFO_LEN;
+
+       data += MT_DMA_HDR_LEN;
+       seg_len -= MT_DMA_HDR_LEN;
+
+       rxwi = (struct mt7601u_rxwi *) data;
+       data += sizeof(struct mt7601u_rxwi);
+       seg_len -= sizeof(struct mt7601u_rxwi);
+
+       if (unlikely(rxwi->zero[0] || rxwi->zero[1] || rxwi->zero[2]))
+               dev_err_once(dev->dev, "Error: RXWI zero fields are set\n");
+       if (unlikely(MT76_GET(MT_RXD_INFO_TYPE, fce_info)))
+               dev_err_once(dev->dev, "Error: RX path seen a non-pkt urb\n");
+
+       trace_mt_rx(dev, rxwi, fce_info);
+
+       if (paged)
+               skb = mt7601u_rx_skb_from_seg_paged(dev, rxwi, data, seg_len,
+                                                   truesize, p);
+       else
+               skb = mt7601u_rx_skb_from_seg(dev, rxwi, data, seg_len);
+       if (!skb)
+               return;
+
+       ieee80211_rx_ni(dev->hw, skb);
+}
+
+static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len)
+{
+       u32 min_seg_len = MT_DMA_HDR_LEN + MT_RX_INFO_LEN +
+               sizeof(struct mt7601u_rxwi) + MT_FCE_INFO_LEN;
+       u16 dma_len = get_unaligned_le16(data);
+
+       if (data_len < min_seg_len ||
+           WARN_ON(!dma_len) ||
+           WARN_ON(dma_len + MT_DMA_HDRS > data_len) ||
+           WARN_ON(dma_len & 0x3))
+               return 0;
+
+       return MT_DMA_HDRS + dma_len;
+}
+
+static void
+mt7601u_rx_process_entry(struct mt7601u_dev *dev, struct mt7601u_dma_buf_rx *e)
+{
+       u32 seg_len, data_len = e->urb->actual_length;
+       u8 *data = page_address(e->p);
+       struct page *new_p = NULL;
+       bool paged = true;
+       int cnt = 0;
+
+       if (!test_bit(MT7601U_STATE_INITIALIZED, &dev->state))
+               return;
+
+       /* Copy if there is very little data in the buffer. */
+       if (data_len < 512) {
+               paged = false;
+       } else {
+               new_p = dev_alloc_pages(MT_RX_ORDER);
+               if (!new_p)
+                       paged = false;
+       }
+
+       while ((seg_len = mt7601u_rx_next_seg_len(data, data_len))) {
+               mt7601u_rx_process_seg(dev, data, seg_len, e->p, paged);
+
+               data_len -= seg_len;
+               data += seg_len;
+               cnt++;
+       }
+
+       if (cnt > 1)
+               trace_mt_rx_dma_aggr(dev, cnt, paged);
+
+       if (paged) {
+               /* we have one extra ref from the allocator */
+               __free_pages(e->p, MT_RX_ORDER);
+
+               e->p = new_p;
+       }
+}
+
+static struct mt7601u_dma_buf_rx *
+mt7601u_rx_get_pending_entry(struct mt7601u_dev *dev)
+{
+       struct mt7601u_rx_queue *q = &dev->rx_q;
+       struct mt7601u_dma_buf_rx *buf = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->rx_lock, flags);
+
+       if (!q->pending)
+               goto out;
+
+       buf = &q->e[q->start];
+       q->pending--;
+       q->start = (q->start + 1) % q->entries;
+out:
+       spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+       return buf;
+}
+
+static void mt7601u_complete_rx(struct urb *urb)
+{
+       struct mt7601u_dev *dev = urb->context;
+       struct mt7601u_rx_queue *q = &dev->rx_q;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->rx_lock, flags);
+
+       if (mt7601u_urb_has_error(urb))
+               dev_err(dev->dev, "Error: RX urb failed:%d\n", urb->status);
+       if (WARN_ONCE(q->e[q->end].urb != urb, "RX urb mismatch"))
+               goto out;
+
+       q->end = (q->end + 1) % q->entries;
+       q->pending++;
+       tasklet_schedule(&dev->rx_tasklet);
+out:
+       spin_unlock_irqrestore(&dev->rx_lock, flags);
+}
+
+static void mt7601u_rx_tasklet(unsigned long data)
+{
+       struct mt7601u_dev *dev = (struct mt7601u_dev *) data;
+       struct mt7601u_dma_buf_rx *e;
+
+       while ((e = mt7601u_rx_get_pending_entry(dev))) {
+               if (e->urb->status)
+                       continue;
+
+               mt7601u_rx_process_entry(dev, e);
+               mt7601u_submit_rx_buf(dev, e, GFP_ATOMIC);
+       }
+}
+
+static void mt7601u_complete_tx(struct urb *urb)
+{
+       struct mt7601u_tx_queue *q = urb->context;
+       struct mt7601u_dev *dev = q->dev;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->tx_lock, flags);
+
+       if (mt7601u_urb_has_error(urb))
+               dev_err(dev->dev, "Error: TX urb failed:%d\n", urb->status);
+       if (WARN_ONCE(q->e[q->start].urb != urb, "TX urb mismatch"))
+               goto out;
+
+       skb = q->e[q->start].skb;
+       trace_mt_tx_dma_done(dev, skb);
+
+       mt7601u_tx_status(dev, skb);
+
+       if (q->used == q->entries - q->entries / 8)
+               ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb));
+
+       q->start = (q->start + 1) % q->entries;
+       q->used--;
+
+       if (urb->status)
+               goto out;
+
+       set_bit(MT7601U_STATE_MORE_STATS, &dev->state);
+       if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state))
+               queue_delayed_work(dev->stat_wq, &dev->stat_work,
+                                  msecs_to_jiffies(10));
+out:
+       spin_unlock_irqrestore(&dev->tx_lock, flags);
+}
+
+static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
+                                struct sk_buff *skb, u8 ep)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+       unsigned snd_pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep]);
+       struct mt7601u_dma_buf_tx *e;
+       struct mt7601u_tx_queue *q = &dev->tx_q[ep];
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&dev->tx_lock, flags);
+
+       if (WARN_ON(q->entries <= q->used)) {
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       e = &q->e[q->end];
+       e->skb = skb;
+       usb_fill_bulk_urb(e->urb, usb_dev, snd_pipe, skb->data, skb->len,
+                         mt7601u_complete_tx, q);
+       ret = usb_submit_urb(e->urb, GFP_ATOMIC);
+       if (ret) {
+               /* Special-handle ENODEV from TX urb submission because it will
+                * often be the first ENODEV we see after device is removed.
+                */
+               if (ret == -ENODEV)
+                       set_bit(MT7601U_STATE_REMOVED, &dev->state);
+               else
+                       dev_err(dev->dev, "Error: TX urb submit failed:%d\n",
+                               ret);
+               goto out;
+       }
+
+       q->end = (q->end + 1) % q->entries;
+       q->used++;
+
+       if (q->used >= q->entries)
+               ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
+out:
+       spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+       return ret;
+}
+
+/* Map hardware Q to USB endpoint number */
+static u8 q2ep(u8 qid)
+{
+       /* TODO: take management packets to queue 5 */
+       return qid + 1;
+}
+
+/* Map USB endpoint number to Q id in the DMA engine */
+static enum mt76_qsel ep2dmaq(u8 ep)
+{
+       if (ep == 5)
+               return MT_QSEL_MGMT;
+       return MT_QSEL_EDCA;
+}
+
+int mt7601u_dma_enqueue_tx(struct mt7601u_dev *dev, struct sk_buff *skb,
+                          struct mt76_wcid *wcid, int hw_q)
+{
+       u8 ep = q2ep(hw_q);
+       u32 dma_flags;
+       int ret;
+
+       dma_flags = MT_TXD_PKT_INFO_80211;
+       if (wcid->hw_key_idx == 0xff)
+               dma_flags |= MT_TXD_PKT_INFO_WIV;
+
+       ret = mt7601u_dma_skb_wrap_pkt(skb, ep2dmaq(ep), dma_flags);
+       if (ret)
+               return ret;
+
+       ret = mt7601u_dma_submit_tx(dev, skb, ep);
+       if (ret) {
+               ieee80211_free_txskb(dev->hw, skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void mt7601u_kill_rx(struct mt7601u_dev *dev)
+{
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->rx_lock, flags);
+
+       for (i = 0; i < dev->rx_q.entries; i++) {
+               int next = dev->rx_q.end;
+
+               spin_unlock_irqrestore(&dev->rx_lock, flags);
+               usb_poison_urb(dev->rx_q.e[next].urb);
+               spin_lock_irqsave(&dev->rx_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&dev->rx_lock, flags);
+}
+
+static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev,
+                                struct mt7601u_dma_buf_rx *e, gfp_t gfp)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+       u8 *buf = page_address(e->p);
+       unsigned pipe;
+       int ret;
+
+       pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[MT_EP_IN_PKT_RX]);
+
+       usb_fill_bulk_urb(e->urb, usb_dev, pipe, buf, MT_RX_URB_SIZE,
+                         mt7601u_complete_rx, dev);
+
+       trace_mt_submit_urb(dev, e->urb);
+       ret = usb_submit_urb(e->urb, gfp);
+       if (ret)
+               dev_err(dev->dev, "Error: submit RX URB failed:%d\n", ret);
+
+       return ret;
+}
+
+static int mt7601u_submit_rx(struct mt7601u_dev *dev)
+{
+       int i, ret;
+
+       for (i = 0; i < dev->rx_q.entries; i++) {
+               ret = mt7601u_submit_rx_buf(dev, &dev->rx_q.e[i], GFP_KERNEL);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void mt7601u_free_rx(struct mt7601u_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < dev->rx_q.entries; i++) {
+               __free_pages(dev->rx_q.e[i].p, MT_RX_ORDER);
+               usb_free_urb(dev->rx_q.e[i].urb);
+       }
+}
+
+static int mt7601u_alloc_rx(struct mt7601u_dev *dev)
+{
+       int i;
+
+       memset(&dev->rx_q, 0, sizeof(dev->rx_q));
+       dev->rx_q.dev = dev;
+       dev->rx_q.entries = N_RX_ENTRIES;
+
+       for (i = 0; i < N_RX_ENTRIES; i++) {
+               dev->rx_q.e[i].urb = usb_alloc_urb(0, GFP_KERNEL);
+               dev->rx_q.e[i].p = dev_alloc_pages(MT_RX_ORDER);
+
+               if (!dev->rx_q.e[i].urb || !dev->rx_q.e[i].p)
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void mt7601u_free_tx_queue(struct mt7601u_tx_queue *q)
+{
+       int i;
+
+       WARN_ON(q->used);
+
+       for (i = 0; i < q->entries; i++)  {
+               usb_poison_urb(q->e[i].urb);
+               usb_free_urb(q->e[i].urb);
+       }
+}
+
+static void mt7601u_free_tx(struct mt7601u_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < __MT_EP_OUT_MAX; i++)
+               mt7601u_free_tx_queue(&dev->tx_q[i]);
+}
+
+static int mt7601u_alloc_tx_queue(struct mt7601u_dev *dev,
+                                 struct mt7601u_tx_queue *q)
+{
+       int i;
+
+       q->dev = dev;
+       q->entries = N_TX_ENTRIES;
+
+       for (i = 0; i < N_TX_ENTRIES; i++) {
+               q->e[i].urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!q->e[i].urb)
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int mt7601u_alloc_tx(struct mt7601u_dev *dev)
+{
+       int i;
+
+       dev->tx_q = devm_kcalloc(dev->dev, __MT_EP_OUT_MAX,
+                                sizeof(*dev->tx_q), GFP_KERNEL);
+
+       for (i = 0; i < __MT_EP_OUT_MAX; i++)
+               if (mt7601u_alloc_tx_queue(dev, &dev->tx_q[i]))
+                       return -ENOMEM;
+
+       return 0;
+}
+
+int mt7601u_dma_init(struct mt7601u_dev *dev)
+{
+       int ret = -ENOMEM;
+
+       tasklet_init(&dev->rx_tasklet, mt7601u_rx_tasklet, (unsigned long) dev);
+
+       ret = mt7601u_alloc_tx(dev);
+       if (ret)
+               goto err;
+       ret = mt7601u_alloc_rx(dev);
+       if (ret)
+               goto err;
+
+       ret = mt7601u_submit_rx(dev);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       mt7601u_dma_cleanup(dev);
+       return ret;
+}
+
+void mt7601u_dma_cleanup(struct mt7601u_dev *dev)
+{
+       mt7601u_kill_rx(dev);
+
+       tasklet_kill(&dev->rx_tasklet);
+
+       mt7601u_free_rx(dev);
+       mt7601u_free_tx(dev);
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.h b/drivers/net/wireless/mediatek/mt7601u/dma.h
new file mode 100644 (file)
index 0000000..978e8a9
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_DMA_H
+#define __MT7601U_DMA_H
+
+#include <asm/unaligned.h>
+#include <linux/skbuff.h>
+
+#include "util.h"
+
+#define MT_DMA_HDR_LEN                 4
+#define MT_RX_INFO_LEN                 4
+#define MT_FCE_INFO_LEN                        4
+#define MT_DMA_HDRS                    (MT_DMA_HDR_LEN + MT_RX_INFO_LEN)
+
+/* Common Tx DMA descriptor fields */
+#define MT_TXD_INFO_LEN                        GENMASK(15, 0)
+#define MT_TXD_INFO_D_PORT             GENMASK(29, 27)
+#define MT_TXD_INFO_TYPE               GENMASK(31, 30)
+
+enum mt76_msg_port {
+       WLAN_PORT,
+       CPU_RX_PORT,
+       CPU_TX_PORT,
+       HOST_PORT,
+       VIRTUAL_CPU_RX_PORT,
+       VIRTUAL_CPU_TX_PORT,
+       DISCARD,
+};
+
+enum mt76_info_type {
+       DMA_PACKET,
+       DMA_COMMAND,
+};
+
+/* Tx DMA packet specific flags */
+#define MT_TXD_PKT_INFO_NEXT_VLD       BIT(16)
+#define MT_TXD_PKT_INFO_TX_BURST       BIT(17)
+#define MT_TXD_PKT_INFO_80211          BIT(19)
+#define MT_TXD_PKT_INFO_TSO            BIT(20)
+#define MT_TXD_PKT_INFO_CSO            BIT(21)
+#define MT_TXD_PKT_INFO_WIV            BIT(24)
+#define MT_TXD_PKT_INFO_QSEL           GENMASK(26, 25)
+
+enum mt76_qsel {
+       MT_QSEL_MGMT,
+       MT_QSEL_HCCA,
+       MT_QSEL_EDCA,
+       MT_QSEL_EDCA_2,
+};
+
+/* Tx DMA MCU command specific flags */
+#define MT_TXD_CMD_INFO_SEQ            GENMASK(19, 16)
+#define MT_TXD_CMD_INFO_TYPE           GENMASK(26, 20)
+
+static inline int mt7601u_dma_skb_wrap(struct sk_buff *skb,
+                                      enum mt76_msg_port d_port,
+                                      enum mt76_info_type type, u32 flags)
+{
+       u32 info;
+
+       /* Buffer layout:
+        *      |   4B   | xfer len |      pad       |  4B  |
+        *      | TXINFO | pkt/cmd  | zero pad to 4B | zero |
+        *
+        * length field of TXINFO should be set to 'xfer len'.
+        */
+
+       info = flags |
+               MT76_SET(MT_TXD_INFO_LEN, round_up(skb->len, 4)) |
+               MT76_SET(MT_TXD_INFO_D_PORT, d_port) |
+               MT76_SET(MT_TXD_INFO_TYPE, type);
+
+       put_unaligned_le32(info, skb_push(skb, sizeof(info)));
+       return skb_put_padto(skb, round_up(skb->len, 4) + 4);
+}
+
+static inline int
+mt7601u_dma_skb_wrap_pkt(struct sk_buff *skb, enum mt76_qsel qsel, u32 flags)
+{
+       flags |= MT76_SET(MT_TXD_PKT_INFO_QSEL, qsel);
+       return mt7601u_dma_skb_wrap(skb, WLAN_PORT, DMA_PACKET, flags);
+}
+
+/* Common Rx DMA descriptor fields */
+#define MT_RXD_INFO_LEN                        GENMASK(13, 0)
+#define MT_RXD_INFO_PCIE_INTR          BIT(24)
+#define MT_RXD_INFO_QSEL               GENMASK(26, 25)
+#define MT_RXD_INFO_PORT               GENMASK(29, 27)
+#define MT_RXD_INFO_TYPE               GENMASK(31, 30)
+
+/* Rx DMA packet specific flags */
+#define MT_RXD_PKT_INFO_UDP_ERR                BIT(16)
+#define MT_RXD_PKT_INFO_TCP_ERR                BIT(17)
+#define MT_RXD_PKT_INFO_IP_ERR         BIT(18)
+#define MT_RXD_PKT_INFO_PKT_80211      BIT(19)
+#define MT_RXD_PKT_INFO_L3L4_DONE      BIT(20)
+#define MT_RXD_PKT_INFO_MAC_LEN                GENMASK(23, 21)
+
+/* Rx DMA MCU command specific flags */
+#define MT_RXD_CMD_INFO_SELF_GEN       BIT(15)
+#define MT_RXD_CMD_INFO_CMD_SEQ                GENMASK(19, 16)
+#define MT_RXD_CMD_INFO_EVT_TYPE       GENMASK(23, 20)
+
+enum mt76_evt_type {
+       CMD_DONE,
+       CMD_ERROR,
+       CMD_RETRY,
+       EVENT_PWR_RSP,
+       EVENT_WOW_RSP,
+       EVENT_CARRIER_DETECT_RSP,
+       EVENT_DFS_DETECT_RSP,
+};
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/eeprom.c b/drivers/net/wireless/mediatek/mt7601u/eeprom.c
new file mode 100644 (file)
index 0000000..ce3837f
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/etherdevice.h>
+#include <asm/unaligned.h>
+#include "mt7601u.h"
+#include "eeprom.h"
+
+static bool
+field_valid(u8 val)
+{
+       return val != 0xff;
+}
+
+static s8
+field_validate(u8 val)
+{
+       if (!field_valid(val))
+               return 0;
+
+       return val;
+}
+
+static int
+mt7601u_efuse_read(struct mt7601u_dev *dev, u16 addr, u8 *data,
+                  enum mt7601u_eeprom_access_modes mode)
+{
+       u32 val;
+       int i;
+
+       val = mt76_rr(dev, MT_EFUSE_CTRL);
+       val &= ~(MT_EFUSE_CTRL_AIN |
+                MT_EFUSE_CTRL_MODE);
+       val |= MT76_SET(MT_EFUSE_CTRL_AIN, addr & ~0xf) |
+              MT76_SET(MT_EFUSE_CTRL_MODE, mode) |
+              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;
+
+       val = mt76_rr(dev, MT_EFUSE_CTRL);
+       if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) {
+               /* Parts of eeprom not in the usage map (0x80-0xc0,0xf0)
+                * will not return valid data but it's ok.
+                */
+               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
+mt7601u_efuse_physical_size_check(struct mt7601u_dev *dev)
+{
+       const int map_reads = DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16);
+       u8 data[map_reads * 16];
+       int ret, i;
+       u32 start = 0, end = 0, cnt_free;
+
+       for (i = 0; i < map_reads; i++) {
+               ret = mt7601u_efuse_read(dev, MT_EE_USAGE_MAP_START + i * 16,
+                                        data + i * 16, MT_EE_PHYSICAL_READ);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < MT_EFUSE_USAGE_MAP_SIZE; i++)
+               if (!data[i]) {
+                       if (!start)
+                               start = MT_EE_USAGE_MAP_START + i;
+                       end = MT_EE_USAGE_MAP_START + i;
+               }
+       cnt_free = end - start + 1;
+
+       if (MT_EFUSE_USAGE_MAP_SIZE - cnt_free < 5) {
+               dev_err(dev->dev, "Error: your device needs default EEPROM file and this driver doesn't support it!\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static bool
+mt7601u_has_tssi(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1);
+
+       return ~nic_conf1 && (nic_conf1 & MT_EE_NIC_CONF_1_TX_ALC_EN);
+}
+
+static void
+mt7601u_set_chip_cap(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       u16 nic_conf0 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_0);
+       u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1);
+
+       if (!field_valid(nic_conf1 & 0xff))
+               nic_conf1 &= 0xff00;
+
+       dev->ee->tssi_enabled = mt7601u_has_tssi(dev, eeprom) &&
+                               !(nic_conf1 & MT_EE_NIC_CONF_1_TEMP_TX_ALC);
+
+       if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL)
+               dev_err(dev->dev,
+                       "Error: this driver does not support HW RF ctrl\n");
+
+       if (!field_valid(nic_conf0 >> 8))
+               return;
+
+       if (MT76_GET(MT_EE_NIC_CONF_0_RX_PATH, nic_conf0) > 1 ||
+           MT76_GET(MT_EE_NIC_CONF_0_TX_PATH, nic_conf0) > 1)
+               dev_err(dev->dev,
+                       "Error: device has more than 1 RX/TX stream!\n");
+}
+
+static int
+mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *eeprom)
+{
+       const void *src = eeprom + MT_EE_MAC_ADDR;
+
+       ether_addr_copy(dev->macaddr, src);
+
+       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);
+       }
+
+       mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr));
+       mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) |
+               MT76_SET(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff));
+
+       return 0;
+}
+
+static void mt7601u_set_channel_target_power(struct mt7601u_dev *dev,
+                                            u8 *eeprom, u8 max_pwr)
+{
+       u8 trgt_pwr = eeprom[MT_EE_TX_TSSI_TARGET_POWER];
+
+       if (trgt_pwr > max_pwr || !trgt_pwr) {
+               dev_warn(dev->dev, "Error: EEPROM trgt power invalid %hhx!\n",
+                        trgt_pwr);
+               trgt_pwr = 0x20;
+       }
+
+       memset(dev->ee->chan_pwr, trgt_pwr, sizeof(dev->ee->chan_pwr));
+}
+
+static void
+mt7601u_set_channel_power(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       u32 i, val;
+       u8 max_pwr;
+
+       val = mt7601u_rr(dev, MT_TX_ALC_CFG_0);
+       max_pwr = MT76_GET(MT_TX_ALC_CFG_0_LIMIT_0, val);
+
+       if (mt7601u_has_tssi(dev, eeprom)) {
+               mt7601u_set_channel_target_power(dev, eeprom, max_pwr);
+               return;
+       }
+
+       for (i = 0; i < 14; i++) {
+               s8 power = field_validate(eeprom[MT_EE_TX_POWER_OFFSET + i]);
+
+               if (power > max_pwr || power < 0)
+                       power = MT7601U_DEFAULT_TX_POWER;
+
+               dev->ee->chan_pwr[i] = power;
+       }
+}
+
+static void
+mt7601u_set_country_reg(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       /* Note: - region 31 is not valid for mt7601u (see rtmp_init.c)
+        *       - comments in rtmp_def.h are incorrect (see rt_channel.c)
+        */
+       static const struct reg_channel_bounds chan_bounds[] = {
+               /* EEPROM country regions 0 - 7 */
+               {  1, 11 },     {  1, 13 },     { 10,  2 },     { 10,  4 },
+               { 14,  1 },     {  1, 14 },     {  3,  7 },     {  5,  9 },
+               /* EEPROM country regions 32 - 33 */
+               {  1, 11 },     {  1, 14 }
+       };
+       u8 val = eeprom[MT_EE_COUNTRY_REGION];
+       int idx = -1;
+
+       if (val < 8)
+               idx = val;
+       if (val > 31 && val < 33)
+               idx = val - 32 + 8;
+
+       if (idx != -1)
+               dev_info(dev->dev,
+                        "EEPROM country region %02hhx (channels %hhd-%hhd)\n",
+                        val, chan_bounds[idx].start,
+                        chan_bounds[idx].start + chan_bounds[idx].num - 1);
+       else
+               idx = 5; /* channels 1 - 14 */
+
+       dev->ee->reg = chan_bounds[idx];
+
+       /* TODO: country region 33 is special - phy should be set to B-mode
+        *       before entering channel 14 (see sta/connect.c)
+        */
+}
+
+static void
+mt7601u_set_rf_freq_off(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       u8 comp;
+
+       dev->ee->rf_freq_off = field_validate(eeprom[MT_EE_FREQ_OFFSET]);
+       comp = field_validate(eeprom[MT_EE_FREQ_OFFSET_COMPENSATION]);
+
+       if (comp & BIT(7))
+               dev->ee->rf_freq_off -= comp & 0x7f;
+       else
+               dev->ee->rf_freq_off += comp;
+}
+
+static void
+mt7601u_set_rssi_offset(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       int i;
+       s8 *rssi_offset = dev->ee->rssi_offset;
+
+       for (i = 0; i < 2; i++) {
+               rssi_offset[i] = eeprom[MT_EE_RSSI_OFFSET + i];
+
+               if (rssi_offset[i] < -10 || rssi_offset[i] > 10) {
+                       dev_warn(dev->dev,
+                                "Warning: EEPROM RSSI is invalid %02hhx\n",
+                                rssi_offset[i]);
+                       rssi_offset[i] = 0;
+               }
+       }
+}
+
+static void
+mt7601u_extra_power_over_mac(struct mt7601u_dev *dev)
+{
+       u32 val;
+
+       val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_1) & 0x0000ff00) >> 8);
+       val |= ((mt7601u_rr(dev, MT_TX_PWR_CFG_2) & 0x0000ff00) << 8);
+       mt7601u_wr(dev, MT_TX_PWR_CFG_7, val);
+
+       val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_4) & 0x0000ff00) >> 8);
+       mt7601u_wr(dev, MT_TX_PWR_CFG_9, val);
+}
+
+static void
+mt7601u_set_power_rate(struct power_per_rate *rate, s8 delta, u8 value)
+{
+       rate->raw = s6_validate(value);
+       rate->bw20 = s6_to_int(value);
+       /* Note: vendor driver does cap the value to s6 right away */
+       rate->bw40 = rate->bw20 + delta;
+}
+
+static void
+mt7601u_save_power_rate(struct mt7601u_dev *dev, s8 delta, u32 val, int i)
+{
+       struct mt7601u_rate_power *t = &dev->ee->power_rate_table;
+
+       switch (i) {
+       case 0:
+               mt7601u_set_power_rate(&t->cck[0], delta, (val >> 0) & 0xff);
+               mt7601u_set_power_rate(&t->cck[1], delta, (val >> 8) & 0xff);
+               /* Save cck bw20 for fixups of channel 14 */
+               dev->ee->real_cck_bw20[0] = t->cck[0].bw20;
+               dev->ee->real_cck_bw20[1] = t->cck[1].bw20;
+
+               mt7601u_set_power_rate(&t->ofdm[0], delta, (val >> 16) & 0xff);
+               mt7601u_set_power_rate(&t->ofdm[1], delta, (val >> 24) & 0xff);
+               break;
+       case 1:
+               mt7601u_set_power_rate(&t->ofdm[2], delta, (val >> 0) & 0xff);
+               mt7601u_set_power_rate(&t->ofdm[3], delta, (val >> 8) & 0xff);
+               mt7601u_set_power_rate(&t->ht[0], delta, (val >> 16) & 0xff);
+               mt7601u_set_power_rate(&t->ht[1], delta, (val >> 24) & 0xff);
+               break;
+       case 2:
+               mt7601u_set_power_rate(&t->ht[2], delta, (val >> 0) & 0xff);
+               mt7601u_set_power_rate(&t->ht[3], delta, (val >> 8) & 0xff);
+               break;
+       }
+}
+
+static s8
+get_delta(u8 val)
+{
+       s8 ret;
+
+       if (!field_valid(val) || !(val & BIT(7)))
+               return 0;
+
+       ret = val & 0x1f;
+       if (ret > 8)
+               ret = 8;
+       if (val & BIT(6))
+               ret = -ret;
+
+       return ret;
+}
+
+static void
+mt7601u_config_tx_power_per_rate(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       u32 val;
+       s8 bw40_delta;
+       int i;
+
+       bw40_delta = get_delta(eeprom[MT_EE_TX_POWER_DELTA_BW40]);
+
+       for (i = 0; i < 5; i++) {
+               val = get_unaligned_le32(eeprom + MT_EE_TX_POWER_BYRATE(i));
+
+               mt7601u_save_power_rate(dev, bw40_delta, val, i);
+
+               if (~val)
+                       mt7601u_wr(dev, MT_TX_PWR_CFG_0 + i * 4, val);
+       }
+
+       mt7601u_extra_power_over_mac(dev);
+}
+
+static void
+mt7601u_init_tssi_params(struct mt7601u_dev *dev, u8 *eeprom)
+{
+       struct tssi_data *d = &dev->ee->tssi_data;
+
+       if (!dev->ee->tssi_enabled)
+               return;
+
+       d->slope = eeprom[MT_EE_TX_TSSI_SLOPE];
+       d->tx0_delta_offset = eeprom[MT_EE_TX_TSSI_OFFSET] * 1024;
+       d->offset[0] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP];
+       d->offset[1] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 1];
+       d->offset[2] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 2];
+}
+
+int
+mt7601u_eeprom_init(struct mt7601u_dev *dev)
+{
+       u8 *eeprom;
+       int i, ret;
+
+       ret = mt7601u_efuse_physical_size_check(dev);
+       if (ret)
+               return ret;
+
+       dev->ee = devm_kzalloc(dev->dev, sizeof(*dev->ee), GFP_KERNEL);
+       if (!dev->ee)
+               return -ENOMEM;
+
+       eeprom = kmalloc(MT7601U_EEPROM_SIZE, GFP_KERNEL);
+       if (!eeprom)
+               return -ENOMEM;
+
+       for (i = 0; i + 16 <= MT7601U_EEPROM_SIZE; i += 16) {
+               ret = mt7601u_efuse_read(dev, i, eeprom + i, MT_EE_READ);
+               if (ret)
+                       goto out;
+       }
+
+       if (eeprom[MT_EE_VERSION_EE] > MT7601U_EE_MAX_VER)
+               dev_warn(dev->dev,
+                        "Warning: unsupported EEPROM version %02hhx\n",
+                        eeprom[MT_EE_VERSION_EE]);
+       dev_info(dev->dev, "EEPROM ver:%02hhx fae:%02hhx\n",
+                eeprom[MT_EE_VERSION_EE], eeprom[MT_EE_VERSION_FAE]);
+
+       mt7601u_set_macaddr(dev, eeprom);
+       mt7601u_set_chip_cap(dev, eeprom);
+       mt7601u_set_channel_power(dev, eeprom);
+       mt7601u_set_country_reg(dev, eeprom);
+       mt7601u_set_rf_freq_off(dev, eeprom);
+       mt7601u_set_rssi_offset(dev, eeprom);
+       dev->ee->ref_temp = eeprom[MT_EE_REF_TEMP];
+       dev->ee->lna_gain = eeprom[MT_EE_LNA_GAIN];
+
+       mt7601u_config_tx_power_per_rate(dev, eeprom);
+
+       mt7601u_init_tssi_params(dev, eeprom);
+out:
+       kfree(eeprom);
+       return ret;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/eeprom.h b/drivers/net/wireless/mediatek/mt7601u/eeprom.h
new file mode 100644 (file)
index 0000000..662d127
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_EEPROM_H
+#define __MT7601U_EEPROM_H
+
+struct mt7601u_dev;
+
+#define MT7601U_EE_MAX_VER                     0x0c
+#define MT7601U_EEPROM_SIZE                    256
+
+#define MT7601U_DEFAULT_TX_POWER               6
+
+enum mt76_eeprom_field {
+       MT_EE_CHIP_ID =                         0x00,
+       MT_EE_VERSION_FAE =                     0x02,
+       MT_EE_VERSION_EE =                      0x03,
+       MT_EE_MAC_ADDR =                        0x04,
+       MT_EE_NIC_CONF_0 =                      0x34,
+       MT_EE_NIC_CONF_1 =                      0x36,
+       MT_EE_COUNTRY_REGION =                  0x39,
+       MT_EE_FREQ_OFFSET =                     0x3a,
+       MT_EE_NIC_CONF_2 =                      0x42,
+
+       MT_EE_LNA_GAIN =                        0x44,
+       MT_EE_RSSI_OFFSET =                     0x46,
+
+       MT_EE_TX_POWER_DELTA_BW40 =             0x50,
+       MT_EE_TX_POWER_OFFSET =                 0x52,
+
+       MT_EE_TX_TSSI_SLOPE =                   0x6e,
+       MT_EE_TX_TSSI_OFFSET_GROUP =            0x6f,
+       MT_EE_TX_TSSI_OFFSET =                  0x76,
+
+       MT_EE_TX_TSSI_TARGET_POWER =            0xd0,
+       MT_EE_REF_TEMP =                        0xd1,
+       MT_EE_FREQ_OFFSET_COMPENSATION =        0xdb,
+       MT_EE_TX_POWER_BYRATE_BASE =            0xde,
+
+       MT_EE_USAGE_MAP_START =                 0x1e0,
+       MT_EE_USAGE_MAP_END =                   0x1fc,
+};
+
+#define MT_EE_NIC_CONF_0_RX_PATH               GENMASK(3, 0)
+#define MT_EE_NIC_CONF_0_TX_PATH               GENMASK(7, 4)
+#define MT_EE_NIC_CONF_0_BOARD_TYPE            GENMASK(13, 12)
+
+#define MT_EE_NIC_CONF_1_HW_RF_CTRL            BIT(0)
+#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)
+
+#define MT_EE_TX_POWER_BYRATE(i)               (MT_EE_TX_POWER_BYRATE_BASE + \
+                                                (i) * 4)
+
+#define MT_EFUSE_USAGE_MAP_SIZE                        (MT_EE_USAGE_MAP_END -  \
+                                                MT_EE_USAGE_MAP_START + 1)
+
+enum mt7601u_eeprom_access_modes {
+       MT_EE_READ = 0,
+       MT_EE_PHYSICAL_READ = 1,
+};
+
+struct power_per_rate  {
+       u8 raw;  /* validated s6 value */
+       s8 bw20; /* sign-extended int */
+       s8 bw40; /* sign-extended int */
+};
+
+/* Power per rate - one value per two rates */
+struct mt7601u_rate_power {
+       struct power_per_rate cck[2];
+       struct power_per_rate ofdm[4];
+       struct power_per_rate ht[4];
+};
+
+struct reg_channel_bounds {
+       u8 start;
+       u8 num;
+};
+
+struct mt7601u_eeprom_params {
+       bool tssi_enabled;
+       u8 rf_freq_off;
+       s8 rssi_offset[2];
+       s8 ref_temp;
+       s8 lna_gain;
+
+       u8 chan_pwr[14];
+       struct mt7601u_rate_power power_rate_table;
+       s8 real_cck_bw20[2];
+
+       /* TSSI stuff - only with internal TX ALC */
+       struct tssi_data {
+               int tx0_delta_offset;
+               u8 slope;
+               u8 offset[3];
+       } tssi_data;
+
+       struct reg_channel_bounds reg;
+};
+
+int mt7601u_eeprom_init(struct mt7601u_dev *dev);
+
+static inline u32 s6_validate(u32 reg)
+{
+       WARN_ON(reg & ~GENMASK(5, 0));
+       return reg & GENMASK(5, 0);
+}
+
+static inline int s6_to_int(u32 reg)
+{
+       int s6;
+
+       s6 = s6_validate(reg);
+       if (s6 & BIT(5))
+               s6 -= BIT(6);
+
+       return s6;
+}
+
+static inline u32 int_to_s6(int val)
+{
+       if (val < -0x20)
+               return 0x20;
+       if (val > 0x1f)
+               return 0x1f;
+
+       return val & 0x3f;
+}
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c
new file mode 100644 (file)
index 0000000..1fc86e8
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ * (c) Copyright 2002-2010, Ralink Technology, Inc.
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "eeprom.h"
+#include "trace.h"
+#include "mcu.h"
+
+#include "initvals.h"
+
+static void
+mt7601u_set_wlan_state(struct mt7601u_dev *dev, u32 val, bool enable)
+{
+       int i;
+
+       /* Note: we don't turn off WLAN_CLK because that makes the device
+        *       not respond properly on the probe path.
+        *       In case anyone (PSM?) wants to use this function we can
+        *       bring the clock stuff back and fixup the probe path.
+        */
+
+       if (enable)
+               val |= (MT_WLAN_FUN_CTRL_WLAN_EN |
+                       MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
+       else
+               val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN);
+
+       mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
+       udelay(20);
+
+       if (enable) {
+               set_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state);
+       } else {
+               clear_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state);
+               return;
+       }
+
+       for (i = 200; i; i--) {
+               val = mt7601u_rr(dev, MT_CMB_CTRL);
+
+               if (val & MT_CMB_CTRL_XTAL_RDY && val & MT_CMB_CTRL_PLL_LD)
+                       break;
+
+               udelay(20);
+       }
+
+       /* Note: vendor driver tries to disable/enable wlan here and retry
+        *       but the code which does it is so buggy it must have never
+        *       triggered, so don't bother.
+        */
+       if (!i)
+               dev_err(dev->dev, "Error: PLL and XTAL check failed!\n");
+}
+
+static void mt7601u_chip_onoff(struct mt7601u_dev *dev, bool enable, bool reset)
+{
+       u32 val;
+
+       mutex_lock(&dev->hw_atomic_mutex);
+
+       val = mt7601u_rr(dev, MT_WLAN_FUN_CTRL);
+
+       if (reset) {
+               val |= MT_WLAN_FUN_CTRL_GPIO_OUT_EN;
+               val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL;
+
+               if (val & MT_WLAN_FUN_CTRL_WLAN_EN) {
+                       val |= (MT_WLAN_FUN_CTRL_WLAN_RESET |
+                               MT_WLAN_FUN_CTRL_WLAN_RESET_RF);
+                       mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
+                       udelay(20);
+
+                       val &= ~(MT_WLAN_FUN_CTRL_WLAN_RESET |
+                                MT_WLAN_FUN_CTRL_WLAN_RESET_RF);
+               }
+       }
+
+       mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
+       udelay(20);
+
+       mt7601u_set_wlan_state(dev, val, enable);
+
+       mutex_unlock(&dev->hw_atomic_mutex);
+}
+
+static void mt7601u_reset_csr_bbp(struct mt7601u_dev *dev)
+{
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, (MT_MAC_SYS_CTRL_RESET_CSR |
+                                         MT_MAC_SYS_CTRL_RESET_BBP));
+       mt7601u_wr(dev, MT_USB_DMA_CFG, 0);
+       msleep(1);
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0);
+}
+
+static void mt7601u_init_usb_dma(struct mt7601u_dev *dev)
+{
+       u32 val;
+
+       val = MT76_SET(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, MT_USB_AGGR_TIMEOUT) |
+             MT76_SET(MT_USB_DMA_CFG_RX_BULK_AGG_LMT, MT_USB_AGGR_SIZE_LIMIT) |
+             MT_USB_DMA_CFG_RX_BULK_EN |
+             MT_USB_DMA_CFG_TX_BULK_EN;
+       if (dev->in_max_packet == 512)
+               val |= MT_USB_DMA_CFG_RX_BULK_AGG_EN;
+       mt7601u_wr(dev, MT_USB_DMA_CFG, val);
+
+       val |= MT_USB_DMA_CFG_UDMA_RX_WL_DROP;
+       mt7601u_wr(dev, MT_USB_DMA_CFG, val);
+       val &= ~MT_USB_DMA_CFG_UDMA_RX_WL_DROP;
+       mt7601u_wr(dev, MT_USB_DMA_CFG, val);
+}
+
+static int mt7601u_init_bbp(struct mt7601u_dev *dev)
+{
+       int ret;
+
+       ret = mt7601u_wait_bbp_ready(dev);
+       if (ret)
+               return ret;
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_common_vals,
+                                     ARRAY_SIZE(bbp_common_vals));
+       if (ret)
+               return ret;
+
+       return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_chip_vals,
+                                      ARRAY_SIZE(bbp_chip_vals));
+}
+
+static void
+mt76_init_beacon_offsets(struct mt7601u_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++)
+               mt7601u_wr(dev, MT_BCN_OFFSET(i), regs[i]);
+}
+
+static int mt7601u_write_mac_initvals(struct mt7601u_dev *dev)
+{
+       int ret;
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, mac_common_vals,
+                                     ARRAY_SIZE(mac_common_vals));
+       if (ret)
+               return ret;
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN,
+                                     mac_chip_vals, ARRAY_SIZE(mac_chip_vals));
+       if (ret)
+               return ret;
+
+       mt76_init_beacon_offsets(dev);
+
+       mt7601u_wr(dev, MT_AUX_CLK_CFG, 0);
+
+       return 0;
+}
+
+static int mt7601u_init_wcid_mem(struct mt7601u_dev *dev)
+{
+       u32 *vals;
+       int i, ret;
+
+       vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL);
+       if (!vals)
+               return -ENOMEM;
+
+       for (i = 0; i < N_WCIDS; i++)  {
+               vals[i * 2] = 0xffffffff;
+               vals[i * 2 + 1] = 0x00ffffff;
+       }
+
+       ret = mt7601u_burst_write_regs(dev, MT_WCID_ADDR_BASE,
+                                      vals, N_WCIDS * 2);
+       kfree(vals);
+
+       return ret;
+}
+
+static int mt7601u_init_key_mem(struct mt7601u_dev *dev)
+{
+       u32 vals[4] = {};
+
+       return mt7601u_burst_write_regs(dev, MT_SKEY_MODE_BASE_0,
+                                       vals, ARRAY_SIZE(vals));
+}
+
+static int mt7601u_init_wcid_attr_mem(struct mt7601u_dev *dev)
+{
+       u32 *vals;
+       int i, ret;
+
+       vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL);
+       if (!vals)
+               return -ENOMEM;
+
+       for (i = 0; i < N_WCIDS * 2; i++)
+               vals[i] = 1;
+
+       ret = mt7601u_burst_write_regs(dev, MT_WCID_ATTR_BASE,
+                                      vals, N_WCIDS * 2);
+       kfree(vals);
+
+       return ret;
+}
+
+static void mt7601u_reset_counters(struct mt7601u_dev *dev)
+{
+       mt7601u_rr(dev, MT_RX_STA_CNT0);
+       mt7601u_rr(dev, MT_RX_STA_CNT1);
+       mt7601u_rr(dev, MT_RX_STA_CNT2);
+       mt7601u_rr(dev, MT_TX_STA_CNT0);
+       mt7601u_rr(dev, MT_TX_STA_CNT1);
+       mt7601u_rr(dev, MT_TX_STA_CNT2);
+}
+
+int mt7601u_mac_start(struct mt7601u_dev *dev)
+{
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
+
+       if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
+                      MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 200000))
+               return -ETIMEDOUT;
+
+       dev->rxfilter = MT_RX_FILTR_CFG_CRC_ERR |
+               MT_RX_FILTR_CFG_PHY_ERR | MT_RX_FILTR_CFG_PROMISC |
+               MT_RX_FILTR_CFG_VER_ERR | MT_RX_FILTR_CFG_DUP |
+               MT_RX_FILTR_CFG_CFACK | MT_RX_FILTR_CFG_CFEND |
+               MT_RX_FILTR_CFG_ACK | MT_RX_FILTR_CFG_CTS |
+               MT_RX_FILTR_CFG_RTS | MT_RX_FILTR_CFG_PSPOLL |
+               MT_RX_FILTR_CFG_BA | MT_RX_FILTR_CFG_CTRL_RSV;
+       mt7601u_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
+
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL,
+                  MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
+
+       if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
+                      MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 50))
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static void mt7601u_mac_stop_hw(struct mt7601u_dev *dev)
+{
+       int i, ok;
+
+       if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return;
+
+       mt76_clear(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_TIMER_EN |
+                  MT_BEACON_TIME_CFG_SYNC_MODE | MT_BEACON_TIME_CFG_TBTT_EN |
+                  MT_BEACON_TIME_CFG_BEACON_TX);
+
+       if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_BUSY, 0, 1000))
+               dev_warn(dev->dev, "Warning: TX DMA did not stop!\n");
+
+       /* Page count on TxQ */
+       i = 200;
+       while (i-- && ((mt76_rr(dev, 0x0438) & 0xffffffff) ||
+                      (mt76_rr(dev, 0x0a30) & 0x000000ff) ||
+                      (mt76_rr(dev, 0x0a34) & 0x00ff00ff)))
+               msleep(10);
+
+       if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX, 0, 1000))
+               dev_warn(dev->dev, "Warning: MAC TX did not stop!\n");
+
+       mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX |
+                                        MT_MAC_SYS_CTRL_ENABLE_TX);
+
+       /* Page count on RxQ */
+       ok = 0;
+       i = 200;
+       while (i--) {
+               if ((mt76_rr(dev, 0x0430) & 0x00ff0000) ||
+                   (mt76_rr(dev, 0x0a30) & 0xffffffff) ||
+                   (mt76_rr(dev, 0x0a34) & 0xffffffff))
+                       ok++;
+               if (ok > 6)
+                       break;
+
+               msleep(1);
+       }
+
+       if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 1000))
+               dev_warn(dev->dev, "Warning: MAC RX did not stop!\n");
+
+       if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_RX_BUSY, 0, 1000))
+               dev_warn(dev->dev, "Warning: RX DMA did not stop!\n");
+}
+
+void mt7601u_mac_stop(struct mt7601u_dev *dev)
+{
+       mt7601u_mac_stop_hw(dev);
+       flush_delayed_work(&dev->stat_work);
+       cancel_delayed_work_sync(&dev->stat_work);
+}
+
+static void mt7601u_stop_hardware(struct mt7601u_dev *dev)
+{
+       mt7601u_chip_onoff(dev, false, false);
+}
+
+int mt7601u_init_hardware(struct mt7601u_dev *dev)
+{
+       static const u16 beacon_offsets[16] = {
+               /* 512 byte per beacon */
+               0xc000, 0xc200, 0xc400, 0xc600,
+               0xc800, 0xca00, 0xcc00, 0xce00,
+               0xd000, 0xd200, 0xd400, 0xd600,
+               0xd800, 0xda00, 0xdc00, 0xde00
+       };
+       int ret;
+
+       dev->beacon_offsets = beacon_offsets;
+
+       mt7601u_chip_onoff(dev, true, false);
+
+       ret = mt7601u_wait_asic_ready(dev);
+       if (ret)
+               goto err;
+       ret = mt7601u_mcu_init(dev);
+       if (ret)
+               goto err;
+
+       if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG,
+                           MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
+                           MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) {
+               ret = -EIO;
+               goto err;
+       }
+
+       /* Wait for ASIC ready after FW load. */
+       ret = mt7601u_wait_asic_ready(dev);
+       if (ret)
+               goto err;
+
+       mt7601u_reset_csr_bbp(dev);
+       mt7601u_init_usb_dma(dev);
+
+       ret = mt7601u_mcu_cmd_init(dev);
+       if (ret)
+               goto err;
+       ret = mt7601u_dma_init(dev);
+       if (ret)
+               goto err_mcu;
+       ret = mt7601u_write_mac_initvals(dev);
+       if (ret)
+               goto err_rx;
+
+       if (!mt76_poll_msec(dev, MT_MAC_STATUS,
+                           MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 100)) {
+               ret = -EIO;
+               goto err_rx;
+       }
+
+       ret = mt7601u_init_bbp(dev);
+       if (ret)
+               goto err_rx;
+       ret = mt7601u_init_wcid_mem(dev);
+       if (ret)
+               goto err_rx;
+       ret = mt7601u_init_key_mem(dev);
+       if (ret)
+               goto err_rx;
+       ret = mt7601u_init_wcid_attr_mem(dev);
+       if (ret)
+               goto err_rx;
+
+       mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN |
+                                            MT_BEACON_TIME_CFG_SYNC_MODE |
+                                            MT_BEACON_TIME_CFG_TBTT_EN |
+                                            MT_BEACON_TIME_CFG_BEACON_TX));
+
+       mt7601u_reset_counters(dev);
+
+       mt7601u_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e);
+
+       mt7601u_wr(dev, MT_TXOP_CTRL_CFG, MT76_SET(MT_TXOP_TRUN_EN, 0x3f) |
+                                         MT76_SET(MT_TXOP_EXT_CCA_DLY, 0x58));
+
+       ret = mt7601u_eeprom_init(dev);
+       if (ret)
+               goto err_rx;
+
+       ret = mt7601u_phy_init(dev);
+       if (ret)
+               goto err_rx;
+
+       mt7601u_set_rx_path(dev, 0);
+       mt7601u_set_tx_dac(dev, 0);
+
+       mt7601u_mac_set_ctrlch(dev, false);
+       mt7601u_bbp_set_ctrlch(dev, false);
+       mt7601u_bbp_set_bw(dev, MT_BW_20);
+
+       return 0;
+
+err_rx:
+       mt7601u_dma_cleanup(dev);
+err_mcu:
+       mt7601u_mcu_cmd_deinit(dev);
+err:
+       mt7601u_chip_onoff(dev, false, false);
+       return ret;
+}
+
+void mt7601u_cleanup(struct mt7601u_dev *dev)
+{
+       mt7601u_stop_hardware(dev);
+       mt7601u_dma_cleanup(dev);
+       mt7601u_mcu_cmd_deinit(dev);
+}
+
+struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev)
+{
+       struct ieee80211_hw *hw;
+       struct mt7601u_dev *dev;
+
+       hw = ieee80211_alloc_hw(sizeof(*dev), &mt7601u_ops);
+       if (!hw)
+               return NULL;
+
+       dev = hw->priv;
+       dev->dev = pdev;
+       dev->hw = hw;
+       mutex_init(&dev->vendor_req_mutex);
+       mutex_init(&dev->reg_atomic_mutex);
+       mutex_init(&dev->hw_atomic_mutex);
+       mutex_init(&dev->mutex);
+       spin_lock_init(&dev->tx_lock);
+       spin_lock_init(&dev->rx_lock);
+       spin_lock_init(&dev->lock);
+       spin_lock_init(&dev->con_mon_lock);
+       atomic_set(&dev->avg_ampdu_len, 1);
+
+       dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0);
+       if (!dev->stat_wq) {
+               ieee80211_free_hw(hw);
+               return NULL;
+       }
+
+       return dev;
+}
+
+#define CHAN2G(_idx, _freq) {                  \
+       .band = IEEE80211_BAND_2GHZ,            \
+       .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),
+};
+
+#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 mt76_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 int
+mt76_init_sband(struct mt7601u_dev *dev, struct ieee80211_supported_band *sband,
+               const struct ieee80211_channel *chan, int n_chan,
+               struct ieee80211_rate *rates, int n_rates)
+{
+       struct ieee80211_sta_ht_cap *ht_cap;
+       void *chanlist;
+       int size;
+
+       size = n_chan * sizeof(*chan);
+       chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
+       if (!chanlist)
+               return -ENOMEM;
+
+       sband->channels = chanlist;
+       sband->n_channels = n_chan;
+       sband->bitrates = rates;
+       sband->n_bitrates = n_rates;
+
+       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 |
+                     (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+       ht_cap->mcs.rx_mask[0] = 0xff;
+       ht_cap->mcs.rx_mask[4] = 0x1;
+       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_2;
+
+       dev->chandef.chan = &sband->channels[0];
+
+       return 0;
+}
+
+static int
+mt76_init_sband_2g(struct mt7601u_dev *dev)
+{
+       dev->sband_2g = devm_kzalloc(dev->dev, sizeof(*dev->sband_2g),
+                                    GFP_KERNEL);
+       dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = dev->sband_2g;
+
+       WARN_ON(dev->ee->reg.start - 1 + dev->ee->reg.num >
+               ARRAY_SIZE(mt76_channels_2ghz));
+
+       return mt76_init_sband(dev, dev->sband_2g,
+                              &mt76_channels_2ghz[dev->ee->reg.start - 1],
+                              dev->ee->reg.num,
+                              mt76_rates, ARRAY_SIZE(mt76_rates));
+}
+
+int mt7601u_register_device(struct mt7601u_dev *dev)
+{
+       struct ieee80211_hw *hw = dev->hw;
+       struct wiphy *wiphy = hw->wiphy;
+       int ret;
+
+       /* Reserve WCID 0 for mcast - thanks to this APs WCID will go to
+        * entry no. 1 like it does in the vendor driver.
+        */
+       dev->wcid_mask[0] |= 1;
+
+       /* init fake wcid for monitor interfaces */
+       dev->mon_wcid = devm_kmalloc(dev->dev, sizeof(*dev->mon_wcid),
+                                    GFP_KERNEL);
+       if (!dev->mon_wcid)
+               return -ENOMEM;
+       dev->mon_wcid->idx = 0xff;
+       dev->mon_wcid->hw_key_idx = -1;
+
+       SET_IEEE80211_DEV(hw, dev->dev);
+
+       hw->queues = 4;
+       hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_PS_NULLFUNC_STACK |
+                   IEEE80211_HW_SUPPORTS_HT_CCK_RATES |
+                   IEEE80211_HW_AMPDU_AGGREGATION |
+                   IEEE80211_HW_SUPPORTS_RC_TABLE;
+       hw->max_rates = 1;
+       hw->max_report_rates = 7;
+       hw->max_rate_tries = 1;
+
+       hw->sta_data_size = sizeof(struct mt76_sta);
+       hw->vif_data_size = sizeof(struct mt76_vif);
+
+       SET_IEEE80211_PERM_ADDR(hw, dev->macaddr);
+
+       wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+       ret = mt76_init_sband_2g(dev);
+       if (ret)
+               return ret;
+
+       INIT_DELAYED_WORK(&dev->mac_work, mt7601u_mac_work);
+       INIT_DELAYED_WORK(&dev->stat_work, mt7601u_tx_stat);
+
+       ret = ieee80211_register_hw(hw);
+       if (ret)
+               return ret;
+
+       mt7601u_init_debugfs(dev);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/initvals.h b/drivers/net/wireless/mediatek/mt7601u/initvals.h
new file mode 100644 (file)
index 0000000..ec11ff6
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * (c) Copyright 2002-2010, Ralink Technology, Inc.
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_INITVALS_H
+#define __MT7601U_INITVALS_H
+
+static const struct mt76_reg_pair bbp_common_vals[] = {
+       {  65,  0x2c },
+       {  66,  0x38 },
+       {  68,  0x0b },
+       {  69,  0x12 },
+       {  70,  0x0a },
+       {  73,  0x10 },
+       {  81,  0x37 },
+       {  82,  0x62 },
+       {  83,  0x6a },
+       {  84,  0x99 },
+       {  86,  0x00 },
+       {  91,  0x04 },
+       {  92,  0x00 },
+       { 103,  0x00 },
+       { 105,  0x05 },
+       { 106,  0x35 },
+};
+
+static const struct mt76_reg_pair bbp_chip_vals[] = {
+       {   1, 0x04 },  {   4, 0x40 },  {  20, 0x06 },  {  31, 0x08 },
+       /* CCK Tx Control */
+       { 178, 0xff },
+       /* AGC/Sync controls */
+       {  66, 0x14 },  {  68, 0x8b },  {  69, 0x12 },  {  70, 0x09 },
+       {  73, 0x11 },  {  75, 0x60 },  {  76, 0x44 },  {  84, 0x9a },
+       {  86, 0x38 },  {  91, 0x07 },  {  92, 0x02 },
+       /* Rx Path Controls */
+       {  99, 0x50 },  { 101, 0x00 },  { 103, 0xc0 },  { 104, 0x92 },
+       { 105, 0x3c },  { 106, 0x03 },  { 128, 0x12 },
+       /* Change RXWI content: Gain Report */
+       { 142, 0x04 },  { 143, 0x37 },
+       /* Change RXWI content: Antenna Report */
+       { 142, 0x03 },  { 143, 0x99 },
+       /* Calibration Index Register */
+       /* CCK Receiver Control */
+       { 160, 0xeb },  { 161, 0xc4 },  { 162, 0x77 },  { 163, 0xf9 },
+       { 164, 0x88 },  { 165, 0x80 },  { 166, 0xff },  { 167, 0xe4 },
+       /* Added AGC controls - these AGC/GLRT registers are accessed
+        * through R195 and R196.
+        */
+       { 195, 0x00 },  { 196, 0x00 },
+       { 195, 0x01 },  { 196, 0x04 },
+       { 195, 0x02 },  { 196, 0x20 },
+       { 195, 0x03 },  { 196, 0x0a },
+       { 195, 0x06 },  { 196, 0x16 },
+       { 195, 0x07 },  { 196, 0x05 },
+       { 195, 0x08 },  { 196, 0x37 },
+       { 195, 0x0a },  { 196, 0x15 },
+       { 195, 0x0b },  { 196, 0x17 },
+       { 195, 0x0c },  { 196, 0x06 },
+       { 195, 0x0d },  { 196, 0x09 },
+       { 195, 0x0e },  { 196, 0x05 },
+       { 195, 0x0f },  { 196, 0x09 },
+       { 195, 0x10 },  { 196, 0x20 },
+       { 195, 0x20 },  { 196, 0x17 },
+       { 195, 0x21 },  { 196, 0x06 },
+       { 195, 0x22 },  { 196, 0x09 },
+       { 195, 0x23 },  { 196, 0x17 },
+       { 195, 0x24 },  { 196, 0x06 },
+       { 195, 0x25 },  { 196, 0x09 },
+       { 195, 0x26 },  { 196, 0x17 },
+       { 195, 0x27 },  { 196, 0x06 },
+       { 195, 0x28 },  { 196, 0x09 },
+       { 195, 0x29 },  { 196, 0x05 },
+       { 195, 0x2a },  { 196, 0x09 },
+       { 195, 0x80 },  { 196, 0x8b },
+       { 195, 0x81 },  { 196, 0x12 },
+       { 195, 0x82 },  { 196, 0x09 },
+       { 195, 0x83 },  { 196, 0x17 },
+       { 195, 0x84 },  { 196, 0x11 },
+       { 195, 0x85 },  { 196, 0x00 },
+       { 195, 0x86 },  { 196, 0x00 },
+       { 195, 0x87 },  { 196, 0x18 },
+       { 195, 0x88 },  { 196, 0x60 },
+       { 195, 0x89 },  { 196, 0x44 },
+       { 195, 0x8a },  { 196, 0x8b },
+       { 195, 0x8b },  { 196, 0x8b },
+       { 195, 0x8c },  { 196, 0x8b },
+       { 195, 0x8d },  { 196, 0x8b },
+       { 195, 0x8e },  { 196, 0x09 },
+       { 195, 0x8f },  { 196, 0x09 },
+       { 195, 0x90 },  { 196, 0x09 },
+       { 195, 0x91 },  { 196, 0x09 },
+       { 195, 0x92 },  { 196, 0x11 },
+       { 195, 0x93 },  { 196, 0x11 },
+       { 195, 0x94 },  { 196, 0x11 },
+       { 195, 0x95 },  { 196, 0x11 },
+       /* PPAD */
+       {  47, 0x80 },  {  60, 0x80 },  { 150, 0xd2 },  { 151, 0x32 },
+       { 152, 0x23 },  { 153, 0x41 },  { 154, 0x00 },  { 155, 0x4f },
+       { 253, 0x7e },  { 195, 0x30 },  { 196, 0x32 },  { 195, 0x31 },
+       { 196, 0x23 },  { 195, 0x32 },  { 196, 0x45 },  { 195, 0x35 },
+       { 196, 0x4a },  { 195, 0x36 },  { 196, 0x5a },  { 195, 0x37 },
+       { 196, 0x5a },
+};
+
+static const struct mt76_reg_pair mac_common_vals[] = {
+       { MT_LEGACY_BASIC_RATE,         0x0000013f },
+       { MT_HT_BASIC_RATE,             0x00008003 },
+       { MT_MAC_SYS_CTRL,              0x00000000 },
+       { MT_RX_FILTR_CFG,              0x00017f97 },
+       { MT_BKOFF_SLOT_CFG,            0x00000209 },
+       { MT_TX_SW_CFG0,                0x00000000 },
+       { MT_TX_SW_CFG1,                0x00080606 },
+       { MT_TX_LINK_CFG,               0x00001020 },
+       { MT_TX_TIMEOUT_CFG,            0x000a2090 },
+       { MT_MAX_LEN_CFG,               0x00003fff },
+       { MT_PBF_TX_MAX_PCNT,           0x1fbf1f1f },
+       { MT_PBF_RX_MAX_PCNT,           0x0000009f },
+       { MT_TX_RETRY_CFG,              0x47d01f0f },
+       { MT_AUTO_RSP_CFG,              0x00000013 },
+       { MT_CCK_PROT_CFG,              0x05740003 },
+       { MT_OFDM_PROT_CFG,             0x05740003 },
+       { MT_MM40_PROT_CFG,             0x03f44084 },
+       { MT_GF20_PROT_CFG,             0x01744004 },
+       { MT_GF40_PROT_CFG,             0x03f44084 },
+       { MT_MM20_PROT_CFG,             0x01744004 },
+       { MT_TXOP_CTRL_CFG,             0x0000583f },
+       { MT_TX_RTS_CFG,                0x01092b20 },
+       { MT_EXP_ACK_TIME,              0x002400ca },
+       { MT_TXOP_HLDR_ET,              0x00000002 },
+       { MT_XIFS_TIME_CFG,             0x33a41010 },
+       { MT_PWR_PIN_CFG,               0x00000000 },
+};
+
+static const struct mt76_reg_pair mac_chip_vals[] = {
+       { MT_TSO_CTRL,                  0x00006050 },
+       { MT_BCN_OFFSET(0),             0x18100800 },
+       { MT_BCN_OFFSET(1),             0x38302820 },
+       { MT_PBF_SYS_CTRL,              0x00080c00 },
+       { MT_PBF_CFG,                   0x7f723c1f },
+       { MT_FCE_PSE_CTRL,              0x00000001 },
+       { MT_PAUSE_ENABLE_CONTROL1,     0x00000000 },
+       { MT_TX0_RF_GAIN_CORR,          0x003b0005 },
+       { MT_TX0_RF_GAIN_ATTEN,         0x00006900 },
+       { MT_TX0_BB_GAIN_ATTEN,         0x00000400 },
+       { MT_TX_ALC_VGA3,               0x00060006 },
+       { MT_TX_SW_CFG0,                0x00000402 },
+       { MT_TX_SW_CFG1,                0x00000000 },
+       { MT_TX_SW_CFG2,                0x00000000 },
+       { MT_HEADER_TRANS_CTRL_REG,     0x00000000 },
+       { MT_FCE_CSO,                   0x0000030f },
+       { MT_FCE_PARAMETERS,            0x00256f0f },
+};
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/initvals_phy.h b/drivers/net/wireless/mediatek/mt7601u/initvals_phy.h
new file mode 100644 (file)
index 0000000..a2bdc3e
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * (c) Copyright 2002-2010, Ralink Technology, Inc.
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_PHY_INITVALS_H
+#define __MT7601U_PHY_INITVALS_H
+
+#define RF_REG_PAIR(bank, reg, value)                          \
+       { MT_MCU_MEMMAP_RF | (bank) << 16 | (reg), value }
+
+static const struct mt76_reg_pair rf_central[] = {
+       /* Bank 0 - for central blocks: BG, PLL, XTAL, LO, ADC/DAC */
+       RF_REG_PAIR(0,   0, 0x02),
+       RF_REG_PAIR(0,   1, 0x01),
+       RF_REG_PAIR(0,   2, 0x11),
+       RF_REG_PAIR(0,   3, 0xff),
+       RF_REG_PAIR(0,   4, 0x0a),
+       RF_REG_PAIR(0,   5, 0x20),
+       RF_REG_PAIR(0,   6, 0x00),
+       /* B/G */
+       RF_REG_PAIR(0,   7, 0x00),
+       RF_REG_PAIR(0,   8, 0x00),
+       RF_REG_PAIR(0,   9, 0x00),
+       RF_REG_PAIR(0,  10, 0x00),
+       RF_REG_PAIR(0,  11, 0x21),
+       /* XO */
+       RF_REG_PAIR(0,  13, 0x00),              /* 40mhz xtal */
+       /* RF_REG_PAIR(0,       13, 0x13), */   /* 20mhz xtal */
+       RF_REG_PAIR(0,  14, 0x7c),
+       RF_REG_PAIR(0,  15, 0x22),
+       RF_REG_PAIR(0,  16, 0x80),
+       /* PLL */
+       RF_REG_PAIR(0,  17, 0x99),
+       RF_REG_PAIR(0,  18, 0x99),
+       RF_REG_PAIR(0,  19, 0x09),
+       RF_REG_PAIR(0,  20, 0x50),
+       RF_REG_PAIR(0,  21, 0xb0),
+       RF_REG_PAIR(0,  22, 0x00),
+       RF_REG_PAIR(0,  23, 0xc5),
+       RF_REG_PAIR(0,  24, 0xfc),
+       RF_REG_PAIR(0,  25, 0x40),
+       RF_REG_PAIR(0,  26, 0x4d),
+       RF_REG_PAIR(0,  27, 0x02),
+       RF_REG_PAIR(0,  28, 0x72),
+       RF_REG_PAIR(0,  29, 0x01),
+       RF_REG_PAIR(0,  30, 0x00),
+       RF_REG_PAIR(0,  31, 0x00),
+       /* test ports */
+       RF_REG_PAIR(0,  32, 0x00),
+       RF_REG_PAIR(0,  33, 0x00),
+       RF_REG_PAIR(0,  34, 0x23),
+       RF_REG_PAIR(0,  35, 0x01), /* change setting to reduce spurs */
+       RF_REG_PAIR(0,  36, 0x00),
+       RF_REG_PAIR(0,  37, 0x00),
+       /* ADC/DAC */
+       RF_REG_PAIR(0,  38, 0x00),
+       RF_REG_PAIR(0,  39, 0x20),
+       RF_REG_PAIR(0,  40, 0x00),
+       RF_REG_PAIR(0,  41, 0xd0),
+       RF_REG_PAIR(0,  42, 0x1b),
+       RF_REG_PAIR(0,  43, 0x02),
+       RF_REG_PAIR(0,  44, 0x00),
+};
+
+static const struct mt76_reg_pair rf_channel[] = {
+       RF_REG_PAIR(4,   0, 0x01),
+       RF_REG_PAIR(4,   1, 0x00),
+       RF_REG_PAIR(4,   2, 0x00),
+       RF_REG_PAIR(4,   3, 0x00),
+       /* LDO */
+       RF_REG_PAIR(4,   4, 0x00),
+       RF_REG_PAIR(4,   5, 0x08),
+       RF_REG_PAIR(4,   6, 0x00),
+       /* RX */
+       RF_REG_PAIR(4,   7, 0x5b),
+       RF_REG_PAIR(4,   8, 0x52),
+       RF_REG_PAIR(4,   9, 0xb6),
+       RF_REG_PAIR(4,  10, 0x57),
+       RF_REG_PAIR(4,  11, 0x33),
+       RF_REG_PAIR(4,  12, 0x22),
+       RF_REG_PAIR(4,  13, 0x3d),
+       RF_REG_PAIR(4,  14, 0x3e),
+       RF_REG_PAIR(4,  15, 0x13),
+       RF_REG_PAIR(4,  16, 0x22),
+       RF_REG_PAIR(4,  17, 0x23),
+       RF_REG_PAIR(4,  18, 0x02),
+       RF_REG_PAIR(4,  19, 0xa4),
+       RF_REG_PAIR(4,  20, 0x01),
+       RF_REG_PAIR(4,  21, 0x12),
+       RF_REG_PAIR(4,  22, 0x80),
+       RF_REG_PAIR(4,  23, 0xb3),
+       RF_REG_PAIR(4,  24, 0x00), /* reserved */
+       RF_REG_PAIR(4,  25, 0x00), /* reserved */
+       RF_REG_PAIR(4,  26, 0x00), /* reserved */
+       RF_REG_PAIR(4,  27, 0x00), /* reserved */
+       /* LOGEN */
+       RF_REG_PAIR(4,  28, 0x18),
+       RF_REG_PAIR(4,  29, 0xee),
+       RF_REG_PAIR(4,  30, 0x6b),
+       RF_REG_PAIR(4,  31, 0x31),
+       RF_REG_PAIR(4,  32, 0x5d),
+       RF_REG_PAIR(4,  33, 0x00), /* reserved */
+       /* TX */
+       RF_REG_PAIR(4,  34, 0x96),
+       RF_REG_PAIR(4,  35, 0x55),
+       RF_REG_PAIR(4,  36, 0x08),
+       RF_REG_PAIR(4,  37, 0xbb),
+       RF_REG_PAIR(4,  38, 0xb3),
+       RF_REG_PAIR(4,  39, 0xb3),
+       RF_REG_PAIR(4,  40, 0x03),
+       RF_REG_PAIR(4,  41, 0x00), /* reserved */
+       RF_REG_PAIR(4,  42, 0x00), /* reserved */
+       RF_REG_PAIR(4,  43, 0xc5),
+       RF_REG_PAIR(4,  44, 0xc5),
+       RF_REG_PAIR(4,  45, 0xc5),
+       RF_REG_PAIR(4,  46, 0x07),
+       RF_REG_PAIR(4,  47, 0xa8),
+       RF_REG_PAIR(4,  48, 0xef),
+       RF_REG_PAIR(4,  49, 0x1a),
+       /* PA */
+       RF_REG_PAIR(4,  54, 0x07),
+       RF_REG_PAIR(4,  55, 0xa7),
+       RF_REG_PAIR(4,  56, 0xcc),
+       RF_REG_PAIR(4,  57, 0x14),
+       RF_REG_PAIR(4,  58, 0x07),
+       RF_REG_PAIR(4,  59, 0xa8),
+       RF_REG_PAIR(4,  60, 0xd7),
+       RF_REG_PAIR(4,  61, 0x10),
+       RF_REG_PAIR(4,  62, 0x1c),
+       RF_REG_PAIR(4,  63, 0x00), /* reserved */
+};
+
+static const struct mt76_reg_pair rf_vga[] = {
+       RF_REG_PAIR(5,   0, 0x47),
+       RF_REG_PAIR(5,   1, 0x00),
+       RF_REG_PAIR(5,   2, 0x00),
+       RF_REG_PAIR(5,   3, 0x08),
+       RF_REG_PAIR(5,   4, 0x04),
+       RF_REG_PAIR(5,   5, 0x20),
+       RF_REG_PAIR(5,   6, 0x3a),
+       RF_REG_PAIR(5,   7, 0x3a),
+       RF_REG_PAIR(5,   8, 0x00),
+       RF_REG_PAIR(5,   9, 0x00),
+       RF_REG_PAIR(5,  10, 0x10),
+       RF_REG_PAIR(5,  11, 0x10),
+       RF_REG_PAIR(5,  12, 0x10),
+       RF_REG_PAIR(5,  13, 0x10),
+       RF_REG_PAIR(5,  14, 0x10),
+       RF_REG_PAIR(5,  15, 0x20),
+       RF_REG_PAIR(5,  16, 0x22),
+       RF_REG_PAIR(5,  17, 0x7c),
+       RF_REG_PAIR(5,  18, 0x00),
+       RF_REG_PAIR(5,  19, 0x00),
+       RF_REG_PAIR(5,  20, 0x00),
+       RF_REG_PAIR(5,  21, 0xf1),
+       RF_REG_PAIR(5,  22, 0x11),
+       RF_REG_PAIR(5,  23, 0x02),
+       RF_REG_PAIR(5,  24, 0x41),
+       RF_REG_PAIR(5,  25, 0x20),
+       RF_REG_PAIR(5,  26, 0x00),
+       RF_REG_PAIR(5,  27, 0xd7),
+       RF_REG_PAIR(5,  28, 0xa2),
+       RF_REG_PAIR(5,  29, 0x20),
+       RF_REG_PAIR(5,  30, 0x49),
+       RF_REG_PAIR(5,  31, 0x20),
+       RF_REG_PAIR(5,  32, 0x04),
+       RF_REG_PAIR(5,  33, 0xf1),
+       RF_REG_PAIR(5,  34, 0xa1),
+       RF_REG_PAIR(5,  35, 0x01),
+       RF_REG_PAIR(5,  41, 0x00),
+       RF_REG_PAIR(5,  42, 0x00),
+       RF_REG_PAIR(5,  43, 0x00),
+       RF_REG_PAIR(5,  44, 0x00),
+       RF_REG_PAIR(5,  45, 0x00),
+       RF_REG_PAIR(5,  46, 0x00),
+       RF_REG_PAIR(5,  47, 0x00),
+       RF_REG_PAIR(5,  48, 0x00),
+       RF_REG_PAIR(5,  49, 0x00),
+       RF_REG_PAIR(5,  50, 0x00),
+       RF_REG_PAIR(5,  51, 0x00),
+       RF_REG_PAIR(5,  52, 0x00),
+       RF_REG_PAIR(5,  53, 0x00),
+       RF_REG_PAIR(5,  54, 0x00),
+       RF_REG_PAIR(5,  55, 0x00),
+       RF_REG_PAIR(5,  56, 0x00),
+       RF_REG_PAIR(5,  57, 0x00),
+       RF_REG_PAIR(5,  58, 0x31),
+       RF_REG_PAIR(5,  59, 0x31),
+       RF_REG_PAIR(5,  60, 0x0a),
+       RF_REG_PAIR(5,  61, 0x02),
+       RF_REG_PAIR(5,  62, 0x00),
+       RF_REG_PAIR(5,  63, 0x00),
+};
+
+/* TODO: BBP178 is set to 0xff for "CCK CH14 OBW" which overrides the settings
+ *      from channel switching. Seems stupid at best.
+ */
+static const struct mt76_reg_pair bbp_high_temp[] = {
+       {  75, 0x60 },
+       {  92, 0x02 },
+       { 178, 0xff }, /* For CCK CH14 OBW */
+       { 195, 0x88 }, { 196, 0x60 },
+}, bbp_high_temp_bw20[] = {
+       {  69, 0x12 },
+       {  91, 0x07 },
+       { 195, 0x23 }, { 196, 0x17 },
+       { 195, 0x24 }, { 196, 0x06 },
+       { 195, 0x81 }, { 196, 0x12 },
+       { 195, 0x83 }, { 196, 0x17 },
+}, bbp_high_temp_bw40[] = {
+       {  69, 0x15 },
+       {  91, 0x04 },
+       { 195, 0x23 }, { 196, 0x12 },
+       { 195, 0x24 }, { 196, 0x08 },
+       { 195, 0x81 }, { 196, 0x15 },
+       { 195, 0x83 }, { 196, 0x16 },
+}, bbp_low_temp[] = {
+       { 178, 0xff }, /* For CCK CH14 OBW */
+}, bbp_low_temp_bw20[] = {
+       {  69, 0x12 },
+       {  75, 0x5e },
+       {  91, 0x07 },
+       {  92, 0x02 },
+       { 195, 0x23 }, { 196, 0x17 },
+       { 195, 0x24 }, { 196, 0x06 },
+       { 195, 0x81 }, { 196, 0x12 },
+       { 195, 0x83 }, { 196, 0x17 },
+       { 195, 0x88 }, { 196, 0x5e },
+}, bbp_low_temp_bw40[] = {
+       {  69, 0x15 },
+       {  75, 0x5c },
+       {  91, 0x04 },
+       {  92, 0x03 },
+       { 195, 0x23 }, { 196, 0x10 },
+       { 195, 0x24 }, { 196, 0x08 },
+       { 195, 0x81 }, { 196, 0x15 },
+       { 195, 0x83 }, { 196, 0x16 },
+       { 195, 0x88 }, { 196, 0x5b },
+}, bbp_normal_temp[] = {
+       {  75, 0x60 },
+       {  92, 0x02 },
+       { 178, 0xff }, /* For CCK CH14 OBW */
+       { 195, 0x88 }, { 196, 0x60 },
+}, bbp_normal_temp_bw20[] = {
+       {  69, 0x12 },
+       {  91, 0x07 },
+       { 195, 0x23 }, { 196, 0x17 },
+       { 195, 0x24 }, { 196, 0x06 },
+       { 195, 0x81 }, { 196, 0x12 },
+       { 195, 0x83 }, { 196, 0x17 },
+}, bbp_normal_temp_bw40[] = {
+       {  69, 0x15 },
+       {  91, 0x04 },
+       { 195, 0x23 }, { 196, 0x12 },
+       { 195, 0x24 }, { 196, 0x08 },
+       { 195, 0x81 }, { 196, 0x15 },
+       { 195, 0x83 }, { 196, 0x16 },
+};
+
+#define BBP_TABLE(arr) { arr, ARRAY_SIZE(arr), }
+
+static const struct reg_table {
+       const struct mt76_reg_pair *regs;
+       size_t n;
+} bbp_mode_table[3][3] = {
+       {
+               BBP_TABLE(bbp_normal_temp_bw20),
+               BBP_TABLE(bbp_normal_temp_bw40),
+               BBP_TABLE(bbp_normal_temp),
+       }, {
+               BBP_TABLE(bbp_high_temp_bw20),
+               BBP_TABLE(bbp_high_temp_bw40),
+               BBP_TABLE(bbp_high_temp),
+       }, {
+               BBP_TABLE(bbp_low_temp_bw20),
+               BBP_TABLE(bbp_low_temp_bw40),
+               BBP_TABLE(bbp_low_temp),
+       }
+};
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
new file mode 100644 (file)
index 0000000..c161bcc
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "trace.h"
+#include <linux/etherdevice.h>
+
+static void
+mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate)
+{
+       u8 idx = MT76_GET(MT_TXWI_RATE_MCS, rate);
+
+       txrate->idx = 0;
+       txrate->flags = 0;
+       txrate->count = 1;
+
+       switch (MT76_GET(MT_TXWI_RATE_PHY_MODE, rate)) {
+       case MT_PHY_TYPE_OFDM:
+               txrate->idx = idx + 4;
+               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;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       if (MT76_GET(MT_TXWI_RATE_BW, rate) == MT_PHY_BW_40)
+               txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+       if (rate & MT_TXWI_RATE_SGI)
+               txrate->flags |= IEEE80211_TX_RC_SHORT_GI;
+}
+
+static void
+mt76_mac_fill_tx_status(struct mt7601u_dev *dev, struct ieee80211_tx_info *info,
+                       struct mt76_tx_status *st)
+{
+       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);
+       mt76_mac_process_tx_rate(&rate[last_rate], st->rate);
+       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 = 1;
+       info->status.ampdu_ack_len = st->success;
+
+       if (st->is_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;
+}
+
+u16 mt76_mac_tx_rate_val(struct mt7601u_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_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->chandef.chan->band;
+               u16 val;
+
+               r = &dev->hw->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 = MT76_SET(MT_RXWI_RATE_MCS, rate_idx);
+       rateval |= MT76_SET(MT_RXWI_RATE_PHY, phy);
+       rateval |= MT76_SET(MT_RXWI_RATE_BW, bw);
+       if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+               rateval |= MT_RXWI_RATE_SGI;
+
+       *nss_val = nss;
+       return rateval;
+}
+
+void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid,
+                           const struct ieee80211_tx_rate *rate)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       wcid->tx_rate = mt76_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss);
+       wcid->tx_rate_set = true;
+       spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+struct mt76_tx_status mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev)
+{
+       struct mt76_tx_status stat = {};
+       u32 val;
+
+       val = mt7601u_rr(dev, MT_TX_STAT_FIFO);
+       stat.valid = !!(val & MT_TX_STAT_FIFO_VALID);
+       stat.success = !!(val & MT_TX_STAT_FIFO_SUCCESS);
+       stat.aggr = !!(val & MT_TX_STAT_FIFO_AGGR);
+       stat.ack_req = !!(val & MT_TX_STAT_FIFO_ACKREQ);
+       stat.pktid = MT76_GET(MT_TX_STAT_FIFO_PID_TYPE, val);
+       stat.wcid = MT76_GET(MT_TX_STAT_FIFO_WCID, val);
+       stat.rate = MT76_GET(MT_TX_STAT_FIFO_RATE, val);
+
+       return stat;
+}
+
+void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
+{
+       struct ieee80211_tx_info info = {};
+       struct ieee80211_sta *sta = NULL;
+       struct mt76_wcid *wcid = NULL;
+       void *msta;
+
+       rcu_read_lock();
+       if (stat->wcid < ARRAY_SIZE(dev->wcid))
+               wcid = rcu_dereference(dev->wcid[stat->wcid]);
+
+       if (wcid) {
+               msta = container_of(wcid, struct mt76_sta, wcid);
+               sta = container_of(msta, struct ieee80211_sta,
+                                  drv_priv);
+       }
+
+       mt76_mac_fill_tx_status(dev, &info, stat);
+       ieee80211_tx_status_noskb(dev->hw, sta, &info);
+       rcu_read_unlock();
+}
+
+void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot,
+                               int ht_mode)
+{
+       int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+       bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+       u32 prot[6];
+       bool ht_rts[4] = {};
+       int i;
+
+       prot[0] = MT_PROT_NAV_SHORT |
+                 MT_PROT_TXOP_ALLOW_ALL |
+                 MT_PROT_RTS_THR_EN;
+       prot[1] = prot[0];
+       if (legacy_prot)
+               prot[1] |= MT_PROT_CTRL_CTS2SELF;
+
+       prot[2] = prot[4] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_BW20;
+       prot[3] = prot[5] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_ALL;
+
+       if (legacy_prot) {
+               prot[2] |= MT_PROT_RATE_CCK_11;
+               prot[3] |= MT_PROT_RATE_CCK_11;
+               prot[4] |= MT_PROT_RATE_CCK_11;
+               prot[5] |= MT_PROT_RATE_CCK_11;
+       } else {
+               prot[2] |= MT_PROT_RATE_OFDM_24;
+               prot[3] |= MT_PROT_RATE_DUP_OFDM_24;
+               prot[4] |= MT_PROT_RATE_OFDM_24;
+               prot[5] |= MT_PROT_RATE_DUP_OFDM_24;
+       }
+
+       switch (mode) {
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
+               break;
+
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+               ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
+               break;
+
+       case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+               ht_rts[1] = ht_rts[3] = true;
+               break;
+
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+               ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
+               break;
+       }
+
+       if (non_gf)
+               ht_rts[2] = ht_rts[3] = true;
+
+       for (i = 0; i < 4; i++)
+               if (ht_rts[i])
+                       prot[i + 2] |= MT_PROT_CTRL_RTS_CTS;
+
+       for (i = 0; i < 6; i++)
+               mt7601u_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]);
+}
+
+void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb)
+{
+       if (short_preamb)
+               mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
+       else
+               mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
+}
+
+void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval)
+{
+       u32 val = mt7601u_rr(dev, MT_BEACON_TIME_CFG);
+
+       val &= ~(MT_BEACON_TIME_CFG_TIMER_EN |
+                MT_BEACON_TIME_CFG_SYNC_MODE |
+                MT_BEACON_TIME_CFG_TBTT_EN);
+
+       if (!enable) {
+               mt7601u_wr(dev, MT_BEACON_TIME_CFG, val);
+               return;
+       }
+
+       val &= ~MT_BEACON_TIME_CFG_INTVAL;
+       val |= MT76_SET(MT_BEACON_TIME_CFG_INTVAL, interval << 4) |
+               MT_BEACON_TIME_CFG_TIMER_EN |
+               MT_BEACON_TIME_CFG_SYNC_MODE |
+               MT_BEACON_TIME_CFG_TBTT_EN;
+}
+
+static void mt7601u_check_mac_err(struct mt7601u_dev *dev)
+{
+       u32 val = mt7601u_rr(dev, 0x10f4);
+
+       if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5))))
+               return;
+
+       dev_err(dev->dev, "Error: MAC specific condition occurred\n");
+
+       mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR);
+       udelay(10);
+       mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR);
+}
+
+void mt7601u_mac_work(struct work_struct *work)
+{
+       struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev,
+                                              mac_work.work);
+       struct {
+               u32 addr_base;
+               u32 span;
+               u64 *stat_base;
+       } spans[] = {
+               { MT_RX_STA_CNT0,       3,      dev->stats.rx_stat },
+               { MT_TX_STA_CNT0,       3,      dev->stats.tx_stat },
+               { MT_TX_AGG_STAT,       1,      dev->stats.aggr_stat },
+               { MT_MPDU_DENSITY_CNT,  1,      dev->stats.zero_len_del },
+               { MT_TX_AGG_CNT_BASE0,  8,      &dev->stats.aggr_n[0] },
+               { MT_TX_AGG_CNT_BASE1,  8,      &dev->stats.aggr_n[16] },
+       };
+       u32 sum, n;
+       int i, j, k;
+
+       /* Note: using MCU_RANDOM_READ is actually slower then reading all the
+        *       registers by hand.  MCU takes ca. 20ms to complete read of 24
+        *       registers while reading them one by one will takes roughly
+        *       24*200us =~ 5ms.
+        */
+
+       k = 0;
+       n = 0;
+       sum = 0;
+       for (i = 0; i < ARRAY_SIZE(spans); i++)
+               for (j = 0; j < spans[i].span; j++) {
+                       u32 val = mt7601u_rr(dev, spans[i].addr_base + j * 4);
+
+                       spans[i].stat_base[j * 2] += val & 0xffff;
+                       spans[i].stat_base[j * 2 + 1] += val >> 16;
+
+                       /* Calculate average AMPDU length */
+                       if (spans[i].addr_base != MT_TX_AGG_CNT_BASE0 &&
+                           spans[i].addr_base != MT_TX_AGG_CNT_BASE1)
+                               continue;
+
+                       n += (val >> 16) + (val & 0xffff);
+                       sum += (val & 0xffff) * (1 + k * 2) +
+                               (val >> 16) * (2 + k * 2);
+                       k++;
+               }
+
+       atomic_set(&dev->avg_ampdu_len, n ? DIV_ROUND_CLOSEST(sum, n) : 1);
+
+       mt7601u_check_mac_err(dev);
+
+       ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, 10 * HZ);
+}
+
+void
+mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac)
+{
+       u8 zmac[ETH_ALEN] = {};
+       u32 attr;
+
+       attr = MT76_SET(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) |
+              MT76_SET(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8));
+
+       mt76_wr(dev, MT_WCID_ATTR(idx), attr);
+
+       if (mac)
+               memcpy(zmac, mac, sizeof(zmac));
+
+       mt7601u_addr_wr(dev, MT_WCID_ADDR(idx), zmac);
+}
+
+void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev)
+{
+       struct ieee80211_sta *sta;
+       struct mt76_wcid *wcid;
+       void *msta;
+       u8 min_factor = 3;
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) {
+               wcid = rcu_dereference(dev->wcid[i]);
+               if (!wcid)
+                       continue;
+
+               msta = container_of(wcid, struct mt76_sta, wcid);
+               sta = container_of(msta, struct ieee80211_sta, drv_priv);
+
+               min_factor = min(min_factor, sta->ht_cap.ampdu_factor);
+       }
+       rcu_read_unlock();
+
+       mt7601u_wr(dev, MT_MAX_LEN_CFG, 0xa0fff |
+                  MT76_SET(MT_MAX_LEN_CFG_AMPDU, min_factor));
+}
+
+static void
+mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
+{
+       u8 idx = MT76_GET(MT_RXWI_RATE_MCS, rate);
+
+       switch (MT76_GET(MT_RXWI_RATE_PHY, rate)) {
+       case MT_PHY_TYPE_OFDM:
+               if (WARN_ON(idx >= 8))
+                       idx = 0;
+               idx += 4;
+
+               status->rate_idx = idx;
+               return;
+       case MT_PHY_TYPE_CCK:
+               if (idx >= 8) {
+                       idx -= 8;
+                       status->flag |= RX_FLAG_SHORTPRE;
+               }
+
+               if (WARN_ON(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;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       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;
+
+       if (rate & MT_RXWI_RATE_BW)
+               status->flag |= RX_FLAG_40MHZ;
+}
+
+static void
+mt7601u_rx_monitor_beacon(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi,
+                         u16 rate, int rssi)
+{
+       dev->bcn_freq_off = rxwi->freq_off;
+       dev->bcn_phy_mode = MT76_GET(MT_RXWI_RATE_PHY, rate);
+       dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
+}
+
+static int
+mt7601u_rx_is_our_beacon(struct mt7601u_dev *dev, u8 *data)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
+
+       return ieee80211_is_beacon(hdr->frame_control) &&
+               ether_addr_equal(hdr->addr2, dev->ap_bssid);
+}
+
+u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb,
+                       u8 *data, void *rxi)
+{
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct mt7601u_rxwi *rxwi = rxi;
+       u32 ctl = le32_to_cpu(rxwi->ctl);
+       u16 rate = le16_to_cpu(rxwi->rate);
+       int rssi;
+
+       if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
+               status->flag |= RX_FLAG_DECRYPTED;
+               status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
+       }
+
+       status->chains = BIT(0);
+       rssi = mt7601u_phy_get_rssi(dev, rxwi, rate);
+       status->chain_signal[0] = status->signal = rssi;
+       status->freq = dev->chandef.chan->center_freq;
+       status->band = dev->chandef.chan->band;
+
+       mt76_mac_process_rate(status, rate);
+
+       spin_lock_bh(&dev->con_mon_lock);
+       if (mt7601u_rx_is_our_beacon(dev, data))
+               mt7601u_rx_monitor_beacon(dev, rxwi, rate, rssi);
+       else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M))
+               dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
+       spin_unlock_bh(&dev->con_mon_lock);
+
+       return MT76_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
+}
+
+static enum mt76_cipher_type
+mt76_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;
+       }
+}
+
+int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx,
+                         struct ieee80211_key_conf *key)
+{
+       enum mt76_cipher_type cipher;
+       u8 key_data[32];
+       u8 iv_data[8];
+       u32 val;
+
+       cipher = mt76_mac_get_key_info(key, key_data);
+       if (cipher == MT_CIPHER_NONE && key)
+               return -EINVAL;
+
+       trace_set_key(dev, idx);
+
+       mt7601u_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data));
+
+       memset(iv_data, 0, sizeof(iv_data));
+       if (key) {
+               iv_data[3] = key->keyidx << 6;
+               if (cipher >= MT_CIPHER_TKIP) {
+                       /* Note: start with 1 to comply with spec,
+                        *       (see comment on common/cmm_wpa.c:4291).
+                        */
+                       iv_data[0] |= 1;
+                       iv_data[3] |= 0x20;
+               }
+       }
+       mt7601u_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data));
+
+       val = mt7601u_rr(dev, MT_WCID_ATTR(idx));
+       val &= ~MT_WCID_ATTR_PKEY_MODE & ~MT_WCID_ATTR_PKEY_MODE_EXT;
+       val |= MT76_SET(MT_WCID_ATTR_PKEY_MODE, cipher & 7) |
+              MT76_SET(MT_WCID_ATTR_PKEY_MODE_EXT, cipher >> 3);
+       val &= ~MT_WCID_ATTR_PAIRWISE;
+       val |= MT_WCID_ATTR_PAIRWISE *
+               !!(key && key->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+       mt7601u_wr(dev, MT_WCID_ATTR(idx), val);
+
+       return 0;
+}
+
+int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx,
+                             struct ieee80211_key_conf *key)
+{
+       enum mt76_cipher_type cipher;
+       u8 key_data[32];
+       u32 val;
+
+       cipher = mt76_mac_get_key_info(key, key_data);
+       if (cipher == MT_CIPHER_NONE && key)
+               return -EINVAL;
+
+       trace_set_shared_key(dev, vif_idx, key_idx);
+
+       mt7601u_wr_copy(dev, MT_SKEY(vif_idx, key_idx),
+                       key_data, sizeof(key_data));
+
+       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);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.h b/drivers/net/wireless/mediatek/mt7601u/mac.h
new file mode 100644 (file)
index 0000000..2c22d63
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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_MAC_H
+#define __MT76_MAC_H
+
+struct mt76_tx_status {
+       u8 valid:1;
+       u8 success:1;
+       u8 aggr:1;
+       u8 ack_req:1;
+       u8 is_probe:1;
+       u8 wcid;
+       u8 pktid;
+       u8 retry;
+       u16 rate;
+} __packed __aligned(2);
+
+/* Note: values in original "RSSI" and "SNR" fields are not actually what they
+ *      are called for MT7601U, names used by this driver are educated guesses
+ *      (see vendor mac/ral_omac.c).
+ */
+struct mt7601u_rxwi {
+       __le32 rxinfo;
+
+       __le32 ctl;
+
+       __le16 frag_sn;
+       __le16 rate;
+
+       u8 unknown;
+       u8 zero[3];
+
+       u8 snr;
+       u8 ant;
+       u8 gain;
+       u8 freq_off;
+
+       __le32 resv2;
+       __le32 expert_ant;
+} __packed __aligned(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_U2M                  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_PKT_80211         BIT(22)
+#define MT_RXINFO_TCP_SUM_BYPASS       BIT(28)
+#define MT_RXINFO_IP_SUM_BYPASS                BIT(29)
+#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(27, 16)
+#define MT_RXWI_CTL_TID                        GENMASK(31, 28)
+
+#define MT_RXWI_FRAG                   GENMASK(3, 0)
+#define MT_RXWI_SN                     GENMASK(15, 4)
+
+#define MT_RXWI_RATE_MCS               GENMASK(6, 0)
+#define MT_RXWI_RATE_BW                        BIT(7)
+#define MT_RXWI_RATE_SGI               BIT(8)
+#define MT_RXWI_RATE_STBC              GENMASK(10, 9)
+#define MT_RXWI_RATE_ETXBF             BIT(11)
+#define MT_RXWI_RATE_SND               BIT(12)
+#define MT_RXWI_RATE_ITXBF             BIT(13)
+#define MT_RXWI_RATE_PHY               GENMASK(15, 14)
+
+#define MT_RXWI_GAIN_RSSI_VAL          GENMASK(5, 0)
+#define MT_RXWI_GAIN_RSSI_LNA_ID       GENMASK(7, 6)
+#define MT_RXWI_ANT_AUX_LNA            BIT(7)
+
+#define MT_RXWI_EANT_ENC_ANT_ID                GENMASK(7, 0)
+
+enum mt76_phy_type {
+       MT_PHY_TYPE_CCK,
+       MT_PHY_TYPE_OFDM,
+       MT_PHY_TYPE_HT,
+       MT_PHY_TYPE_HT_GF,
+};
+
+enum mt76_phy_bandwidth {
+       MT_PHY_BW_20,
+       MT_PHY_BW_40,
+};
+
+struct mt76_txwi {
+       __le16 flags;
+       __le16 rate_ctl;
+
+       u8 ack_ctl;
+       u8 wcid;
+       __le16 len_ctl;
+
+       __le32 iv;
+
+       __le32 eiv;
+
+       u8 aid;
+       u8 txstream;
+       __le16 ctl;
+} __packed __aligned(4);
+
+#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_CWMIN            GENMASK(12, 10)
+#define MT_TXWI_FLAGS_NO_RATE_FALLBACK BIT(13)
+#define MT_TXWI_FLAGS_TX_RPT           BIT(14)
+#define MT_TXWI_FLAGS_TX_RATE_LUT      BIT(15)
+
+#define MT_TXWI_RATE_MCS               GENMASK(6, 0)
+#define MT_TXWI_RATE_BW                        BIT(7)
+#define MT_TXWI_RATE_SGI               BIT(8)
+#define MT_TXWI_RATE_STBC              GENMASK(10, 9)
+#define MT_TXWI_RATE_PHY_MODE          GENMASK(15, 14)
+
+#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_LEN_BYTE_CNT           GENMASK(11, 0)
+#define MT_TXWI_LEN_PKTID              GENMASK(15, 12)
+
+#define MT_TXWI_CTL_TX_POWER_ADJ       GENMASK(3, 0)
+#define MT_TXWI_CTL_CHAN_CHECK_PKT     BIT(4)
+#define MT_TXWI_CTL_PIFS_REV           BIT(6)
+
+u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb,
+                       u8 *data, void *rxi);
+int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx,
+                         struct ieee80211_key_conf *key);
+void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid,
+                           const struct ieee80211_tx_rate *rate);
+
+int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx,
+                             struct ieee80211_key_conf *key);
+u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev,
+                        const struct ieee80211_tx_rate *rate, u8 *nss_val);
+struct mt76_tx_status
+mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev);
+void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c
new file mode 100644 (file)
index 0000000..ced82ab
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "mac.h"
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+
+static int mt7601u_start(struct ieee80211_hw *hw)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       int ret;
+
+       mutex_lock(&dev->mutex);
+
+       ret = mt7601u_mac_start(dev);
+       if (ret)
+               goto out;
+
+       ieee80211_queue_delayed_work(dev->hw, &dev->mac_work,
+                                    MT_CALIBRATE_INTERVAL);
+       ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
+                                    MT_CALIBRATE_INTERVAL);
+out:
+       mutex_unlock(&dev->mutex);
+       return ret;
+}
+
+static void mt7601u_stop(struct ieee80211_hw *hw)
+{
+       struct mt7601u_dev *dev = hw->priv;
+
+       mutex_lock(&dev->mutex);
+
+       cancel_delayed_work_sync(&dev->cal_work);
+       cancel_delayed_work_sync(&dev->mac_work);
+       mt7601u_mac_stop(dev);
+
+       mutex_unlock(&dev->mutex);
+}
+
+static int mt7601u_add_interface(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
+       unsigned int idx = 0;
+       unsigned int wcid = GROUP_WCID(idx);
+
+       /* Note: for AP do the AP-STA things mt76 does:
+        *      - beacon offsets
+        *      - do mac address tricks
+        *      - shift vif idx
+        */
+       mvif->idx = idx;
+
+       if (dev->wcid_mask[wcid / BITS_PER_LONG] & BIT(wcid % BITS_PER_LONG))
+               return -ENOSPC;
+       dev->wcid_mask[wcid / BITS_PER_LONG] |= BIT(wcid % BITS_PER_LONG);
+       mvif->group_wcid.idx = wcid;
+       mvif->group_wcid.hw_key_idx = -1;
+
+       return 0;
+}
+
+static void mt7601u_remove_interface(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
+       unsigned int wcid = mvif->group_wcid.idx;
+
+       dev->wcid_mask[wcid / BITS_PER_LONG] &= ~BIT(wcid % BITS_PER_LONG);
+}
+
+static int mt7601u_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       int ret = 0;
+
+       mutex_lock(&dev->mutex);
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ieee80211_stop_queues(hw);
+               ret = mt7601u_phy_set_channel(dev, &hw->conf.chandef);
+               ieee80211_wake_queues(hw);
+       }
+
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+
+static void
+mt76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
+                     unsigned int *total_flags, u64 multicast)
+{
+       struct mt7601u_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
+mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_bss_conf *info, u32 changed)
+{
+       struct mt7601u_dev *dev = hw->priv;
+
+       mutex_lock(&dev->mutex);
+
+       if (changed & BSS_CHANGED_ASSOC)
+               mt7601u_phy_con_cal_onoff(dev, info);
+
+       if (changed & BSS_CHANGED_BSSID) {
+               mt7601u_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid);
+
+               /* Note: this is a hack because beacon_int is not changed
+                *       on leave nor is any more appropriate event generated.
+                *       rt2x00 doesn't seem to be bothered though.
+                */
+               if (is_zero_ether_addr(info->bssid))
+                       mt7601u_mac_config_tsf(dev, false, 0);
+       }
+
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               mt7601u_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates);
+               mt7601u_wr(dev, MT_HT_FBK_CFG0, 0x65432100);
+               mt7601u_wr(dev, MT_HT_FBK_CFG1, 0xedcba980);
+               mt7601u_wr(dev, MT_LG_FBK_CFG0, 0xedcba988);
+               mt7601u_wr(dev, MT_LG_FBK_CFG1, 0x00002100);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT)
+               mt7601u_mac_config_tsf(dev, true, info->beacon_int);
+
+       if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+               mt7601u_mac_set_protection(dev, info->use_cts_prot,
+                                          info->ht_operation_mode);
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE)
+               mt7601u_mac_set_short_preamble(dev, info->use_short_preamble);
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               int slottime = info->use_short_slot ? 9 : 20;
+
+               mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
+                              MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
+       }
+
+       if (changed & BSS_CHANGED_ASSOC)
+               mt7601u_phy_recalibrate_after_assoc(dev);
+
+       mutex_unlock(&dev->mutex);
+}
+
+static int
+mt76_wcid_alloc(struct mt7601u_dev *dev)
+{
+       int i, idx = 0;
+
+       for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) {
+               idx = ffs(~dev->wcid_mask[i]);
+               if (!idx)
+                       continue;
+
+               idx--;
+               dev->wcid_mask[i] |= BIT(idx);
+               break;
+       }
+
+       idx = i * BITS_PER_LONG + idx;
+       if (idx > 119)
+               return -1;
+
+       return idx;
+}
+
+static int
+mt7601u_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+               struct ieee80211_sta *sta)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
+       struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
+       int ret = 0;
+       int idx = 0;
+
+       mutex_lock(&dev->mutex);
+
+       idx = mt76_wcid_alloc(dev);
+       if (idx < 0) {
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       msta->wcid.idx = idx;
+       msta->wcid.hw_key_idx = -1;
+       mt7601u_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
+       mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+       rcu_assign_pointer(dev->wcid[idx], &msta->wcid);
+       mt7601u_mac_set_ampdu_factor(dev);
+
+out:
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+
+static int
+mt7601u_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
+       int idx = msta->wcid.idx;
+
+       mutex_lock(&dev->mutex);
+       rcu_assign_pointer(dev->wcid[idx], NULL);
+       mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
+       dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG);
+       mt7601u_mac_wcid_setup(dev, idx, 0, NULL);
+       mt7601u_mac_set_ampdu_factor(dev);
+       mutex_unlock(&dev->mutex);
+
+       return 0;
+}
+
+static void
+mt7601u_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+}
+
+static void
+mt7601u_sw_scan(struct ieee80211_hw *hw,
+               struct ieee80211_vif *vif,
+               const u8 *mac_addr)
+{
+       struct mt7601u_dev *dev = hw->priv;
+
+       mt7601u_agc_save(dev);
+       set_bit(MT7601U_STATE_SCANNING, &dev->state);
+}
+
+static void
+mt7601u_sw_scan_complete(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif)
+{
+       struct mt7601u_dev *dev = hw->priv;
+
+       mt7601u_agc_restore(dev);
+       clear_bit(MT7601U_STATE_SCANNING, &dev->state);
+}
+
+static int
+mt7601u_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 mt7601u_dev *dev = hw->priv;
+       struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
+       struct mt76_sta *msta = sta ? (struct mt76_sta *) sta->drv_priv : NULL;
+       struct mt76_wcid *wcid = msta ? &msta->wcid : &mvif->group_wcid;
+       int idx = key->keyidx;
+       int ret;
+
+       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 = mt76_mac_wcid_set_key(dev, wcid->idx, key);
+                       if (ret)
+                               return ret;
+               }
+
+               return mt76_mac_shared_key_setup(dev, mvif->idx, idx, key);
+       }
+
+       return mt76_mac_wcid_set_key(dev, msta->wcid.idx, key);
+}
+
+static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct mt7601u_dev *dev = hw->priv;
+
+       mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value);
+
+       return 0;
+}
+
+static int
+mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 enum ieee80211_ampdu_mlme_action action,
+                 struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
+
+       WARN_ON(msta->wcid.idx > GROUP_WCID(0));
+
+       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:
+               ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               msta->agg_ssn[tid] = *ssn << 4;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       }
+
+       return 0;
+}
+
+static void
+mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
+       struct ieee80211_sta_rates *rates;
+       struct ieee80211_tx_rate rate = {};
+
+       rcu_read_lock();
+       rates = rcu_dereference(sta->rates);
+
+       if (!rates)
+               goto out;
+
+       rate.idx = rates->rate[0].idx;
+       rate.flags = rates->rate[0].flags;
+       mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate);
+
+out:
+       rcu_read_unlock();
+}
+
+const struct ieee80211_ops mt7601u_ops = {
+       .tx = mt7601u_tx,
+       .start = mt7601u_start,
+       .stop = mt7601u_stop,
+       .add_interface = mt7601u_add_interface,
+       .remove_interface = mt7601u_remove_interface,
+       .config = mt7601u_config,
+       .configure_filter = mt76_configure_filter,
+       .bss_info_changed = mt7601u_bss_info_changed,
+       .sta_add = mt7601u_sta_add,
+       .sta_remove = mt7601u_sta_remove,
+       .sta_notify = mt7601u_sta_notify,
+       .set_key = mt7601u_set_key,
+       .conf_tx = mt7601u_conf_tx,
+       .sw_scan_start = mt7601u_sw_scan,
+       .sw_scan_complete = mt7601u_sw_scan_complete,
+       .ampdu_action = mt76_ampdu_action,
+       .sta_rate_tbl_update = mt76_sta_rate_tbl_update,
+       .set_rts_threshold = mt7601u_set_rts_threshold,
+};
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
new file mode 100644 (file)
index 0000000..fbb1986
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * (c) Copyright 2002-2010, Ralink Technology, Inc.
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/skbuff.h>
+
+#include "mt7601u.h"
+#include "dma.h"
+#include "mcu.h"
+#include "usb.h"
+#include "trace.h"
+
+#define MCU_FW_URB_MAX_PAYLOAD         0x3800
+#define MCU_FW_URB_SIZE                        (MCU_FW_URB_MAX_PAYLOAD + 12)
+#define MCU_RESP_URB_SIZE              1024
+
+static inline int firmware_running(struct mt7601u_dev *dev)
+{
+       return mt7601u_rr(dev, MT_MCU_COM_REG0) == 1;
+}
+
+static inline void skb_put_le32(struct sk_buff *skb, u32 val)
+{
+       put_unaligned_le32(val, skb_put(skb, 4));
+}
+
+static inline void mt7601u_dma_skb_wrap_cmd(struct sk_buff *skb,
+                                           u8 seq, enum mcu_cmd cmd)
+{
+       WARN_ON(mt7601u_dma_skb_wrap(skb, CPU_TX_PORT, DMA_COMMAND,
+                                    MT76_SET(MT_TXD_CMD_INFO_SEQ, seq) |
+                                    MT76_SET(MT_TXD_CMD_INFO_TYPE, cmd)));
+}
+
+static inline void trace_mt_mcu_msg_send_cs(struct mt7601u_dev *dev,
+                                           struct sk_buff *skb, bool need_resp)
+{
+       u32 i, csum = 0;
+
+       for (i = 0; i < skb->len / 4; i++)
+               csum ^= get_unaligned_le32(skb->data + i * 4);
+
+       trace_mt_mcu_msg_send(dev, skb, csum, need_resp);
+}
+
+static struct sk_buff *
+mt7601u_mcu_msg_alloc(struct mt7601u_dev *dev, const void *data, int len)
+{
+       struct sk_buff *skb;
+
+       WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */
+
+       skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
+       skb_reserve(skb, MT_DMA_HDR_LEN);
+       memcpy(skb_put(skb, len), data, len);
+
+       return skb;
+}
+
+static int mt7601u_mcu_wait_resp(struct mt7601u_dev *dev, u8 seq)
+{
+       struct urb *urb = dev->mcu.resp.urb;
+       u32 rxfce;
+       int urb_status, ret, i = 5;
+
+       while (i--) {
+               if (!wait_for_completion_timeout(&dev->mcu.resp_cmpl,
+                                                msecs_to_jiffies(300))) {
+                       dev_warn(dev->dev, "Warning: %s retrying\n", __func__);
+                       continue;
+               }
+
+               /* Make copies of important data before reusing the urb */
+               rxfce = get_unaligned_le32(dev->mcu.resp.buf);
+               urb_status = urb->status * mt7601u_urb_has_error(urb);
+
+               ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP,
+                                            &dev->mcu.resp, GFP_KERNEL,
+                                            mt7601u_complete_urb,
+                                            &dev->mcu.resp_cmpl);
+               if (ret)
+                       return ret;
+
+               if (urb_status)
+                       dev_err(dev->dev, "Error: MCU resp urb failed:%d\n",
+                               urb_status);
+
+               if (MT76_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce) == seq &&
+                   MT76_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce) == CMD_DONE)
+                       return 0;
+
+               dev_err(dev->dev, "Error: MCU resp evt:%hhx seq:%hhx-%hhx!\n",
+                       MT76_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce),
+                       seq, MT76_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce));
+       }
+
+       dev_err(dev->dev, "Error: %s timed out\n", __func__);
+       return -ETIMEDOUT;
+}
+
+static int
+mt7601u_mcu_msg_send(struct mt7601u_dev *dev, struct sk_buff *skb,
+                    enum mcu_cmd cmd, bool wait_resp)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+       unsigned cmd_pipe = usb_sndbulkpipe(usb_dev,
+                                           dev->out_eps[MT_EP_OUT_INBAND_CMD]);
+       int sent, ret;
+       u8 seq = 0;
+
+       if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return 0;
+
+       mutex_lock(&dev->mcu.mutex);
+
+       if (wait_resp)
+               while (!seq)
+                       seq = ++dev->mcu.msg_seq & 0xf;
+
+       mt7601u_dma_skb_wrap_cmd(skb, seq, cmd);
+
+       if (dev->mcu.resp_cmpl.done)
+               dev_err(dev->dev, "Error: MCU response pre-completed!\n");
+
+       trace_mt_mcu_msg_send_cs(dev, skb, wait_resp);
+       trace_mt_submit_urb_sync(dev, cmd_pipe, skb->len);
+       ret = usb_bulk_msg(usb_dev, cmd_pipe, skb->data, skb->len, &sent, 500);
+       if (ret) {
+               dev_err(dev->dev, "Error: send MCU cmd failed:%d\n", ret);
+               goto out;
+       }
+       if (sent != skb->len)
+               dev_err(dev->dev, "Error: %s sent != skb->len\n", __func__);
+
+       if (wait_resp)
+               ret = mt7601u_mcu_wait_resp(dev, seq);
+out:
+       mutex_unlock(&dev->mcu.mutex);
+
+       consume_skb(skb);
+
+       return ret;
+}
+
+static int mt7601u_mcu_function_select(struct mt7601u_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 = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+       return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5);
+}
+
+int mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga)
+{
+       int ret;
+
+       if (!test_bit(MT7601U_STATE_MCU_RUNNING, &dev->state))
+               return 0;
+
+       ret = mt7601u_mcu_function_select(dev, ATOMIC_TSSI_SETTING,
+                                         use_hvga);
+       if (ret) {
+               dev_warn(dev->dev, "Warning: MCU TSSI read kick failed\n");
+               return ret;
+       }
+
+       dev->tssi_read_trig = true;
+
+       return 0;
+}
+
+int
+mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val)
+{
+       struct sk_buff *skb;
+       struct {
+               __le32 id;
+               __le32 value;
+       } __packed __aligned(4) msg = {
+               .id = cpu_to_le32(cal),
+               .value = cpu_to_le32(val),
+       };
+
+       skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+       return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true);
+}
+
+int mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base,
+                           const struct mt76_reg_pair *data, int n)
+{
+       const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8;
+       struct sk_buff *skb;
+       int cnt, i, ret;
+
+       if (!n)
+               return 0;
+
+       cnt = min(max_vals_per_cmd, n);
+
+       skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, MT_DMA_HDR_LEN);
+
+       for (i = 0; i < cnt; i++) {
+               skb_put_le32(skb, base + data[i].reg);
+               skb_put_le32(skb, data[i].value);
+       }
+
+       ret = mt7601u_mcu_msg_send(dev, skb, CMD_RANDOM_WRITE, cnt == n);
+       if (ret)
+               return ret;
+
+       return mt7601u_write_reg_pairs(dev, base, data + cnt, n - cnt);
+}
+
+int mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset,
+                            const u32 *data, int n)
+{
+       const int max_regs_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1;
+       struct sk_buff *skb;
+       int cnt, i, ret;
+
+       if (!n)
+               return 0;
+
+       cnt = min(max_regs_per_cmd, n);
+
+       skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, MT_DMA_HDR_LEN);
+
+       skb_put_le32(skb, MT_MCU_MEMMAP_WLAN + offset);
+       for (i = 0; i < cnt; i++)
+               skb_put_le32(skb, data[i]);
+
+       ret = mt7601u_mcu_msg_send(dev, skb, CMD_BURST_WRITE, cnt == n);
+       if (ret)
+               return ret;
+
+       return mt7601u_burst_write_regs(dev, offset + cnt * 4,
+                                       data + cnt, n - cnt);
+}
+
+struct mt76_fw_header {
+       __le32 ilm_len;
+       __le32 dlm_len;
+       __le16 build_ver;
+       __le16 fw_ver;
+       u8 pad[4];
+       char build_time[16];
+};
+
+struct mt76_fw {
+       struct mt76_fw_header hdr;
+       u8 ivb[MT_MCU_IVB_SIZE];
+       u8 ilm[];
+};
+
+static int __mt7601u_dma_fw(struct mt7601u_dev *dev,
+                           const struct mt7601u_dma_buf *dma_buf,
+                           const void *data, u32 len, u32 dst_addr)
+{
+       DECLARE_COMPLETION_ONSTACK(cmpl);
+       struct mt7601u_dma_buf buf = *dma_buf; /* we need to fake length */
+       __le32 reg;
+       u32 val;
+       int ret;
+
+       reg = cpu_to_le32(MT76_SET(MT_TXD_INFO_TYPE, DMA_PACKET) |
+                         MT76_SET(MT_TXD_INFO_D_PORT, CPU_TX_PORT) |
+                         MT76_SET(MT_TXD_INFO_LEN, len));
+       memcpy(buf.buf, &reg, sizeof(reg));
+       memcpy(buf.buf + sizeof(reg), data, len);
+       memset(buf.buf + sizeof(reg) + len, 0, 8);
+
+       ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE,
+                                      MT_FCE_DMA_ADDR, dst_addr);
+       if (ret)
+               return ret;
+       len = roundup(len, 4);
+       ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE,
+                                      MT_FCE_DMA_LEN, len << 16);
+       if (ret)
+               return ret;
+
+       buf.len = MT_DMA_HDR_LEN + len + 4;
+       ret = mt7601u_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD,
+                                    &buf, GFP_KERNEL,
+                                    mt7601u_complete_urb, &cmpl);
+       if (ret)
+               return ret;
+
+       if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) {
+               dev_err(dev->dev, "Error: firmware upload timed out\n");
+               usb_kill_urb(buf.urb);
+               return -ETIMEDOUT;
+       }
+       if (mt7601u_urb_has_error(buf.urb)) {
+               dev_err(dev->dev, "Error: firmware upload urb failed:%d\n",
+                       buf.urb->status);
+               return buf.urb->status;
+       }
+
+       val = mt7601u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX);
+       val++;
+       mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val);
+
+       return 0;
+}
+
+static int
+mt7601u_dma_fw(struct mt7601u_dev *dev, struct mt7601u_dma_buf *dma_buf,
+              const void *data, int len, u32 dst_addr)
+{
+       int n, ret;
+
+       if (len == 0)
+               return 0;
+
+       n = min(MCU_FW_URB_MAX_PAYLOAD, len);
+       ret = __mt7601u_dma_fw(dev, dma_buf, data, n, dst_addr);
+       if (ret)
+               return ret;
+
+       if (!mt76_poll_msec(dev, MT_MCU_COM_REG1, BIT(31), BIT(31), 500))
+               return -ETIMEDOUT;
+
+       return mt7601u_dma_fw(dev, dma_buf, data + n, len - n, dst_addr + n);
+}
+
+static int
+mt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw)
+{
+       struct mt7601u_dma_buf dma_buf;
+       void *ivb;
+       u32 ilm_len, dlm_len;
+       int i, ret;
+
+       ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL);
+       if (!ivb || mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ilm_len = le32_to_cpu(fw->hdr.ilm_len) - sizeof(fw->ivb);
+       dev_dbg(dev->dev, "loading FW - ILM %u + IVB %zu\n",
+               ilm_len, sizeof(fw->ivb));
+       ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm, ilm_len, sizeof(fw->ivb));
+       if (ret)
+               goto error;
+
+       dlm_len = le32_to_cpu(fw->hdr.dlm_len);
+       dev_dbg(dev->dev, "loading FW - DLM %u\n", dlm_len);
+       ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm + ilm_len,
+                            dlm_len, MT_MCU_DLM_OFFSET);
+       if (ret)
+               goto error;
+
+       ret = mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT,
+                                    0x12, 0, ivb, sizeof(fw->ivb));
+       if (ret < 0)
+               goto error;
+       ret = 0;
+
+       for (i = 100; i && !firmware_running(dev); i--)
+               msleep(10);
+       if (!i) {
+               ret = -ETIMEDOUT;
+               goto error;
+       }
+
+       dev_dbg(dev->dev, "Firmware running!\n");
+error:
+       kfree(ivb);
+       mt7601u_usb_free_buf(dev, &dma_buf);
+
+       return ret;
+}
+
+static int mt7601u_load_firmware(struct mt7601u_dev *dev)
+{
+       const struct firmware *fw;
+       const struct mt76_fw_header *hdr;
+       int len, ret;
+       u32 val;
+
+       mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN |
+                                        MT_USB_DMA_CFG_TX_BULK_EN));
+
+       if (firmware_running(dev))
+               return 0;
+
+       ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev);
+       if (ret)
+               return ret;
+
+       if (!fw || !fw->data || fw->size < sizeof(*hdr))
+               goto err_inv_fw;
+
+       hdr = (const struct mt76_fw_header *) fw->data;
+
+       if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE)
+               goto err_inv_fw;
+
+       len = sizeof(*hdr);
+       len += le32_to_cpu(hdr->ilm_len);
+       len += le32_to_cpu(hdr->dlm_len);
+
+       if (fw->size != len)
+               goto err_inv_fw;
+
+       val = le16_to_cpu(hdr->fw_ver);
+       dev_info(dev->dev,
+                "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n",
+                (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf,
+                le16_to_cpu(hdr->build_ver), hdr->build_time);
+
+       len = le32_to_cpu(hdr->ilm_len);
+
+       mt7601u_wr(dev, 0x94c, 0);
+       mt7601u_wr(dev, MT_FCE_PSE_CTRL, 0);
+
+       mt7601u_vendor_reset(dev);
+       msleep(5);
+
+       mt7601u_wr(dev, 0xa44, 0);
+       mt7601u_wr(dev, 0x230, 0x84210);
+       mt7601u_wr(dev, 0x400, 0x80c00);
+       mt7601u_wr(dev, 0x800, 1);
+
+       mt7601u_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN |
+                                        MT_PBF_CFG_TX1Q_EN |
+                                        MT_PBF_CFG_TX2Q_EN |
+                                        MT_PBF_CFG_TX3Q_EN));
+
+       mt7601u_wr(dev, MT_FCE_PSE_CTRL, 1);
+
+       mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN |
+                                        MT_USB_DMA_CFG_TX_BULK_EN));
+       val = mt76_set(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_CLR);
+       val &= ~MT_USB_DMA_CFG_TX_CLR;
+       mt7601u_wr(dev, MT_USB_DMA_CFG, val);
+
+       /* FCE tx_fs_base_ptr */
+       mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
+       /* FCE tx_fs_max_cnt */
+       mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1);
+       /* FCE pdma enable */
+       mt7601u_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
+       /* FCE skip_fs_en */
+       mt7601u_wr(dev, MT_FCE_SKIP_FS, 3);
+
+       ret = mt7601u_upload_firmware(dev, (const struct mt76_fw *)fw->data);
+
+       release_firmware(fw);
+
+       return ret;
+
+err_inv_fw:
+       dev_err(dev->dev, "Invalid firmware image\n");
+       release_firmware(fw);
+       return -ENOENT;
+}
+
+int mt7601u_mcu_init(struct mt7601u_dev *dev)
+{
+       int ret;
+
+       mutex_init(&dev->mcu.mutex);
+
+       ret = mt7601u_load_firmware(dev);
+       if (ret)
+               return ret;
+
+       set_bit(MT7601U_STATE_MCU_RUNNING, &dev->state);
+
+       return 0;
+}
+
+int mt7601u_mcu_cmd_init(struct mt7601u_dev *dev)
+{
+       int ret;
+
+       ret = mt7601u_mcu_function_select(dev, Q_SELECT, 1);
+       if (ret)
+               return ret;
+
+       init_completion(&dev->mcu.resp_cmpl);
+       if (mt7601u_usb_alloc_buf(dev, MCU_RESP_URB_SIZE, &dev->mcu.resp)) {
+               mt7601u_usb_free_buf(dev, &dev->mcu.resp);
+               return -ENOMEM;
+       }
+
+       ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP,
+                                    &dev->mcu.resp, GFP_KERNEL,
+                                    mt7601u_complete_urb, &dev->mcu.resp_cmpl);
+       if (ret) {
+               mt7601u_usb_free_buf(dev, &dev->mcu.resp);
+               return ret;
+       }
+
+       return 0;
+}
+
+void mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev)
+{
+       usb_kill_urb(dev->mcu.resp.urb);
+       mt7601u_usb_free_buf(dev, &dev->mcu.resp);
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.h b/drivers/net/wireless/mediatek/mt7601u/mcu.h
new file mode 100644 (file)
index 0000000..4a66d10
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_MCU_H
+#define __MT7601U_MCU_H
+
+struct mt7601u_dev;
+
+/* Register definitions */
+#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_IVB_SIZE                        0x40
+#define MT_MCU_DLM_OFFSET              0x80000
+
+#define MT_MCU_MEMMAP_WLAN             0x00410000
+#define MT_MCU_MEMMAP_BBP              0x40000000
+#define MT_MCU_MEMMAP_RF               0x80000000
+
+#define INBAND_PACKET_MAX_LEN          192
+
+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,
+       ATOMIC_TSSI_SETTING = 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_calibrate {
+       MCU_CAL_R = 1,
+       MCU_CAL_DCOC,
+       MCU_CAL_LC,
+       MCU_CAL_LOFT,
+       MCU_CAL_TXIQ,
+       MCU_CAL_BW,
+       MCU_CAL_DPD,
+       MCU_CAL_RXIQ,
+       MCU_CAL_TXDCOC,
+};
+
+int mt7601u_mcu_init(struct mt7601u_dev *dev);
+int mt7601u_mcu_cmd_init(struct mt7601u_dev *dev);
+void mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev);
+
+int
+mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val);
+int mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
new file mode 100644 (file)
index 0000000..9102be6
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 MT7601U_H
+#define MT7601U_H
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/completion.h>
+#include <net/mac80211.h>
+#include <linux/debugfs.h>
+
+#include "regs.h"
+#include "util.h"
+
+#define MT_CALIBRATE_INTERVAL          (4 * HZ)
+
+#define MT_FREQ_CAL_INIT_DELAY         (30 * HZ)
+#define MT_FREQ_CAL_CHECK_INTERVAL     (10 * HZ)
+#define MT_FREQ_CAL_ADJ_INTERVAL       (HZ / 2)
+
+#define MT_BBP_REG_VERSION             0x00
+
+#define MT_USB_AGGR_SIZE_LIMIT         28 /* * 1024B */
+#define MT_USB_AGGR_TIMEOUT            0x80 /* * 33ns */
+#define MT_RX_ORDER                    3
+#define MT_RX_URB_SIZE                 (PAGE_SIZE << MT_RX_ORDER)
+
+struct mt7601u_dma_buf {
+       struct urb *urb;
+       void *buf;
+       dma_addr_t dma;
+       size_t len;
+};
+
+struct mt7601u_mcu {
+       struct mutex mutex;
+
+       u8 msg_seq;
+
+       struct mt7601u_dma_buf resp;
+       struct completion resp_cmpl;
+};
+
+struct mt7601u_freq_cal {
+       struct delayed_work work;
+       u8 freq;
+       bool enabled;
+       bool adjusting;
+};
+
+struct mac_stats {
+       u64 rx_stat[6];
+       u64 tx_stat[6];
+       u64 aggr_stat[2];
+       u64 aggr_n[32];
+       u64 zero_len_del[2];
+};
+
+#define N_RX_ENTRIES   16
+struct mt7601u_rx_queue {
+       struct mt7601u_dev *dev;
+
+       struct mt7601u_dma_buf_rx {
+               struct urb *urb;
+               struct page *p;
+       } e[N_RX_ENTRIES];
+
+       unsigned int start;
+       unsigned int end;
+       unsigned int entries;
+       unsigned int pending;
+};
+
+#define N_TX_ENTRIES   64
+
+struct mt7601u_tx_queue {
+       struct mt7601u_dev *dev;
+
+       struct mt7601u_dma_buf_tx {
+               struct urb *urb;
+               struct sk_buff *skb;
+       } e[N_TX_ENTRIES];
+
+       unsigned int start;
+       unsigned int end;
+       unsigned int entries;
+       unsigned int used;
+       unsigned int fifo_seq;
+};
+
+/* WCID allocation:
+ *     0: mcast wcid
+ *     1: bssid wcid
+ *  1...: STAs
+ * ...7e: group wcids
+ *    7f: reserved
+ */
+#define N_WCIDS                128
+#define GROUP_WCID(idx)        (N_WCIDS - 2 - idx)
+
+struct mt7601u_eeprom_params;
+
+#define MT_EE_TEMPERATURE_SLOPE                39
+#define MT_FREQ_OFFSET_INVALID         -128
+
+enum mt_temp_mode {
+       MT_TEMP_MODE_NORMAL,
+       MT_TEMP_MODE_HIGH,
+       MT_TEMP_MODE_LOW,
+};
+
+enum mt_bw {
+       MT_BW_20,
+       MT_BW_40,
+};
+
+enum {
+       MT7601U_STATE_INITIALIZED,
+       MT7601U_STATE_REMOVED,
+       MT7601U_STATE_WLAN_RUNNING,
+       MT7601U_STATE_MCU_RUNNING,
+       MT7601U_STATE_SCANNING,
+       MT7601U_STATE_READING_STATS,
+       MT7601U_STATE_MORE_STATS,
+};
+
+/**
+ * struct mt7601u_dev - adapter structure
+ * @lock:              protects @wcid->tx_rate.
+ * @tx_lock:           protects @tx_q and changes of MT7601U_STATE_*_STATS
+                       flags in @state.
+ * @rx_lock:           protects @rx_q.
+ * @con_mon_lock:      protects @ap_bssid, @bcn_*, @avg_rssi.
+ * @mutex:             ensures exclusive access from mac80211 callbacks.
+ * @vendor_req_mutex:  ensures atomicity of vendor requests.
+ * @reg_atomic_mutex:  ensures atomicity of indirect register accesses
+ *                     (accesses to RF and BBP).
+ * @hw_atomic_mutex:   ensures exclusive access to HW during critical
+ *                     operations (power management, channel switch).
+ */
+struct mt7601u_dev {
+       struct ieee80211_hw *hw;
+       struct device *dev;
+
+       unsigned long state;
+
+       struct mutex mutex;
+
+       unsigned long wcid_mask[N_WCIDS / BITS_PER_LONG];
+
+       struct cfg80211_chan_def chandef;
+       struct ieee80211_supported_band *sband_2g;
+
+       struct mt7601u_mcu mcu;
+
+       struct delayed_work cal_work;
+       struct delayed_work mac_work;
+
+       struct workqueue_struct *stat_wq;
+       struct delayed_work stat_work;
+
+       struct mt76_wcid *mon_wcid;
+       struct mt76_wcid __rcu *wcid[N_WCIDS];
+
+       spinlock_t lock;
+
+       const u16 *beacon_offsets;
+
+       u8 macaddr[ETH_ALEN];
+       struct mt7601u_eeprom_params *ee;
+
+       struct mutex vendor_req_mutex;
+       struct mutex reg_atomic_mutex;
+       struct mutex hw_atomic_mutex;
+
+       u32 rxfilter;
+       u32 debugfs_reg;
+
+       u8 out_eps[8];
+       u8 in_eps[8];
+       u16 out_max_packet;
+       u16 in_max_packet;
+
+       /* TX */
+       spinlock_t tx_lock;
+       struct mt7601u_tx_queue *tx_q;
+
+       atomic_t avg_ampdu_len;
+
+       /* RX */
+       spinlock_t rx_lock;
+       struct tasklet_struct rx_tasklet;
+       struct mt7601u_rx_queue rx_q;
+
+       /* Connection monitoring things */
+       spinlock_t con_mon_lock;
+       u8 ap_bssid[ETH_ALEN];
+
+       s8 bcn_freq_off;
+       u8 bcn_phy_mode;
+
+       int avg_rssi; /* starts at 0 and converges */
+
+       u8 agc_save;
+
+       struct mt7601u_freq_cal freq_cal;
+
+       bool tssi_read_trig;
+
+       s8 tssi_init;
+       s8 tssi_init_hvga;
+       s16 tssi_init_hvga_offset_db;
+
+       int prev_pwr_diff;
+
+       enum mt_temp_mode temp_mode;
+       int curr_temp;
+       int dpd_temp;
+       s8 raw_temp;
+       bool pll_lock_protect;
+
+       u8 bw;
+       bool chan_ext_below;
+
+       /* PA mode */
+       u32 rf_pa_mode[2];
+
+       struct mac_stats stats;
+};
+
+struct mt7601u_tssi_params {
+       char tssi0;
+       int trgt_power;
+};
+
+struct mt76_wcid {
+       u8 idx;
+       u8 hw_key_idx;
+
+       u16 tx_rate;
+       bool tx_rate_set;
+       u8 tx_rate_nss;
+};
+
+struct mt76_vif {
+       u8 idx;
+
+       struct mt76_wcid group_wcid;
+};
+
+struct mt76_sta {
+       struct mt76_wcid wcid;
+       u16 agg_ssn[IEEE80211_NUM_TIDS];
+};
+
+struct mt76_reg_pair {
+       u32 reg;
+       u32 value;
+};
+
+struct mt7601u_rxwi;
+
+extern const struct ieee80211_ops mt7601u_ops;
+
+void mt7601u_init_debugfs(struct mt7601u_dev *dev);
+
+u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset);
+void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val);
+u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val);
+u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val);
+void mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset,
+                    const void *data, int len);
+
+int mt7601u_wait_asic_ready(struct mt7601u_dev *dev);
+bool mt76_poll(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
+              int timeout);
+bool mt76_poll_msec(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
+                   int timeout);
+
+/* Compatibility with mt76 */
+#define mt76_rmw_field(_dev, _reg, _field, _val)       \
+       mt76_rmw(_dev, _reg, _field, MT76_SET(_field, _val))
+
+static inline u32 mt76_rr(struct mt7601u_dev *dev, u32 offset)
+{
+       return mt7601u_rr(dev, offset);
+}
+
+static inline void mt76_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
+{
+       return mt7601u_wr(dev, offset, val);
+}
+
+static inline u32
+mt76_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
+{
+       return mt7601u_rmw(dev, offset, mask, val);
+}
+
+static inline u32 mt76_set(struct mt7601u_dev *dev, u32 offset, u32 val)
+{
+       return mt76_rmw(dev, offset, 0, val);
+}
+
+static inline u32 mt76_clear(struct mt7601u_dev *dev, u32 offset, u32 val)
+{
+       return mt76_rmw(dev, offset, val, 0);
+}
+
+int mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base,
+                           const struct mt76_reg_pair *data, int len);
+int mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset,
+                            const u32 *data, int n);
+void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr);
+
+/* Init */
+struct mt7601u_dev *mt7601u_alloc_device(struct device *dev);
+int mt7601u_init_hardware(struct mt7601u_dev *dev);
+int mt7601u_register_device(struct mt7601u_dev *dev);
+void mt7601u_cleanup(struct mt7601u_dev *dev);
+
+int mt7601u_mac_start(struct mt7601u_dev *dev);
+void mt7601u_mac_stop(struct mt7601u_dev *dev);
+
+/* PHY */
+int mt7601u_phy_init(struct mt7601u_dev *dev);
+int mt7601u_wait_bbp_ready(struct mt7601u_dev *dev);
+void mt7601u_set_rx_path(struct mt7601u_dev *dev, u8 path);
+void mt7601u_set_tx_dac(struct mt7601u_dev *dev, u8 path);
+int mt7601u_bbp_set_bw(struct mt7601u_dev *dev, int bw);
+void mt7601u_agc_save(struct mt7601u_dev *dev);
+void mt7601u_agc_restore(struct mt7601u_dev *dev);
+int mt7601u_phy_set_channel(struct mt7601u_dev *dev,
+                           struct cfg80211_chan_def *chandef);
+void mt7601u_phy_recalibrate_after_assoc(struct mt7601u_dev *dev);
+int mt7601u_phy_get_rssi(struct mt7601u_dev *dev,
+                        struct mt7601u_rxwi *rxwi, u16 rate);
+void mt7601u_phy_con_cal_onoff(struct mt7601u_dev *dev,
+                              struct ieee80211_bss_conf *info);
+
+/* MAC */
+void mt7601u_mac_work(struct work_struct *work);
+void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot,
+                               int ht_mode);
+void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb);
+void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval);
+void
+mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac);
+void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev);
+
+/* TX */
+void mt7601u_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
+               struct sk_buff *skb);
+int mt7601u_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                   u16 queue, const struct ieee80211_tx_queue_params *params);
+void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb);
+void mt7601u_tx_stat(struct work_struct *work);
+
+/* util */
+void mt76_remove_hdr_pad(struct sk_buff *skb);
+int mt76_insert_hdr_pad(struct sk_buff *skb);
+
+u32 mt7601u_bbp_set_ctrlch(struct mt7601u_dev *dev, bool below);
+
+static inline u32 mt7601u_mac_set_ctrlch(struct mt7601u_dev *dev, bool below)
+{
+       return mt7601u_rmc(dev, MT_TX_BAND_CFG, 1, below);
+}
+
+int mt7601u_dma_init(struct mt7601u_dev *dev);
+void mt7601u_dma_cleanup(struct mt7601u_dev *dev);
+
+int mt7601u_dma_enqueue_tx(struct mt7601u_dev *dev, struct sk_buff *skb,
+                          struct mt76_wcid *wcid, int hw_q);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/phy.c b/drivers/net/wireless/mediatek/mt7601u/phy.c
new file mode 100644 (file)
index 0000000..1908af6
--- /dev/null
@@ -0,0 +1,1251 @@
+/*
+ * (c) Copyright 2002-2010, Ralink Technology, Inc.
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "mcu.h"
+#include "eeprom.h"
+#include "trace.h"
+#include "initvals_phy.h"
+
+#include <linux/etherdevice.h>
+
+static void mt7601u_agc_reset(struct mt7601u_dev *dev);
+
+static int
+mt7601u_rf_wr(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 value)
+{
+       int ret = 0;
+
+       if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) ||
+           WARN_ON(offset > 63))
+               return -EINVAL;
+       if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return 0;
+
+       mutex_lock(&dev->reg_atomic_mutex);
+
+       if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       mt7601u_wr(dev, MT_RF_CSR_CFG, MT76_SET(MT_RF_CSR_CFG_DATA, value) |
+                                      MT76_SET(MT_RF_CSR_CFG_REG_BANK, bank) |
+                                      MT76_SET(MT_RF_CSR_CFG_REG_ID, offset) |
+                                      MT_RF_CSR_CFG_WR |
+                                      MT_RF_CSR_CFG_KICK);
+       trace_rf_write(dev, bank, offset, value);
+out:
+       mutex_unlock(&dev->reg_atomic_mutex);
+
+       if (ret < 0)
+               dev_err(dev->dev, "Error: RF write %02hhx:%02hhx failed:%d!!\n",
+                       bank, offset, ret);
+
+       return ret;
+}
+
+static int
+mt7601u_rf_rr(struct mt7601u_dev *dev, u8 bank, u8 offset)
+{
+       int ret = -ETIMEDOUT;
+       u32 val;
+
+       if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) ||
+           WARN_ON(offset > 63))
+               return -EINVAL;
+       if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return 0xff;
+
+       mutex_lock(&dev->reg_atomic_mutex);
+
+       if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100))
+               goto out;
+
+       mt7601u_wr(dev, MT_RF_CSR_CFG, MT76_SET(MT_RF_CSR_CFG_REG_BANK, bank) |
+                                      MT76_SET(MT_RF_CSR_CFG_REG_ID, offset) |
+                                      MT_RF_CSR_CFG_KICK);
+
+       if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100))
+               goto out;
+
+       val = mt7601u_rr(dev, MT_RF_CSR_CFG);
+       if (MT76_GET(MT_RF_CSR_CFG_REG_ID, val) == offset &&
+           MT76_GET(MT_RF_CSR_CFG_REG_BANK, val) == bank) {
+               ret = MT76_GET(MT_RF_CSR_CFG_DATA, val);
+               trace_rf_read(dev, bank, offset, ret);
+       }
+out:
+       mutex_unlock(&dev->reg_atomic_mutex);
+
+       if (ret < 0)
+               dev_err(dev->dev, "Error: RF read %02hhx:%02hhx failed:%d!!\n",
+                       bank, offset, ret);
+
+       return ret;
+}
+
+static int
+mt7601u_rf_rmw(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 mask, u8 val)
+{
+       int ret;
+
+       ret = mt7601u_rf_rr(dev, bank, offset);
+       if (ret < 0)
+               return ret;
+       val |= ret & ~mask;
+       ret = mt7601u_rf_wr(dev, bank, offset, val);
+       if (ret)
+               return ret;
+
+       return val;
+}
+
+static int
+mt7601u_rf_set(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 val)
+{
+       return mt7601u_rf_rmw(dev, bank, offset, 0, val);
+}
+
+static int
+mt7601u_rf_clear(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 mask)
+{
+       return mt7601u_rf_rmw(dev, bank, offset, mask, 0);
+}
+
+static void mt7601u_bbp_wr(struct mt7601u_dev *dev, u8 offset, u8 val)
+{
+       if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) ||
+           test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return;
+
+       mutex_lock(&dev->reg_atomic_mutex);
+
+       if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000)) {
+               dev_err(dev->dev, "Error: BBP write %02hhx failed!!\n", offset);
+               goto out;
+       }
+
+       mt7601u_wr(dev, MT_BBP_CSR_CFG,
+                  MT76_SET(MT_BBP_CSR_CFG_VAL, val) |
+                  MT76_SET(MT_BBP_CSR_CFG_REG_NUM, offset) |
+                  MT_BBP_CSR_CFG_RW_MODE | MT_BBP_CSR_CFG_BUSY);
+       trace_bbp_write(dev, offset, val);
+out:
+       mutex_unlock(&dev->reg_atomic_mutex);
+}
+
+static int mt7601u_bbp_rr(struct mt7601u_dev *dev, u8 offset)
+{
+       u32 val;
+       int ret = -ETIMEDOUT;
+
+       if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)))
+               return -EINVAL;
+       if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
+               return 0xff;
+
+       mutex_lock(&dev->reg_atomic_mutex);
+
+       if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000))
+               goto out;
+
+       mt7601u_wr(dev, MT_BBP_CSR_CFG,
+                  MT76_SET(MT_BBP_CSR_CFG_REG_NUM, offset) |
+                  MT_BBP_CSR_CFG_RW_MODE | MT_BBP_CSR_CFG_BUSY |
+                  MT_BBP_CSR_CFG_READ);
+
+       if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000))
+               goto out;
+
+       val = mt7601u_rr(dev, MT_BBP_CSR_CFG);
+       if (MT76_GET(MT_BBP_CSR_CFG_REG_NUM, val) == offset) {
+               ret = MT76_GET(MT_BBP_CSR_CFG_VAL, val);
+               trace_bbp_read(dev, offset, ret);
+       }
+out:
+       mutex_unlock(&dev->reg_atomic_mutex);
+
+       if (ret < 0)
+               dev_err(dev->dev, "Error: BBP read %02hhx failed:%d!!\n",
+                       offset, ret);
+
+       return ret;
+}
+
+static int mt7601u_bbp_rmw(struct mt7601u_dev *dev, u8 offset, u8 mask, u8 val)
+{
+       int ret;
+
+       ret = mt7601u_bbp_rr(dev, offset);
+       if (ret < 0)
+               return ret;
+       val |= ret & ~mask;
+       mt7601u_bbp_wr(dev, offset, val);
+
+       return val;
+}
+
+static u8 mt7601u_bbp_rmc(struct mt7601u_dev *dev, u8 offset, u8 mask, u8 val)
+{
+       int ret;
+
+       ret = mt7601u_bbp_rr(dev, offset);
+       if (ret < 0)
+               return ret;
+       val |= ret & ~mask;
+       if (ret != val)
+               mt7601u_bbp_wr(dev, offset, val);
+
+       return val;
+}
+
+int mt7601u_wait_bbp_ready(struct mt7601u_dev *dev)
+{
+       int i = 20;
+       u8 val;
+
+       do {
+               val = mt7601u_bbp_rr(dev, MT_BBP_REG_VERSION);
+               if (val && ~val)
+                       break;
+       } while (--i);
+
+       if (!i) {
+               dev_err(dev->dev, "Error: BBP is not ready\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+u32 mt7601u_bbp_set_ctrlch(struct mt7601u_dev *dev, bool below)
+{
+       return mt7601u_bbp_rmc(dev, 3, 0x20, below ? 0x20 : 0);
+}
+
+int mt7601u_phy_get_rssi(struct mt7601u_dev *dev,
+                        struct mt7601u_rxwi *rxwi, u16 rate)
+{
+       static const s8 lna[2][2][3] = {
+               /* main LNA */ {
+                       /* bw20 */ { -2, 15, 33 },
+                       /* bw40 */ {  0, 16, 34 }
+               },
+               /*  aux LNA */ {
+                       /* bw20 */ { -2, 15, 33 },
+                       /* bw40 */ { -2, 16, 34 }
+               }
+       };
+       int bw = MT76_GET(MT_RXWI_RATE_BW, rate);
+       int aux_lna = MT76_GET(MT_RXWI_ANT_AUX_LNA, rxwi->ant);
+       int lna_id = MT76_GET(MT_RXWI_GAIN_RSSI_LNA_ID, rxwi->gain);
+       int val;
+
+       if (lna_id) /* LNA id can be 0, 2, 3. */
+               lna_id--;
+
+       val = 8;
+       val -= lna[aux_lna][bw][lna_id];
+       val -= MT76_GET(MT_RXWI_GAIN_RSSI_VAL, rxwi->gain);
+       val -= dev->ee->lna_gain;
+       val -= dev->ee->rssi_offset[0];
+
+       return val;
+}
+
+static void mt7601u_vco_cal(struct mt7601u_dev *dev)
+{
+       mt7601u_rf_wr(dev, 0, 4, 0x0a);
+       mt7601u_rf_wr(dev, 0, 5, 0x20);
+       mt7601u_rf_set(dev, 0, 4, BIT(7));
+       msleep(2);
+}
+
+static int mt7601u_set_bw_filter(struct mt7601u_dev *dev, bool cal)
+{
+       u32 filter = 0;
+       int ret;
+
+       if (!cal)
+               filter |= 0x10000;
+       if (dev->bw != MT_BW_20)
+               filter |= 0x00100;
+
+       /* TX */
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_BW, filter | 1);
+       if (ret)
+               return ret;
+       /* RX */
+       return mt7601u_mcu_calibrate(dev, MCU_CAL_BW, filter);
+}
+
+static int mt7601u_load_bbp_temp_table_bw(struct mt7601u_dev *dev)
+{
+       const struct reg_table *t;
+
+       if (WARN_ON(dev->temp_mode > MT_TEMP_MODE_LOW))
+               return -EINVAL;
+
+       t = &bbp_mode_table[dev->temp_mode][dev->bw];
+
+       return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, t->regs, t->n);
+}
+
+static int mt7601u_bbp_temp(struct mt7601u_dev *dev, int mode, const char *name)
+{
+       const struct reg_table *t;
+       int ret;
+
+       if (dev->temp_mode == mode)
+               return 0;
+
+       dev->temp_mode = mode;
+       trace_temp_mode(dev, mode);
+
+       t = bbp_mode_table[dev->temp_mode];
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP,
+                                     t[2].regs, t[2].n);
+       if (ret)
+               return ret;
+
+       return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP,
+                                      t[dev->bw].regs, t[dev->bw].n);
+}
+
+static void mt7601u_apply_ch14_fixup(struct mt7601u_dev *dev, int hw_chan)
+{
+       struct mt7601u_rate_power *t = &dev->ee->power_rate_table;
+
+       if (hw_chan != 14 || dev->bw != MT_BW_20) {
+               mt7601u_bbp_rmw(dev, 4, 0x20, 0);
+               mt7601u_bbp_wr(dev, 178, 0xff);
+
+               t->cck[0].bw20 = dev->ee->real_cck_bw20[0];
+               t->cck[1].bw20 = dev->ee->real_cck_bw20[1];
+       } else { /* Apply CH14 OBW fixup */
+               mt7601u_bbp_wr(dev, 4, 0x60);
+               mt7601u_bbp_wr(dev, 178, 0);
+
+               /* Note: vendor code is buggy here for negative values */
+               t->cck[0].bw20 = dev->ee->real_cck_bw20[0] - 2;
+               t->cck[1].bw20 = dev->ee->real_cck_bw20[1] - 2;
+       }
+}
+
+static int __mt7601u_phy_set_channel(struct mt7601u_dev *dev,
+                                    struct cfg80211_chan_def *chandef)
+{
+#define FREQ_PLAN_REGS 4
+       static const u8 freq_plan[14][FREQ_PLAN_REGS] = {
+               { 0x99, 0x99,   0x09,   0x50 },
+               { 0x46, 0x44,   0x0a,   0x50 },
+               { 0xec, 0xee,   0x0a,   0x50 },
+               { 0x99, 0x99,   0x0b,   0x50 },
+               { 0x46, 0x44,   0x08,   0x51 },
+               { 0xec, 0xee,   0x08,   0x51 },
+               { 0x99, 0x99,   0x09,   0x51 },
+               { 0x46, 0x44,   0x0a,   0x51 },
+               { 0xec, 0xee,   0x0a,   0x51 },
+               { 0x99, 0x99,   0x0b,   0x51 },
+               { 0x46, 0x44,   0x08,   0x52 },
+               { 0xec, 0xee,   0x08,   0x52 },
+               { 0x99, 0x99,   0x09,   0x52 },
+               { 0x33, 0x33,   0x0b,   0x52 },
+       };
+       struct mt76_reg_pair channel_freq_plan[FREQ_PLAN_REGS] = {
+               { 17, 0 }, { 18, 0 }, { 19, 0 }, { 20, 0 },
+       };
+       struct mt76_reg_pair bbp_settings[3] = {
+               { 62, 0x37 - dev->ee->lna_gain },
+               { 63, 0x37 - dev->ee->lna_gain },
+               { 64, 0x37 - dev->ee->lna_gain },
+       };
+
+       struct ieee80211_channel *chan = chandef->chan;
+       enum nl80211_channel_type chan_type =
+               cfg80211_get_chandef_type(chandef);
+       struct mt7601u_rate_power *t = &dev->ee->power_rate_table;
+       int chan_idx;
+       bool chan_ext_below;
+       u8 bw;
+       int i, ret;
+
+       bw = MT_BW_20;
+       chan_ext_below = (chan_type == NL80211_CHAN_HT40MINUS);
+       chan_idx = chan->hw_value - 1;
+
+       if (chandef->width == NL80211_CHAN_WIDTH_40) {
+               bw = MT_BW_40;
+
+               if (chan_idx > 1 && chan_type == NL80211_CHAN_HT40MINUS)
+                       chan_idx -= 2;
+               else if (chan_idx < 12 && chan_type == NL80211_CHAN_HT40PLUS)
+                       chan_idx += 2;
+               else
+                       dev_err(dev->dev, "Error: invalid 40MHz channel!!\n");
+       }
+
+       if (bw != dev->bw || chan_ext_below != dev->chan_ext_below) {
+               dev_dbg(dev->dev, "Info: switching HT mode bw:%d below:%d\n",
+                       bw, chan_ext_below);
+
+               mt7601u_bbp_set_bw(dev, bw);
+
+               mt7601u_bbp_set_ctrlch(dev, chan_ext_below);
+               mt7601u_mac_set_ctrlch(dev, chan_ext_below);
+               dev->chan_ext_below = chan_ext_below;
+       }
+
+       for (i = 0; i < FREQ_PLAN_REGS; i++)
+               channel_freq_plan[i].value = freq_plan[chan_idx][i];
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_RF,
+                                     channel_freq_plan, FREQ_PLAN_REGS);
+       if (ret)
+               return ret;
+
+       mt7601u_rmw(dev, MT_TX_ALC_CFG_0, 0x3f3f,
+                   dev->ee->chan_pwr[chan_idx] & 0x3f);
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP,
+                                     bbp_settings, ARRAY_SIZE(bbp_settings));
+       if (ret)
+               return ret;
+
+       mt7601u_vco_cal(dev);
+       mt7601u_bbp_set_bw(dev, bw);
+       ret = mt7601u_set_bw_filter(dev, false);
+       if (ret)
+               return ret;
+
+       mt7601u_apply_ch14_fixup(dev, chan->hw_value);
+       mt7601u_wr(dev, MT_TX_PWR_CFG_0, int_to_s6(t->ofdm[1].bw20) << 24 |
+                                        int_to_s6(t->ofdm[0].bw20) << 16 |
+                                        int_to_s6(t->cck[1].bw20) << 8 |
+                                        int_to_s6(t->cck[0].bw20));
+
+       if (test_bit(MT7601U_STATE_SCANNING, &dev->state))
+               mt7601u_agc_reset(dev);
+
+       dev->chandef = *chandef;
+
+       return 0;
+}
+
+int mt7601u_phy_set_channel(struct mt7601u_dev *dev,
+                           struct cfg80211_chan_def *chandef)
+{
+       int ret;
+
+       cancel_delayed_work_sync(&dev->cal_work);
+       cancel_delayed_work_sync(&dev->freq_cal.work);
+
+       mutex_lock(&dev->hw_atomic_mutex);
+       ret = __mt7601u_phy_set_channel(dev, chandef);
+       mutex_unlock(&dev->hw_atomic_mutex);
+       if (ret)
+               return ret;
+
+       if (test_bit(MT7601U_STATE_SCANNING, &dev->state))
+               return 0;
+
+       ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
+                                    MT_CALIBRATE_INTERVAL);
+       if (dev->freq_cal.enabled)
+               ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work,
+                                            MT_FREQ_CAL_INIT_DELAY);
+       return 0;
+}
+
+#define BBP_R47_FLAG           GENMASK(2, 0)
+#define BBP_R47_F_TSSI         0
+#define BBP_R47_F_PKT_T                1
+#define BBP_R47_F_TX_RATE      2
+#define BBP_R47_F_TEMP         4
+/**
+ * mt7601u_bbp_r47_get - read value through BBP R47/R49 pair
+ * @dev:       pointer to adapter structure
+ * @reg:       value of BBP R47 before the operation
+ * @flag:      one of the BBP_R47_F_* flags
+ *
+ * Convenience helper for reading values through BBP R47/R49 pair.
+ * Takes old value of BBP R47 as @reg, because callers usually have it
+ * cached already.
+ *
+ * Return: value of BBP R49.
+ */
+static u8 mt7601u_bbp_r47_get(struct mt7601u_dev *dev, u8 reg, u8 flag)
+{
+       flag |= reg & ~BBP_R47_FLAG;
+       mt7601u_bbp_wr(dev, 47, flag);
+       usleep_range(500, 700);
+       return mt7601u_bbp_rr(dev, 49);
+}
+
+static s8 mt7601u_read_bootup_temp(struct mt7601u_dev *dev)
+{
+       u8 bbp_val, temp;
+       u32 rf_bp, rf_set;
+       int i;
+
+       rf_set = mt7601u_rr(dev, MT_RF_SETTING_0);
+       rf_bp = mt7601u_rr(dev, MT_RF_BYPASS_0);
+
+       mt7601u_wr(dev, MT_RF_BYPASS_0, 0);
+       mt7601u_wr(dev, MT_RF_SETTING_0, 0x00000010);
+       mt7601u_wr(dev, MT_RF_BYPASS_0, 0x00000010);
+
+       bbp_val = mt7601u_bbp_rmw(dev, 47, 0, 0x10);
+
+       mt7601u_bbp_wr(dev, 22, 0x40);
+
+       for (i = 100; i && (bbp_val & 0x10); i--)
+               bbp_val = mt7601u_bbp_rr(dev, 47);
+
+       temp = mt7601u_bbp_r47_get(dev, bbp_val, BBP_R47_F_TEMP);
+
+       mt7601u_bbp_wr(dev, 22, 0);
+
+       bbp_val = mt7601u_bbp_rr(dev, 21);
+       bbp_val |= 0x02;
+       mt7601u_bbp_wr(dev, 21, bbp_val);
+       bbp_val &= ~0x02;
+       mt7601u_bbp_wr(dev, 21, bbp_val);
+
+       mt7601u_wr(dev, MT_RF_BYPASS_0, 0);
+       mt7601u_wr(dev, MT_RF_SETTING_0, rf_set);
+       mt7601u_wr(dev, MT_RF_BYPASS_0, rf_bp);
+
+       trace_read_temp(dev, temp);
+       return temp;
+}
+
+static s8 mt7601u_read_temp(struct mt7601u_dev *dev)
+{
+       int i;
+       u8 val;
+       s8 temp;
+
+       val = mt7601u_bbp_rmw(dev, 47, 0x7f, 0x10);
+
+       /* Note: this rarely succeeds, temp can change even if it fails. */
+       for (i = 100; i && (val & 0x10); i--)
+               val = mt7601u_bbp_rr(dev, 47);
+
+       temp = mt7601u_bbp_r47_get(dev, val, BBP_R47_F_TEMP);
+
+       trace_read_temp(dev, temp);
+       return temp;
+}
+
+static void mt7601u_rxdc_cal(struct mt7601u_dev *dev)
+{
+       static const struct mt76_reg_pair intro[] = {
+               { 158, 0x8d }, { 159, 0xfc },
+               { 158, 0x8c }, { 159, 0x4c },
+       }, outro[] = {
+               { 158, 0x8d }, { 159, 0xe0 },
+       };
+       u32 mac_ctrl;
+       int i, ret;
+
+       mac_ctrl = mt7601u_rr(dev, MT_MAC_SYS_CTRL);
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX);
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP,
+                                     intro, ARRAY_SIZE(intro));
+       if (ret)
+               dev_err(dev->dev, "%s intro failed:%d\n", __func__, ret);
+
+       for (i = 20; i; i--) {
+               usleep_range(300, 500);
+
+               mt7601u_bbp_wr(dev, 158, 0x8c);
+               if (mt7601u_bbp_rr(dev, 159) == 0x0c)
+                       break;
+       }
+       if (!i)
+               dev_err(dev->dev, "%s timed out\n", __func__);
+
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0);
+
+       ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP,
+                                     outro, ARRAY_SIZE(outro));
+       if (ret)
+               dev_err(dev->dev, "%s outro failed:%d\n", __func__, ret);
+
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, mac_ctrl);
+}
+
+void mt7601u_phy_recalibrate_after_assoc(struct mt7601u_dev *dev)
+{
+       mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->curr_temp);
+
+       mt7601u_rxdc_cal(dev);
+}
+
+/* Note: function copied from vendor driver */
+static s16 lin2dBd(u16 linear)
+{
+       short exp = 0;
+       unsigned int mantisa;
+       int app, dBd;
+
+       if (WARN_ON(!linear))
+               return -10000;
+
+       mantisa = linear;
+
+       exp = fls(mantisa) - 16;
+       if (exp > 0)
+               mantisa >>= exp;
+       else
+               mantisa <<= abs(exp);
+
+       if (mantisa <= 0xb800)
+               app = (mantisa + (mantisa >> 3) + (mantisa >> 4) - 0x9600);
+       else
+               app = (mantisa - (mantisa >> 3) - (mantisa >> 6) - 0x5a00);
+       if (app < 0)
+               app = 0;
+
+       dBd = ((15 + exp) << 15) + app;
+       dBd = (dBd << 2) + (dBd << 1) + (dBd >> 6) + (dBd >> 7);
+       dBd = (dBd >> 10);
+
+       return dBd;
+}
+
+static void
+mt7601u_set_initial_tssi(struct mt7601u_dev *dev, s16 tssi_db, s16 tssi_hvga_db)
+{
+       struct tssi_data *d = &dev->ee->tssi_data;
+       int init_offset;
+
+       init_offset = -((tssi_db * d->slope + d->offset[1]) / 4096) + 10;
+
+       mt76_rmw(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
+                int_to_s6(init_offset) & MT_TX_ALC_CFG_1_TEMP_COMP);
+}
+
+static void mt7601u_tssi_dc_gain_cal(struct mt7601u_dev *dev)
+{
+       u8 rf_vga, rf_mixer, bbp_r47;
+       int i, j;
+       s8 res[4];
+       s16 tssi_init_db, tssi_init_hvga_db;
+
+       mt7601u_wr(dev, MT_RF_SETTING_0, 0x00000030);
+       mt7601u_wr(dev, MT_RF_BYPASS_0, 0x000c0030);
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0);
+
+       mt7601u_bbp_wr(dev, 58, 0);
+       mt7601u_bbp_wr(dev, 241, 0x2);
+       mt7601u_bbp_wr(dev, 23, 0x8);
+       bbp_r47 = mt7601u_bbp_rr(dev, 47);
+
+       /* Set VGA gain */
+       rf_vga = mt7601u_rf_rr(dev, 5, 3);
+       mt7601u_rf_wr(dev, 5, 3, 8);
+
+       /* Mixer disable */
+       rf_mixer = mt7601u_rf_rr(dev, 4, 39);
+       mt7601u_rf_wr(dev, 4, 39, 0);
+
+       for (i = 0; i < 4; i++) {
+               mt7601u_rf_wr(dev, 4, 39, (i & 1) ? rf_mixer : 0);
+
+               mt7601u_bbp_wr(dev, 23, (i < 2) ? 0x08 : 0x02);
+               mt7601u_rf_wr(dev, 5, 3, (i < 2) ? 0x08 : 0x11);
+
+               /* BBP TSSI initial and soft reset */
+               mt7601u_bbp_wr(dev, 22, 0);
+               mt7601u_bbp_wr(dev, 244, 0);
+
+               mt7601u_bbp_wr(dev, 21, 1);
+               udelay(1);
+               mt7601u_bbp_wr(dev, 21, 0);
+
+               /* TSSI measurement */
+               mt7601u_bbp_wr(dev, 47, 0x50);
+               mt7601u_bbp_wr(dev, (i & 1) ? 244 : 22, (i & 1) ? 0x31 : 0x40);
+
+               for (j = 20; j; j--)
+                       if (!(mt7601u_bbp_rr(dev, 47) & 0x10))
+                               break;
+               if (!j)
+                       dev_err(dev->dev, "%s timed out\n", __func__);
+
+               /* TSSI read */
+               mt7601u_bbp_wr(dev, 47, 0x40);
+               res[i] = mt7601u_bbp_rr(dev, 49);
+       }
+
+       tssi_init_db = lin2dBd((short)res[1] - res[0]);
+       tssi_init_hvga_db = lin2dBd(((short)res[3] - res[2]) * 4);
+       dev->tssi_init = res[0];
+       dev->tssi_init_hvga = res[2];
+       dev->tssi_init_hvga_offset_db = tssi_init_hvga_db - tssi_init_db;
+
+       dev_dbg(dev->dev,
+               "TSSI_init:%hhx db:%hx hvga:%hhx hvga_db:%hx off_db:%hx\n",
+               dev->tssi_init, tssi_init_db, dev->tssi_init_hvga,
+               tssi_init_hvga_db, dev->tssi_init_hvga_offset_db);
+
+       mt7601u_bbp_wr(dev, 22, 0);
+       mt7601u_bbp_wr(dev, 244, 0);
+
+       mt7601u_bbp_wr(dev, 21, 1);
+       udelay(1);
+       mt7601u_bbp_wr(dev, 21, 0);
+
+       mt7601u_wr(dev, MT_RF_BYPASS_0, 0);
+       mt7601u_wr(dev, MT_RF_SETTING_0, 0);
+
+       mt7601u_rf_wr(dev, 5, 3, rf_vga);
+       mt7601u_rf_wr(dev, 4, 39, rf_mixer);
+       mt7601u_bbp_wr(dev, 47, bbp_r47);
+
+       mt7601u_set_initial_tssi(dev, tssi_init_db, tssi_init_hvga_db);
+}
+
+static int mt7601u_temp_comp(struct mt7601u_dev *dev, bool on)
+{
+       int ret, temp, hi_temp = 400, lo_temp = -200;
+
+       temp = (dev->raw_temp - dev->ee->ref_temp) * MT_EE_TEMPERATURE_SLOPE;
+       dev->curr_temp = temp;
+
+       /* DPD Calibration */
+       if (temp - dev->dpd_temp > 450 || temp - dev->dpd_temp < -450) {
+               dev->dpd_temp = temp;
+
+               ret = mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->dpd_temp);
+               if (ret)
+                       return ret;
+
+               mt7601u_vco_cal(dev);
+
+               dev_dbg(dev->dev, "Recalibrate DPD\n");
+       }
+
+       /* PLL Lock Protect */
+       if (temp < -50 && !dev->pll_lock_protect) { /* < 20C */
+               dev->pll_lock_protect =  true;
+
+               mt7601u_rf_wr(dev, 4, 4, 6);
+               mt7601u_rf_clear(dev, 4, 10, 0x30);
+
+               dev_dbg(dev->dev, "PLL lock protect on - too cold\n");
+       } else if (temp > 50 && dev->pll_lock_protect) { /* > 30C */
+               dev->pll_lock_protect = false;
+
+               mt7601u_rf_wr(dev, 4, 4, 0);
+               mt7601u_rf_rmw(dev, 4, 10, 0x30, 0x10);
+
+               dev_dbg(dev->dev, "PLL lock protect off\n");
+       }
+
+       if (on) {
+               hi_temp -= 50;
+               lo_temp -= 50;
+       }
+
+       /* BBP CR for H, L, N temperature */
+       if (temp > hi_temp)
+               return mt7601u_bbp_temp(dev, MT_TEMP_MODE_HIGH, "high");
+       else if (temp > lo_temp)
+               return mt7601u_bbp_temp(dev, MT_TEMP_MODE_NORMAL, "normal");
+       else
+               return mt7601u_bbp_temp(dev, MT_TEMP_MODE_LOW, "low");
+}
+
+/* Note: this is used only with TSSI, we can just use trgt_pwr from eeprom. */
+static int mt7601u_current_tx_power(struct mt7601u_dev *dev)
+{
+       return dev->ee->chan_pwr[dev->chandef.chan->hw_value - 1];
+}
+
+static bool mt7601u_use_hvga(struct mt7601u_dev *dev)
+{
+       return !(mt7601u_current_tx_power(dev) > 20);
+}
+
+static s16
+mt7601u_phy_rf_pa_mode_val(struct mt7601u_dev *dev, int phy_mode, int tx_rate)
+{
+       static const s16 decode_tb[] = { 0, 8847, -5734, -5734 };
+       u32 reg;
+
+       switch (phy_mode) {
+       case MT_PHY_TYPE_OFDM:
+               tx_rate += 4;
+       case MT_PHY_TYPE_CCK:
+               reg = dev->rf_pa_mode[0];
+               break;
+       default:
+               reg = dev->rf_pa_mode[1];
+               break;
+       }
+
+       return decode_tb[(reg >> (tx_rate * 2)) & 0x3];
+}
+
+static struct mt7601u_tssi_params
+mt7601u_tssi_params_get(struct mt7601u_dev *dev)
+{
+       static const u8 ofdm_pkt2rate[8] = { 6, 4, 2, 0, 7, 5, 3, 1 };
+       static const int static_power[4] = { 0, -49152, -98304, 49152 };
+       struct mt7601u_tssi_params p;
+       u8 bbp_r47, pkt_type, tx_rate;
+       struct power_per_rate *rate_table;
+
+       bbp_r47 = mt7601u_bbp_rr(dev, 47);
+
+       p.tssi0 = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TSSI);
+       dev->raw_temp = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TEMP);
+       pkt_type = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_PKT_T);
+
+       p.trgt_power = mt7601u_current_tx_power(dev);
+
+       switch (pkt_type & 0x03) {
+       case MT_PHY_TYPE_CCK:
+               tx_rate = (pkt_type >> 4) & 0x03;
+               rate_table = dev->ee->power_rate_table.cck;
+               break;
+
+       case MT_PHY_TYPE_OFDM:
+               tx_rate = ofdm_pkt2rate[(pkt_type >> 4) & 0x07];
+               rate_table = dev->ee->power_rate_table.ofdm;
+               break;
+
+       default:
+               tx_rate = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TX_RATE);
+               tx_rate &= 0x7f;
+               rate_table = dev->ee->power_rate_table.ht;
+               break;
+       }
+
+       if (dev->bw == MT_BW_20)
+               p.trgt_power += rate_table[tx_rate / 2].bw20;
+       else
+               p.trgt_power += rate_table[tx_rate / 2].bw40;
+
+       p.trgt_power <<= 12;
+
+       dev_dbg(dev->dev, "tx_rate:%02hhx pwr:%08x\n", tx_rate, p.trgt_power);
+
+       p.trgt_power += mt7601u_phy_rf_pa_mode_val(dev, pkt_type & 0x03,
+                                                  tx_rate);
+
+       /* Channel 14, cck, bw20 */
+       if ((pkt_type & 0x03) == MT_PHY_TYPE_CCK) {
+               if (mt7601u_bbp_rr(dev, 4) & 0x20)
+                       p.trgt_power += mt7601u_bbp_rr(dev, 178) ? 18022 : 9830;
+               else
+                       p.trgt_power += mt7601u_bbp_rr(dev, 178) ? 819 : 24576;
+       }
+
+       p.trgt_power += static_power[mt7601u_bbp_rr(dev, 1) & 0x03];
+
+       p.trgt_power += dev->ee->tssi_data.tx0_delta_offset;
+
+       dev_dbg(dev->dev,
+               "tssi:%02hhx t_power:%08x temp:%02hhx pkt_type:%02hhx\n",
+               p.tssi0, p.trgt_power, dev->raw_temp, pkt_type);
+
+       return p;
+}
+
+static bool mt7601u_tssi_read_ready(struct mt7601u_dev *dev)
+{
+       return !(mt7601u_bbp_rr(dev, 47) & 0x10);
+}
+
+static int mt7601u_tssi_cal(struct mt7601u_dev *dev)
+{
+       struct mt7601u_tssi_params params;
+       int curr_pwr, diff_pwr;
+       char tssi_offset;
+       s8 tssi_init;
+       s16 tssi_m_dc, tssi_db;
+       bool hvga;
+       u32 val;
+
+       if (!dev->ee->tssi_enabled)
+               return 0;
+
+       hvga = mt7601u_use_hvga(dev);
+       if (!dev->tssi_read_trig)
+               return mt7601u_mcu_tssi_read_kick(dev, hvga);
+
+       if (!mt7601u_tssi_read_ready(dev))
+               return 0;
+
+       params = mt7601u_tssi_params_get(dev);
+
+       tssi_init = (hvga ? dev->tssi_init_hvga : dev->tssi_init);
+       tssi_m_dc = params.tssi0 - tssi_init;
+       tssi_db = lin2dBd(tssi_m_dc);
+       dev_dbg(dev->dev, "tssi dc:%04hx db:%04hx hvga:%d\n",
+               tssi_m_dc, tssi_db, hvga);
+
+       if (dev->chandef.chan->hw_value < 5)
+               tssi_offset = dev->ee->tssi_data.offset[0];
+       else if (dev->chandef.chan->hw_value < 9)
+               tssi_offset = dev->ee->tssi_data.offset[1];
+       else
+               tssi_offset = dev->ee->tssi_data.offset[2];
+
+       if (hvga)
+               tssi_db -= dev->tssi_init_hvga_offset_db;
+
+       curr_pwr = tssi_db * dev->ee->tssi_data.slope + (tssi_offset << 9);
+       diff_pwr = params.trgt_power - curr_pwr;
+       dev_dbg(dev->dev, "Power curr:%08x diff:%08x\n", curr_pwr, diff_pwr);
+
+       if (params.tssi0 > 126 && diff_pwr > 0) {
+               dev_err(dev->dev, "Error: TSSI upper saturation\n");
+               diff_pwr = 0;
+       }
+       if (params.tssi0 - tssi_init < 1 && diff_pwr < 0) {
+               dev_err(dev->dev, "Error: TSSI lower saturation\n");
+               diff_pwr = 0;
+       }
+
+       if ((dev->prev_pwr_diff ^ diff_pwr) < 0 && abs(diff_pwr) < 4096 &&
+           (abs(diff_pwr) > abs(dev->prev_pwr_diff) ||
+            (diff_pwr > 0 && diff_pwr == -dev->prev_pwr_diff)))
+               diff_pwr = 0;
+       else
+               dev->prev_pwr_diff = diff_pwr;
+
+       diff_pwr += (diff_pwr > 0) ? 2048 : -2048;
+       diff_pwr /= 4096;
+
+       dev_dbg(dev->dev, "final diff: %08x\n", diff_pwr);
+
+       val = mt7601u_rr(dev, MT_TX_ALC_CFG_1);
+       curr_pwr = s6_to_int(MT76_GET(MT_TX_ALC_CFG_1_TEMP_COMP, val));
+       diff_pwr += curr_pwr;
+       val = (val & ~MT_TX_ALC_CFG_1_TEMP_COMP) | int_to_s6(diff_pwr);
+       mt7601u_wr(dev, MT_TX_ALC_CFG_1, val);
+
+       return mt7601u_mcu_tssi_read_kick(dev, hvga);
+}
+
+static u8 mt7601u_agc_default(struct mt7601u_dev *dev)
+{
+       return (dev->ee->lna_gain - 8) * 2 + 0x34;
+}
+
+static void mt7601u_agc_reset(struct mt7601u_dev *dev)
+{
+       u8 agc = mt7601u_agc_default(dev);
+
+       mt7601u_bbp_wr(dev, 66, agc);
+}
+
+void mt7601u_agc_save(struct mt7601u_dev *dev)
+{
+       dev->agc_save = mt7601u_bbp_rr(dev, 66);
+}
+
+void mt7601u_agc_restore(struct mt7601u_dev *dev)
+{
+       mt7601u_bbp_wr(dev, 66, dev->agc_save);
+}
+
+static void mt7601u_agc_tune(struct mt7601u_dev *dev)
+{
+       u8 val = mt7601u_agc_default(dev);
+
+       if (test_bit(MT7601U_STATE_SCANNING, &dev->state))
+               return;
+
+       /* Note: only in STA mode and not dozing; perhaps do this only if
+        *       there is enough rssi updates since last run?
+        *       Rssi updates are only on beacons and U2M so should work...
+        */
+       spin_lock_bh(&dev->con_mon_lock);
+       if (dev->avg_rssi <= -70)
+               val -= 0x20;
+       else if (dev->avg_rssi <= -60)
+               val -= 0x10;
+       spin_unlock_bh(&dev->con_mon_lock);
+
+       if (val != mt7601u_bbp_rr(dev, 66))
+               mt7601u_bbp_wr(dev, 66, val);
+
+       /* TODO: also if lost a lot of beacons try resetting
+        *       (see RTMPSetAGCInitValue() call in mlme.c).
+        */
+}
+
+static void mt7601u_phy_calibrate(struct work_struct *work)
+{
+       struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev,
+                                           cal_work.work);
+
+       mt7601u_agc_tune(dev);
+       mt7601u_tssi_cal(dev);
+       /* If TSSI calibration was run it already updated temperature. */
+       if (!dev->ee->tssi_enabled)
+               dev->raw_temp = mt7601u_read_temp(dev);
+       mt7601u_temp_comp(dev, true); /* TODO: find right value for @on */
+
+       ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
+                                    MT_CALIBRATE_INTERVAL);
+}
+
+static unsigned long
+__mt7601u_phy_freq_cal(struct mt7601u_dev *dev, s8 last_offset, u8 phy_mode)
+{
+       u8 activate_threshold, deactivate_threshold;
+
+       trace_freq_cal_offset(dev, phy_mode, last_offset);
+
+       /* No beacons received - reschedule soon */
+       if (last_offset == MT_FREQ_OFFSET_INVALID)
+               return MT_FREQ_CAL_ADJ_INTERVAL;
+
+       switch (phy_mode) {
+       case MT_PHY_TYPE_CCK:
+               activate_threshold = 19;
+               deactivate_threshold = 5;
+               break;
+       case MT_PHY_TYPE_OFDM:
+               activate_threshold = 102;
+               deactivate_threshold = 32;
+               break;
+       case MT_PHY_TYPE_HT:
+       case MT_PHY_TYPE_HT_GF:
+               activate_threshold = 82;
+               deactivate_threshold = 20;
+               break;
+       default:
+               WARN_ON(1);
+               return MT_FREQ_CAL_CHECK_INTERVAL;
+       }
+
+       if (abs(last_offset) >= activate_threshold)
+               dev->freq_cal.adjusting = true;
+       else if (abs(last_offset) <= deactivate_threshold)
+               dev->freq_cal.adjusting = false;
+
+       if (!dev->freq_cal.adjusting)
+               return MT_FREQ_CAL_CHECK_INTERVAL;
+
+       if (last_offset > deactivate_threshold) {
+               if (dev->freq_cal.freq > 0)
+                       dev->freq_cal.freq--;
+               else
+                       dev->freq_cal.adjusting = false;
+       } else if (last_offset < -deactivate_threshold) {
+               if (dev->freq_cal.freq < 0xbf)
+                       dev->freq_cal.freq++;
+               else
+                       dev->freq_cal.adjusting = false;
+       }
+
+       trace_freq_cal_adjust(dev, dev->freq_cal.freq);
+       mt7601u_rf_wr(dev, 0, 12, dev->freq_cal.freq);
+       mt7601u_vco_cal(dev);
+
+       return dev->freq_cal.adjusting ? MT_FREQ_CAL_ADJ_INTERVAL :
+                                        MT_FREQ_CAL_CHECK_INTERVAL;
+}
+
+static void mt7601u_phy_freq_cal(struct work_struct *work)
+{
+       struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev,
+                                              freq_cal.work.work);
+       s8 last_offset;
+       u8 phy_mode;
+       unsigned long delay;
+
+       spin_lock_bh(&dev->con_mon_lock);
+       last_offset = dev->bcn_freq_off;
+       phy_mode = dev->bcn_phy_mode;
+       spin_unlock_bh(&dev->con_mon_lock);
+
+       delay = __mt7601u_phy_freq_cal(dev, last_offset, phy_mode);
+       ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, delay);
+
+       spin_lock_bh(&dev->con_mon_lock);
+       dev->bcn_freq_off = MT_FREQ_OFFSET_INVALID;
+       spin_unlock_bh(&dev->con_mon_lock);
+}
+
+void mt7601u_phy_con_cal_onoff(struct mt7601u_dev *dev,
+                              struct ieee80211_bss_conf *info)
+{
+       if (!info->assoc)
+               cancel_delayed_work_sync(&dev->freq_cal.work);
+
+       /* Start/stop collecting beacon data */
+       spin_lock_bh(&dev->con_mon_lock);
+       ether_addr_copy(dev->ap_bssid, info->bssid);
+       dev->avg_rssi = 0;
+       dev->bcn_freq_off = MT_FREQ_OFFSET_INVALID;
+       spin_unlock_bh(&dev->con_mon_lock);
+
+       dev->freq_cal.freq = dev->ee->rf_freq_off;
+       dev->freq_cal.enabled = info->assoc;
+       dev->freq_cal.adjusting = false;
+
+       if (info->assoc)
+               ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work,
+                                            MT_FREQ_CAL_INIT_DELAY);
+}
+
+static int mt7601u_init_cal(struct mt7601u_dev *dev)
+{
+       u32 mac_ctrl;
+       int ret;
+
+       dev->raw_temp = mt7601u_read_bootup_temp(dev);
+       dev->curr_temp = (dev->raw_temp - dev->ee->ref_temp) *
+               MT_EE_TEMPERATURE_SLOPE;
+       dev->dpd_temp = dev->curr_temp;
+
+       mac_ctrl = mt7601u_rr(dev, MT_MAC_SYS_CTRL);
+
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_R, 0);
+       if (ret)
+               return ret;
+
+       ret = mt7601u_rf_rr(dev, 0, 4);
+       if (ret < 0)
+               return ret;
+       ret |= 0x80;
+       ret = mt7601u_rf_wr(dev, 0, 4, ret);
+       if (ret)
+               return ret;
+       msleep(2);
+
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_TXDCOC, 0);
+       if (ret)
+               return ret;
+
+       mt7601u_rxdc_cal(dev);
+
+       ret = mt7601u_set_bw_filter(dev, true);
+       if (ret)
+               return ret;
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_LOFT, 0);
+       if (ret)
+               return ret;
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_TXIQ, 0);
+       if (ret)
+               return ret;
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_RXIQ, 0);
+       if (ret)
+               return ret;
+       ret = mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->dpd_temp);
+       if (ret)
+               return ret;
+
+       mt7601u_rxdc_cal(dev);
+
+       mt7601u_tssi_dc_gain_cal(dev);
+
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, mac_ctrl);
+
+       mt7601u_temp_comp(dev, true);
+
+       return 0;
+}
+
+int mt7601u_bbp_set_bw(struct mt7601u_dev *dev, int bw)
+{
+       u32 val, old;
+
+       if (bw == dev->bw) {
+               /* Vendor driver does the rmc even when no change is needed. */
+               mt7601u_bbp_rmc(dev, 4, 0x18, bw == MT_BW_20 ? 0 : 0x10);
+
+               return 0;
+       }
+       dev->bw = bw;
+
+       /* Stop MAC for the time of bw change */
+       old = mt7601u_rr(dev, MT_MAC_SYS_CTRL);
+       val = old & ~(MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, val);
+       mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX | MT_MAC_STATUS_RX,
+                 0, 500000);
+
+       mt7601u_bbp_rmc(dev, 4, 0x18, bw == MT_BW_20 ? 0 : 0x10);
+
+       mt7601u_wr(dev, MT_MAC_SYS_CTRL, old);
+
+       return mt7601u_load_bbp_temp_table_bw(dev);
+}
+
+/**
+ * mt7601u_set_rx_path - set rx path in BBP
+ * @dev:       pointer to adapter structure
+ * @path:      rx path to set values are 0-based
+ */
+void mt7601u_set_rx_path(struct mt7601u_dev *dev, u8 path)
+{
+       mt7601u_bbp_rmw(dev, 3, 0x18, path << 3);
+}
+
+/**
+ * mt7601u_set_tx_dac - set which tx DAC to use
+ * @dev:       pointer to adapter structure
+ * @path:      DAC index, values are 0-based
+ */
+void mt7601u_set_tx_dac(struct mt7601u_dev *dev, u8 dac)
+{
+       mt7601u_bbp_rmc(dev, 1, 0x18, dac << 3);
+}
+
+int mt7601u_phy_init(struct mt7601u_dev *dev)
+{
+       int ret;
+
+       dev->rf_pa_mode[0] = mt7601u_rr(dev, MT_RF_PA_MODE_CFG0);
+       dev->rf_pa_mode[1] = mt7601u_rr(dev, MT_RF_PA_MODE_CFG1);
+
+       ret = mt7601u_rf_wr(dev, 0, 12, dev->ee->rf_freq_off);
+       if (ret)
+               return ret;
+       ret = mt7601u_write_reg_pairs(dev, 0, rf_central,
+                                     ARRAY_SIZE(rf_central));
+       if (ret)
+               return ret;
+       ret = mt7601u_write_reg_pairs(dev, 0, rf_channel,
+                                     ARRAY_SIZE(rf_channel));
+       if (ret)
+               return ret;
+       ret = mt7601u_write_reg_pairs(dev, 0, rf_vga, ARRAY_SIZE(rf_vga));
+       if (ret)
+               return ret;
+
+       ret = mt7601u_init_cal(dev);
+       if (ret)
+               return ret;
+
+       dev->prev_pwr_diff = 100;
+
+       INIT_DELAYED_WORK(&dev->cal_work, mt7601u_phy_calibrate);
+       INIT_DELAYED_WORK(&dev->freq_cal.work, mt7601u_phy_freq_cal);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/regs.h b/drivers/net/wireless/mediatek/mt7601u/regs.h
new file mode 100644 (file)
index 0000000..afd8978
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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_REGS_H
+#define __MT76_REGS_H
+
+#include <linux/bitops.h>
+
+#ifndef GENMASK
+#define GENMASK(h, l)       (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#endif
+
+#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_FCE_DMA_ADDR                        0x0230
+#define MT_FCE_DMA_LEN                 0x0234
+
+#define MT_USB_DMA_CFG                 0x238
+#define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT        GENMASK(7, 0)
+#define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8)
+#define MT_USB_DMA_CFG_PHY_CLR         BIT(16)
+#define MT_USB_DMA_CFG_TX_CLR          BIT(19)
+#define MT_USB_DMA_CFG_TXOP_HALT       BIT(20)
+#define MT_USB_DMA_CFG_RX_BULK_AGG_EN  BIT(21)
+#define MT_USB_DMA_CFG_RX_BULK_EN      BIT(22)
+#define MT_USB_DMA_CFG_TX_BULK_EN      BIT(23)
+#define MT_USB_DMA_CFG_UDMA_RX_WL_DROP BIT(25)
+#define MT_USB_DMA_CFG_EP_OUT_VALID    GENMASK(29, 27)
+#define MT_USB_DMA_CFG_RX_BUSY         BIT(30)
+#define MT_USB_DMA_CFG_TX_BUSY         BIT(31)
+
+#define MT_TSO_CTRL                    0x0250
+#define MT_HEADER_TRANS_CTRL_REG       0x0260
+
+#define MT_US_CYC_CFG                  0x02a4
+#define MT_US_CYC_CNT                  GENMASK(7, 0)
+
+#define MT_TX_RING_BASE                        0x0300
+#define MT_RX_RING_BASE                        0x03c0
+#define MT_RING_SIZE                   0x10
+
+#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_CSR_CFG                   0x0500
+#define MT_RF_CSR_CFG_DATA             GENMASK(7, 0)
+#define MT_RF_CSR_CFG_REG_ID           GENMASK(13, 8)
+#define MT_RF_CSR_CFG_REG_BANK         GENMASK(17, 14)
+#define MT_RF_CSR_CFG_WR               BIT(30)
+#define MT_RF_CSR_CFG_KICK             BIT(31)
+
+#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_TX_CPU_FROM_FCE_BASE_PTR    0x09a0
+#define MT_TX_CPU_FROM_FCE_MAX_COUNT   0x09a4
+#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX        0x09a8
+
+#define MT_FCE_PDMA_GLOBAL_CONF                0x09c4
+
+#define MT_PAUSE_ENABLE_CONTROL1       0x0a38
+
+#define MT_FCE_SKIP_FS                 0x0a6c
+
+#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_ADDR_DW1_U2ME_MASK      GENMASK(23, 16)
+
+#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_MAX_LEN_CFG_AMPDU           GENMASK(13, 12)
+
+#define MT_BBP_CSR_CFG                 0x101c
+#define MT_BBP_CSR_CFG_VAL             GENMASK(7, 0)
+#define MT_BBP_CSR_CFG_REG_NUM         GENMASK(15, 8)
+#define MT_BBP_CSR_CFG_READ            BIT(16)
+#define MT_BBP_CSR_CFG_BUSY            BIT(17)
+#define MT_BBP_CSR_CFG_PAR_DUR         BIT(18)
+#define MT_BBP_CSR_CFG_RW_MODE         BIT(19)
+
+#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_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_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_TXOP_TRUN_EN                        GENMASK(5, 0)
+#define MT_TXOP_EXT_CCA_DLY            GENMASK(15, 8)
+#define MT_TXOP_CTRL
+
+#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_RETRY_CFG                        0x134c
+#define MT_TX_LINK_CFG                 0x1350
+#define MT_HT_FBK_CFG0                 0x1354
+#define MT_HT_FBK_CFG1                 0x1358
+#define MT_LG_FBK_CFG0                 0x135c
+#define MT_LG_FBK_CFG1                 0x1360
+
+#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_PROT_RATE                   GENMASK(15, 0)
+#define MT_PROT_CTRL_RTS_CTS           BIT(16)
+#define MT_PROT_CTRL_CTS2SELF          BIT(17)
+#define MT_PROT_NAV_SHORT              BIT(18)
+#define MT_PROT_NAV_LONG               BIT(19)
+#define MT_PROT_TXOP_ALLOW_CCK         BIT(20)
+#define MT_PROT_TXOP_ALLOW_OFDM                BIT(21)
+#define MT_PROT_TXOP_ALLOW_MM20                BIT(22)
+#define MT_PROT_TXOP_ALLOW_MM40                BIT(23)
+#define MT_PROT_TXOP_ALLOW_GF20                BIT(24)
+#define MT_PROT_TXOP_ALLOW_GF40                BIT(25)
+#define MT_PROT_RTS_THR_EN             BIT(26)
+#define MT_PROT_RATE_CCK_11            0x0003
+#define MT_PROT_RATE_OFDM_6            0x4000
+#define MT_PROT_RATE_OFDM_24           0x4004
+#define MT_PROT_RATE_DUP_OFDM_24       0x4084
+#define MT_PROT_TXOP_ALLOW_ALL         GENMASK(25, 20)
+#define MT_PROT_TXOP_ALLOW_BW20                (MT_PROT_TXOP_ALLOW_ALL &       \
+                                        ~MT_PROT_TXOP_ALLOW_MM40 &     \
+                                        ~MT_PROT_TXOP_ALLOW_GF40)
+
+#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_TX0_RF_GAIN_ATTEN           0x13a8
+
+#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_TX0_BB_GAIN_ATTEN           0x13c0
+
+#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_AUTO_RSP_CFG                        0x1404
+
+#define MT_AUTO_RSP_PREAMB_SHORT       BIT(4)
+
+#define MT_LEGACY_BASIC_RATE           0x1408
+#define MT_HT_BASIC_RATE               0x140c
+
+#define MT_RX_PARSER_CFG               0x1418
+#define MT_RX_PARSER_RX_SET_NAV_ALL    BIT(0)
+
+#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_RX_STA_CNT0                 0x1700
+#define MT_RX_STA_CNT1                 0x1704
+#define MT_RX_STA_CNT2                 0x1708
+#define MT_TX_STA_CNT0                 0x170c
+#define MT_TX_STA_CNT1                 0x1710
+#define MT_TX_STA_CNT2                 0x1714
+
+/* Vendor driver defines content of the second word of STAT_FIFO as follows:
+ *     MT_TX_STAT_FIFO_RATE            GENMASK(26, 16)
+ *     MT_TX_STAT_FIFO_ETXBF           BIT(27)
+ *     MT_TX_STAT_FIFO_SND             BIT(28)
+ *     MT_TX_STAT_FIFO_ITXBF           BIT(29)
+ * However, tests show that b16-31 have the same layout as TXWI rate_ctl
+ * with rate set to rate at which frame was acked.
+ */
+#define MT_TX_STAT_FIFO                        0x1718
+#define MT_TX_STAT_FIFO_VALID          BIT(0)
+#define MT_TX_STAT_FIFO_PID_TYPE       GENMASK(4, 1)
+#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_STAT                 0x171c
+
+#define MT_TX_AGG_CNT_BASE0            0x1720
+
+#define MT_MPDU_DENSITY_CNT            0x1740
+
+#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_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)
+
+enum mt76_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/mt7601u/trace.c b/drivers/net/wireless/mediatek/mt7601u/trace.c
new file mode 100644 (file)
index 0000000..8abdd3c
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/trace.h b/drivers/net/wireless/mediatek/mt7601u/trace.h
new file mode 100644 (file)
index 0000000..2898973
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#if !defined(__MT7601U_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __MT7601U_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "mt7601u.h"
+#include "mac.h"
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mt7601u
+
+#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 mt7601u_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_read,
+       TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val),
+       TP_ARGS(dev, reg, val)
+);
+
+DEFINE_EVENT(dev_reg_evt, reg_write,
+       TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val),
+       TP_ARGS(dev, reg, val)
+);
+
+TRACE_EVENT(mt_submit_urb,
+       TP_PROTO(struct mt7601u_dev *dev, struct urb *u),
+       TP_ARGS(dev, u),
+       TP_STRUCT__entry(
+               DEV_ENTRY __field(unsigned, pipe) __field(u32, len)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->pipe = u->pipe;
+               __entry->len = u->transfer_buffer_length;
+       ),
+       TP_printk(DEV_PR_FMT "p:%08x len:%u",
+                 DEV_PR_ARG, __entry->pipe, __entry->len)
+);
+
+#define trace_mt_submit_urb_sync(__dev, __pipe, __len) ({      \
+       struct urb u;                                   \
+       u.pipe = __pipe;                                \
+       u.transfer_buffer_length = __len;               \
+       trace_mt_submit_urb(__dev, &u);                 \
+})
+
+TRACE_EVENT(mt_mcu_msg_send,
+       TP_PROTO(struct mt7601u_dev *dev,
+                struct sk_buff *skb, u32 csum, bool resp),
+       TP_ARGS(dev, skb, csum, resp),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u32, info)
+               __field(u32, csum)
+               __field(bool, resp)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->info = *(u32 *)skb->data;
+               __entry->csum = csum;
+               __entry->resp = resp;
+       ),
+       TP_printk(DEV_PR_FMT "i:%08x c:%08x r:%d",
+                 DEV_PR_ARG, __entry->info, __entry->csum, __entry->resp)
+);
+
+TRACE_EVENT(mt_vend_req,
+       TP_PROTO(struct mt7601u_dev *dev, unsigned pipe, u8 req, u8 req_type,
+                u16 val, u16 offset, void *buf, size_t buflen, int ret),
+       TP_ARGS(dev, pipe, req, req_type, val, offset, buf, buflen, ret),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(unsigned, pipe) __field(u8, req) __field(u8, req_type)
+               __field(u16, val) __field(u16, offset) __field(void*, buf)
+               __field(int, buflen) __field(int, ret)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->pipe = pipe;
+               __entry->req = req;
+               __entry->req_type = req_type;
+               __entry->val = val;
+               __entry->offset = offset;
+               __entry->buf = buf;
+               __entry->buflen = buflen;
+               __entry->ret = ret;
+       ),
+       TP_printk(DEV_PR_FMT
+                 "%d p:%08x req:%02hhx %02hhx val:%04hx %04hx buf:%d %d",
+                 DEV_PR_ARG, __entry->ret, __entry->pipe, __entry->req,
+                 __entry->req_type, __entry->val, __entry->offset,
+                 !!__entry->buf, __entry->buflen)
+);
+
+TRACE_EVENT(ee_read,
+       TP_PROTO(struct mt7601u_dev *dev, int offset, u16 val),
+       TP_ARGS(dev, offset, val),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(int, o) __field(u16, v)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->o = offset;
+               __entry->v = val;
+       ),
+       TP_printk(DEV_PR_FMT "%04x=%04x", DEV_PR_ARG, __entry->o, __entry->v)
+);
+
+DECLARE_EVENT_CLASS(dev_rf_reg_evt,
+       TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val),
+       TP_ARGS(dev, bank, reg, val),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, bank)
+               __field(u8, reg)
+               __field(u8, val)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               REG_ASSIGN;
+               __entry->bank = bank;
+       ),
+       TP_printk(
+               DEV_PR_FMT "%02hhx:%02hhx=%02hhx",
+               DEV_PR_ARG, __entry->bank, __entry->reg, __entry->val
+       )
+);
+
+DEFINE_EVENT(dev_rf_reg_evt, rf_read,
+       TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val),
+       TP_ARGS(dev, bank, reg, val)
+);
+
+DEFINE_EVENT(dev_rf_reg_evt, rf_write,
+       TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val),
+       TP_ARGS(dev, bank, reg, val)
+);
+
+DECLARE_EVENT_CLASS(dev_bbp_reg_evt,
+       TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val),
+       TP_ARGS(dev, reg, val),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, reg)
+               __field(u8, val)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               REG_ASSIGN;
+       ),
+       TP_printk(
+               DEV_PR_FMT "%02hhx=%02hhx",
+               DEV_PR_ARG, __entry->reg, __entry->val
+       )
+);
+
+DEFINE_EVENT(dev_bbp_reg_evt, bbp_read,
+       TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val),
+       TP_ARGS(dev, reg, val)
+);
+
+DEFINE_EVENT(dev_bbp_reg_evt, bbp_write,
+       TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val),
+       TP_ARGS(dev, reg, val)
+);
+
+DECLARE_EVENT_CLASS(dev_simple_evt,
+       TP_PROTO(struct mt7601u_dev *dev, u8 val),
+       TP_ARGS(dev, val),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, val)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->val = val;
+       ),
+       TP_printk(
+               DEV_PR_FMT "%02hhx", DEV_PR_ARG, __entry->val
+       )
+);
+
+DEFINE_EVENT(dev_simple_evt, temp_mode,
+       TP_PROTO(struct mt7601u_dev *dev, u8 val),
+       TP_ARGS(dev, val)
+);
+
+DEFINE_EVENT(dev_simple_evt, read_temp,
+       TP_PROTO(struct mt7601u_dev *dev, u8 val),
+       TP_ARGS(dev, val)
+);
+
+DEFINE_EVENT(dev_simple_evt, freq_cal_adjust,
+       TP_PROTO(struct mt7601u_dev *dev, u8 val),
+       TP_ARGS(dev, val)
+);
+
+TRACE_EVENT(freq_cal_offset,
+       TP_PROTO(struct mt7601u_dev *dev, u8 phy_mode, s8 freq_off),
+       TP_ARGS(dev, phy_mode, freq_off),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, phy_mode)
+               __field(s8, freq_off)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->phy_mode = phy_mode;
+               __entry->freq_off = freq_off;
+       ),
+       TP_printk(DEV_PR_FMT "phy:%02hhx off:%02hhx",
+                 DEV_PR_ARG, __entry->phy_mode, __entry->freq_off)
+);
+
+TRACE_EVENT(mt_rx,
+       TP_PROTO(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi, u32 f),
+       TP_ARGS(dev, rxwi, f),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field_struct(struct mt7601u_rxwi, rxwi)
+               __field(u32, fce_info)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->rxwi = *rxwi;
+               __entry->fce_info = f;
+       ),
+       TP_printk(DEV_PR_FMT "rxi:%08x ctl:%08x frag_sn:%04hx rate:%04hx "
+                 "uknw:%02hhx z:%02hhx%02hhx%02hhx snr:%02hhx "
+                 "ant:%02hhx gain:%02hhx freq_o:%02hhx "
+                 "r:%08x ea:%08x fce:%08x", DEV_PR_ARG,
+                 le32_to_cpu(__entry->rxwi.rxinfo),
+                 le32_to_cpu(__entry->rxwi.ctl),
+                 le16_to_cpu(__entry->rxwi.frag_sn),
+                 le16_to_cpu(__entry->rxwi.rate),
+                 __entry->rxwi.unknown,
+                 __entry->rxwi.zero[0], __entry->rxwi.zero[1],
+                 __entry->rxwi.zero[2],
+                 __entry->rxwi.snr, __entry->rxwi.ant,
+                 __entry->rxwi.gain, __entry->rxwi.freq_off,
+                 __entry->rxwi.resv2, __entry->rxwi.expert_ant,
+                 __entry->fce_info)
+);
+
+TRACE_EVENT(mt_tx,
+       TP_PROTO(struct mt7601u_dev *dev, struct sk_buff *skb,
+                struct mt76_sta *sta, struct mt76_txwi *h),
+       TP_ARGS(dev, skb, sta, h),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field_struct(struct mt76_txwi, h)
+               __field(struct sk_buff *, skb)
+               __field(struct mt76_sta *, sta)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->h = *h;
+               __entry->skb = skb;
+               __entry->sta = sta;
+       ),
+       TP_printk(DEV_PR_FMT "skb:%p sta:%p  flg:%04hx rate_ctl:%04hx "
+                 "ack:%02hhx wcid:%02hhx len_ctl:%05hx", DEV_PR_ARG,
+                 __entry->skb, __entry->sta,
+                 le16_to_cpu(__entry->h.flags),
+                 le16_to_cpu(__entry->h.rate_ctl),
+                 __entry->h.ack_ctl, __entry->h.wcid,
+                 le16_to_cpu(__entry->h.len_ctl))
+);
+
+TRACE_EVENT(mt_tx_dma_done,
+       TP_PROTO(struct mt7601u_dev *dev, struct sk_buff *skb),
+       TP_ARGS(dev, skb),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(struct sk_buff *, skb)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->skb = skb;
+       ),
+       TP_printk(DEV_PR_FMT "%p", DEV_PR_ARG, __entry->skb)
+);
+
+TRACE_EVENT(mt_tx_status_cleaned,
+       TP_PROTO(struct mt7601u_dev *dev, int cleaned),
+       TP_ARGS(dev, cleaned),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(int, cleaned)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->cleaned = cleaned;
+       ),
+       TP_printk(DEV_PR_FMT "%d", DEV_PR_ARG, __entry->cleaned)
+);
+
+TRACE_EVENT(mt_tx_status,
+       TP_PROTO(struct mt7601u_dev *dev, u32 stat1, u32 stat2),
+       TP_ARGS(dev, stat1, stat2),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u32, stat1)     __field(u32, stat2)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->stat1 = stat1;
+               __entry->stat2 = stat2;
+       ),
+       TP_printk(DEV_PR_FMT "%08x %08x",
+                 DEV_PR_ARG, __entry->stat1, __entry->stat2)
+);
+
+TRACE_EVENT(mt_rx_dma_aggr,
+       TP_PROTO(struct mt7601u_dev *dev, int cnt, bool paged),
+       TP_ARGS(dev, cnt, paged),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, cnt)
+               __field(bool, paged)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->cnt = cnt;
+               __entry->paged = paged;
+       ),
+       TP_printk(DEV_PR_FMT "cnt:%d paged:%d",
+                 DEV_PR_ARG, __entry->cnt, __entry->paged)
+);
+
+DEFINE_EVENT(dev_simple_evt, set_key,
+       TP_PROTO(struct mt7601u_dev *dev, u8 val),
+       TP_ARGS(dev, val)
+);
+
+TRACE_EVENT(set_shared_key,
+       TP_PROTO(struct mt7601u_dev *dev, u8 vid, u8 key),
+       TP_ARGS(dev, vid, key),
+       TP_STRUCT__entry(
+               DEV_ENTRY
+               __field(u8, vid)
+               __field(u8, key)
+       ),
+       TP_fast_assign(
+               DEV_ASSIGN;
+               __entry->vid = vid;
+               __entry->key = key;
+       ),
+       TP_printk(DEV_PR_FMT "phy:%02hhx off:%02hhx",
+                 DEV_PR_ARG, __entry->vid, __entry->key)
+);
+
+#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/mt7601u/tx.c b/drivers/net/wireless/mediatek/mt7601u/tx.c
new file mode 100644 (file)
index 0000000..0be2080
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+#include "trace.h"
+
+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_MAX
+};
+
+/* Hardware uses mirrored order of queues with Q0 having the highest priority */
+static u8 q2hwq(u8 q)
+{
+       return q ^ 0x3;
+}
+
+/* Take mac80211 Q id from the skb and translate it to hardware Q id */
+static u8 skb2q(struct sk_buff *skb)
+{
+       int qid = skb_get_queue_mapping(skb);
+
+       if (WARN_ON(qid >= MT_TXQ_PSD)) {
+               qid = MT_TXQ_BE;
+               skb_set_queue_mapping(skb, qid);
+       }
+
+       return q2hwq(qid);
+}
+
+/* Note: TX retry reporting is a bit broken.
+ *      Retries are reported only once per AMPDU and often come a frame early
+ *      i.e. they are reported in the last status preceding the AMPDU. Apart
+ *      from the fact that it's hard to know the length of the AMPDU (which is
+ *      required to know to how many consecutive frames retries should be
+ *      applied), if status comes early on full FIFO it gets lost and retries
+ *      of the whole AMPDU become invisible.
+ *      As a work-around encode the desired rate in PKT_ID of TX descriptor
+ *      and based on that guess the retries (every rate is tried once).
+ *      Only downside here is that for MCS0 we have to rely solely on
+ *      transmission failures as no retries can ever be reported.
+ *      Not having to read EXT_FIFO has a nice effect of doubling the number
+ *      of reports which can be fetched.
+ *      Also the vendor driver never uses the EXT_FIFO register so it may be
+ *      undertested.
+ */
+static u8 mt7601u_tx_pktid_enc(struct mt7601u_dev *dev, u8 rate, bool is_probe)
+{
+       u8 encoded = (rate + 1) + is_probe *  8;
+
+       /* Because PKT_ID 0 disables status reporting only 15 values are
+        * available but 16 are needed (8 MCS * 2 for encoding is_probe)
+        * - we need to cram together two rates. MCS0 and MCS7 with is_probe
+        * share PKT_ID 9.
+        */
+       if (is_probe && rate == 7)
+               return encoded - 7;
+
+       return encoded;
+}
+
+static void
+mt7601u_tx_pktid_dec(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
+{
+       u8 req_rate = stat->pktid;
+       u8 eff_rate = stat->rate & 0x7;
+
+       req_rate -= 1;
+
+       if (req_rate > 7) {
+               stat->is_probe = true;
+               req_rate -= 8;
+
+               /* Decide between MCS0 and MCS7 which share pktid 9 */
+               if (!req_rate && eff_rate)
+                       req_rate = 7;
+       }
+
+       stat->retry = req_rate - eff_rate;
+}
+
+static void mt7601u_tx_skb_remove_dma_overhead(struct sk_buff *skb,
+                                              struct ieee80211_tx_info *info)
+{
+       int pkt_len = (unsigned long)info->status.status_driver_data[0];
+
+       skb_pull(skb, sizeof(struct mt76_txwi) + 4);
+       if (ieee80211_get_hdrlen_from_skb(skb) % 4)
+               mt76_remove_hdr_pad(skb);
+
+       skb_trim(skb, pkt_len);
+}
+
+void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       mt7601u_tx_skb_remove_dma_overhead(skb, info);
+
+       ieee80211_tx_info_clear_status(info);
+       info->status.rates[0].idx = -1;
+       info->flags |= IEEE80211_TX_STAT_ACK;
+       ieee80211_tx_status(dev->hw, skb);
+}
+
+static int mt7601u_skb_rooms(struct mt7601u_dev *dev, struct sk_buff *skb)
+{
+       int hdr_len = ieee80211_get_hdrlen_from_skb(skb);
+       u32 need_head;
+
+       need_head = sizeof(struct mt76_txwi) + 4;
+       if (hdr_len % 4)
+               need_head += 2;
+
+       return skb_cow(skb, need_head);
+}
+
+static struct mt76_txwi *
+mt7601u_push_txwi(struct mt7601u_dev *dev, struct sk_buff *skb,
+                 struct ieee80211_sta *sta, struct mt76_wcid *wcid,
+                 int pkt_len)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_rate *rate = &info->control.rates[0];
+       struct mt76_txwi *txwi;
+       unsigned long flags;
+       bool is_probe;
+       u32 pkt_id;
+       u16 rate_ctl;
+       u8 nss;
+
+       txwi = (struct mt76_txwi *)skb_push(skb, sizeof(struct mt76_txwi));
+       memset(txwi, 0, sizeof(*txwi));
+
+       if (!wcid->tx_rate_set)
+               ieee80211_get_tx_rates(info->control.vif, sta, skb,
+                                      info->control.rates, 1);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (rate->idx < 0 || !rate->count)
+               rate_ctl = wcid->tx_rate;
+       else
+               rate_ctl = mt76_mac_tx_rate_val(dev, rate, &nss);
+       spin_unlock_irqrestore(&dev->lock, flags);
+       txwi->rate_ctl = cpu_to_le16(rate_ctl);
+
+       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_AMPDU) && sta) {
+               u8 ba_size = IEEE80211_MIN_AMPDU_BUF;
+
+               ba_size <<= sta->ht_cap.ampdu_factor;
+               ba_size = min_t(int, 63, ba_size);
+               if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+                       ba_size = 0;
+               txwi->ack_ctl |= MT76_SET(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size);
+
+               txwi->flags = cpu_to_le16(MT_TXWI_FLAGS_AMPDU |
+                                         MT76_SET(MT_TXWI_FLAGS_MPDU_DENSITY,
+                                                  sta->ht_cap.ampdu_density));
+               if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+                       txwi->flags = 0;
+       }
+
+       txwi->wcid = wcid->idx;
+
+       is_probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+       pkt_id = mt7601u_tx_pktid_enc(dev, rate_ctl & 0x7, is_probe);
+       pkt_len |= MT76_SET(MT_TXWI_LEN_PKTID, pkt_id);
+       txwi->len_ctl = cpu_to_le16(pkt_len);
+
+       return txwi;
+}
+
+void mt7601u_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
+               struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct mt7601u_dev *dev = hw->priv;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ieee80211_sta *sta = control->sta;
+       struct mt76_sta *msta = NULL;
+       struct mt76_wcid *wcid = dev->mon_wcid;
+       struct mt76_txwi *txwi;
+       int pkt_len = skb->len;
+       int hw_q = skb2q(skb);
+
+       BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
+       info->status.status_driver_data[0] = (void *)(unsigned long)pkt_len;
+
+       if (mt7601u_skb_rooms(dev, skb) || mt76_insert_hdr_pad(skb)) {
+               ieee80211_free_txskb(dev->hw, skb);
+               return;
+       }
+
+       if (sta) {
+               msta = (struct mt76_sta *) sta->drv_priv;
+               wcid = &msta->wcid;
+       } else if (vif) {
+               struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+
+               wcid = &mvif->group_wcid;
+       }
+
+       txwi = mt7601u_push_txwi(dev, skb, sta, wcid, pkt_len);
+
+       if (mt7601u_dma_enqueue_tx(dev, skb, wcid, hw_q))
+               return;
+
+       trace_mt_tx(dev, skb, msta, txwi);
+}
+
+void mt7601u_tx_stat(struct work_struct *work)
+{
+       struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev,
+                                              stat_work.work);
+       struct mt76_tx_status stat;
+       unsigned long flags;
+       int cleaned = 0;
+
+       while (!test_bit(MT7601U_STATE_REMOVED, &dev->state)) {
+               stat = mt7601u_mac_fetch_tx_status(dev);
+               if (!stat.valid)
+                       break;
+
+               mt7601u_tx_pktid_dec(dev, &stat);
+               mt76_send_tx_status(dev, &stat);
+
+               cleaned++;
+       }
+       trace_mt_tx_status_cleaned(dev, cleaned);
+
+       spin_lock_irqsave(&dev->tx_lock, flags);
+       if (cleaned)
+               queue_delayed_work(dev->stat_wq, &dev->stat_work,
+                                  msecs_to_jiffies(10));
+       else if (test_and_clear_bit(MT7601U_STATE_MORE_STATS, &dev->state))
+               queue_delayed_work(dev->stat_wq, &dev->stat_work,
+                                  msecs_to_jiffies(20));
+       else
+               clear_bit(MT7601U_STATE_READING_STATS, &dev->state);
+       spin_unlock_irqrestore(&dev->tx_lock, flags);
+}
+
+int mt7601u_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                   u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+       struct mt7601u_dev *dev = hw->priv;
+       u8 cw_min = 5, cw_max = 10, hw_q = q2hwq(queue);
+       u32 val;
+
+       /* TODO: should we do funny things with the parameters?
+        *       See what mt7601u_set_default_edca() used to do in init.c.
+        */
+
+       if (params->cw_min)
+               cw_min = fls(params->cw_min);
+       if (params->cw_max)
+               cw_max = fls(params->cw_max);
+
+       WARN_ON(params->txop > 0xff);
+       WARN_ON(params->aifs > 0xf);
+       WARN_ON(cw_min > 0xf);
+       WARN_ON(cw_max > 0xf);
+
+       val = MT76_SET(MT_EDCA_CFG_AIFSN, params->aifs) |
+             MT76_SET(MT_EDCA_CFG_CWMIN, cw_min) |
+             MT76_SET(MT_EDCA_CFG_CWMAX, cw_max);
+       /* TODO: based on user-controlled EnableTxBurst var vendor drv sets
+        *       a really long txop on AC0 (see connect.c:2009) but only on
+        *       connect? When not connected should be 0.
+        */
+       if (!hw_q)
+               val |= 0x60;
+       else
+               val |= MT76_SET(MT_EDCA_CFG_TXOP, params->txop);
+       mt76_wr(dev, MT_EDCA_CFG_AC(hw_q), val);
+
+       val = mt76_rr(dev, MT_WMM_TXOP(hw_q));
+       val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(hw_q));
+       val |= params->txop << MT_WMM_TXOP_SHIFT(hw_q);
+       mt76_wr(dev, MT_WMM_TXOP(hw_q), val);
+
+       val = mt76_rr(dev, MT_WMM_AIFSN);
+       val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(hw_q));
+       val |= params->aifs << MT_WMM_AIFSN_SHIFT(hw_q);
+       mt76_wr(dev, MT_WMM_AIFSN, val);
+
+       val = mt76_rr(dev, MT_WMM_CWMIN);
+       val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(hw_q));
+       val |= cw_min << MT_WMM_CWMIN_SHIFT(hw_q);
+       mt76_wr(dev, MT_WMM_CWMIN, val);
+
+       val = mt76_rr(dev, MT_WMM_CWMAX);
+       val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(hw_q));
+       val |= cw_max << MT_WMM_CWMAX_SHIFT(hw_q);
+       mt76_wr(dev, MT_WMM_CWMAX, val);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
new file mode 100644 (file)
index 0000000..99e2b39
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "mt7601u.h"
+#include "usb.h"
+#include "trace.h"
+
+static struct usb_device_id mt7601u_device_table[] = {
+       { USB_DEVICE(0x0b05, 0x17d3) },
+       { USB_DEVICE(0x0e8d, 0x760a) },
+       { USB_DEVICE(0x0e8d, 0x760b) },
+       { USB_DEVICE(0x13d3, 0x3431) },
+       { USB_DEVICE(0x13d3, 0x3434) },
+       { USB_DEVICE(0x148f, 0x7601) },
+       { USB_DEVICE(0x148f, 0x760a) },
+       { USB_DEVICE(0x148f, 0x760b) },
+       { USB_DEVICE(0x148f, 0x760c) },
+       { USB_DEVICE(0x148f, 0x760d) },
+       { USB_DEVICE(0x2001, 0x3d04) },
+       { USB_DEVICE(0x2717, 0x4106) },
+       { USB_DEVICE(0x2955, 0x0001) },
+       { USB_DEVICE(0x2955, 0x1001) },
+       { USB_DEVICE(0x2a5f, 0x1000) },
+       { USB_DEVICE(0x7392, 0x7710) },
+       { 0, }
+};
+
+bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len,
+                          struct mt7601u_dma_buf *buf)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+
+       buf->len = len;
+       buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+       buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma);
+
+       return !buf->urb || !buf->buf;
+}
+
+void mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+
+       usb_free_coherent(usb_dev, buf->len, buf->buf, buf->dma);
+       usb_free_urb(buf->urb);
+}
+
+int mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx,
+                          struct mt7601u_dma_buf *buf, gfp_t gfp,
+                          usb_complete_t complete_fn, void *context)
+{
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+       unsigned pipe;
+       int ret;
+
+       if (dir == USB_DIR_IN)
+               pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[ep_idx]);
+       else
+               pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep_idx]);
+
+       usb_fill_bulk_urb(buf->urb, usb_dev, pipe, buf->buf, buf->len,
+                         complete_fn, context);
+       buf->urb->transfer_dma = buf->dma;
+       buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       trace_mt_submit_urb(dev, buf->urb);
+       ret = usb_submit_urb(buf->urb, gfp);
+       if (ret)
+               dev_err(dev->dev, "Error: submit URB dir:%d ep:%d failed:%d\n",
+                       dir, ep_idx, ret);
+       return ret;
+}
+
+void mt7601u_complete_urb(struct urb *urb)
+{
+       struct completion *cmpl = urb->context;
+
+       complete(cmpl);
+}
+
+static int
+__mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
+                        const u8 direction, const u16 val, const u16 offset,
+                        void *buf, const size_t buflen)
+{
+       int i, ret;
+       struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
+       const u8 req_type = direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+       const unsigned int pipe = (direction == USB_DIR_IN) ?
+               usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0);
+
+       for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) {
+               ret = usb_control_msg(usb_dev, pipe, req, req_type,
+                                     val, offset, buf, buflen,
+                                     MT_VEND_REQ_TOUT_MS);
+               trace_mt_vend_req(dev, pipe, req, req_type, val, offset,
+                                 buf, buflen, ret);
+
+               if (ret >= 0 || ret == -ENODEV)
+                       return ret;
+
+               msleep(5);
+       }
+
+       dev_err(dev->dev, "Vendor request req:%02x off:%04x failed:%d\n",
+               req, offset, ret);
+
+       return ret;
+}
+
+int
+mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
+                      const u8 direction, const u16 val, const u16 offset,
+                      void *buf, const size_t buflen)
+{
+       int ret;
+
+       mutex_lock(&dev->vendor_req_mutex);
+
+       ret = __mt7601u_vendor_request(dev, req, direction, val, offset,
+                                      buf, buflen);
+       if (ret == -ENODEV)
+               set_bit(MT7601U_STATE_REMOVED, &dev->state);
+
+       mutex_unlock(&dev->vendor_req_mutex);
+
+       return ret;
+}
+
+void mt7601u_vendor_reset(struct mt7601u_dev *dev)
+{
+       mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT,
+                              MT_VEND_DEV_MODE_RESET, 0, NULL, 0);
+}
+
+u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
+{
+       int ret;
+       __le32 reg;
+       u32 val;
+
+       WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset);
+
+       ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN,
+                                    0, offset, &reg, sizeof(reg));
+       val = le32_to_cpu(reg);
+       if (ret > 0 && ret != sizeof(reg)) {
+               dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n",
+                       ret, offset);
+               val = ~0;
+       }
+
+       trace_reg_read(dev, offset, val);
+       return val;
+}
+
+int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
+                            const u16 offset, const u32 val)
+{
+       int ret;
+
+       ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
+                                    val & 0xffff, offset, NULL, 0);
+       if (ret)
+               return ret;
+       return mt7601u_vendor_request(dev, req, USB_DIR_OUT,
+                                     val >> 16, offset + 2, NULL, 0);
+}
+
+void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
+{
+       WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset);
+
+       mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val);
+       trace_reg_write(dev, offset, val);
+}
+
+u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
+{
+       val |= mt7601u_rr(dev, offset) & ~mask;
+       mt7601u_wr(dev, offset, val);
+       return val;
+}
+
+u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
+{
+       u32 reg = mt7601u_rr(dev, offset);
+
+       val |= reg & ~mask;
+       if (reg != val)
+               mt7601u_wr(dev, offset, val);
+       return val;
+}
+
+void mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset,
+                    const void *data, int len)
+{
+       WARN_ONCE(offset & 3, "unaligned write copy off:%08x", offset);
+       WARN_ONCE(len & 3, "short write copy off:%08x", offset);
+
+       mt7601u_burst_write_regs(dev, offset, data, len / 4);
+}
+
+void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr)
+{
+       mt7601u_wr(dev, offset, get_unaligned_le32(addr));
+       mt7601u_wr(dev, offset + 4, addr[4] | addr[5] << 8);
+}
+
+static int mt7601u_assign_pipes(struct usb_interface *usb_intf,
+                               struct mt7601u_dev *dev)
+{
+       struct usb_endpoint_descriptor *ep_desc;
+       struct usb_host_interface *intf_desc = usb_intf->cur_altsetting;
+       unsigned i, ep_i = 0, ep_o = 0;
+
+       BUILD_BUG_ON(sizeof(dev->in_eps) < __MT_EP_IN_MAX);
+       BUILD_BUG_ON(sizeof(dev->out_eps) < __MT_EP_OUT_MAX);
+
+       for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+               ep_desc = &intf_desc->endpoint[i].desc;
+
+               if (usb_endpoint_is_bulk_in(ep_desc) &&
+                   ep_i++ < __MT_EP_IN_MAX) {
+                       dev->in_eps[ep_i - 1] = usb_endpoint_num(ep_desc);
+                       dev->in_max_packet = usb_endpoint_maxp(ep_desc);
+                       /* Note: this is ignored by usb sub-system but vendor
+                        *       code does it. We can drop this at some point.
+                        */
+                       dev->in_eps[ep_i - 1] |= USB_DIR_IN;
+               } else if (usb_endpoint_is_bulk_out(ep_desc) &&
+                          ep_o++ < __MT_EP_OUT_MAX) {
+                       dev->out_eps[ep_o - 1] = usb_endpoint_num(ep_desc);
+                       dev->out_max_packet = usb_endpoint_maxp(ep_desc);
+               }
+       }
+
+       if (ep_i != __MT_EP_IN_MAX || ep_o != __MT_EP_OUT_MAX) {
+               dev_err(dev->dev, "Error: wrong pipe number in:%d out:%d\n",
+                       ep_i, ep_o);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mt7601u_probe(struct usb_interface *usb_intf,
+                        const struct usb_device_id *id)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(usb_intf);
+       struct mt7601u_dev *dev;
+       u32 asic_rev, mac_rev;
+       int ret;
+
+       dev = mt7601u_alloc_device(&usb_intf->dev);
+       if (!dev)
+               return -ENOMEM;
+
+       usb_dev = usb_get_dev(usb_dev);
+       usb_reset_device(usb_dev);
+
+       usb_set_intfdata(usb_intf, dev);
+
+       ret = mt7601u_assign_pipes(usb_intf, dev);
+       if (ret)
+               goto err;
+       ret = mt7601u_wait_asic_ready(dev);
+       if (ret)
+               goto err;
+
+       asic_rev = mt7601u_rr(dev, MT_ASIC_VERSION);
+       mac_rev = mt7601u_rr(dev, MT_MAC_CSR0);
+       dev_info(dev->dev, "ASIC revision: %08x MAC revision: %08x\n",
+                asic_rev, mac_rev);
+
+       /* Note: vendor driver skips this check for MT7601U */
+       if (!(mt7601u_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL))
+               dev_warn(dev->dev, "Warning: eFUSE not present\n");
+
+       ret = mt7601u_init_hardware(dev);
+       if (ret)
+               goto err;
+       ret = mt7601u_register_device(dev);
+       if (ret)
+               goto err_hw;
+
+       set_bit(MT7601U_STATE_INITIALIZED, &dev->state);
+
+       return 0;
+err_hw:
+       mt7601u_cleanup(dev);
+err:
+       usb_set_intfdata(usb_intf, NULL);
+       usb_put_dev(interface_to_usbdev(usb_intf));
+
+       destroy_workqueue(dev->stat_wq);
+       ieee80211_free_hw(dev->hw);
+       return ret;
+}
+
+static void mt7601u_disconnect(struct usb_interface *usb_intf)
+{
+       struct mt7601u_dev *dev = usb_get_intfdata(usb_intf);
+
+       ieee80211_unregister_hw(dev->hw);
+       mt7601u_cleanup(dev);
+
+       usb_set_intfdata(usb_intf, NULL);
+       usb_put_dev(interface_to_usbdev(usb_intf));
+
+       destroy_workqueue(dev->stat_wq);
+       ieee80211_free_hw(dev->hw);
+}
+
+static int mt7601u_suspend(struct usb_interface *usb_intf, pm_message_t state)
+{
+       struct mt7601u_dev *dev = usb_get_intfdata(usb_intf);
+
+       mt7601u_cleanup(dev);
+
+       return 0;
+}
+
+static int mt7601u_resume(struct usb_interface *usb_intf)
+{
+       struct mt7601u_dev *dev = usb_get_intfdata(usb_intf);
+
+       return mt7601u_init_hardware(dev);
+}
+
+MODULE_DEVICE_TABLE(usb, mt7601u_device_table);
+MODULE_FIRMWARE(MT7601U_FIRMWARE);
+MODULE_LICENSE("GPL");
+
+static struct usb_driver mt7601u_driver = {
+       .name           = KBUILD_MODNAME,
+       .id_table       = mt7601u_device_table,
+       .probe          = mt7601u_probe,
+       .disconnect     = mt7601u_disconnect,
+       .suspend        = mt7601u_suspend,
+       .resume         = mt7601u_resume,
+       .reset_resume   = mt7601u_resume,
+       .soft_unbind    = 1,
+       .disable_hub_initiated_lpm = 1,
+};
+module_usb_driver(mt7601u_driver);
diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h
new file mode 100644 (file)
index 0000000..49e188f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
+ *
+ * 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 __MT7601U_USB_H
+#define __MT7601U_USB_H
+
+#include "mt7601u.h"
+
+#define MT7601U_FIRMWARE       "mt7601u.bin"
+
+#define MT_VEND_REQ_MAX_RETRY  10
+#define MT_VEND_REQ_TOUT_MS    300
+
+#define MT_VEND_DEV_MODE_RESET 1
+
+enum mt_vendor_req {
+       MT_VEND_DEV_MODE = 1,
+       MT_VEND_WRITE = 2,
+       MT_VEND_MULTI_READ = 7,
+       MT_VEND_WRITE_FCE = 0x42,
+};
+
+enum mt_usb_ep_in {
+       MT_EP_IN_PKT_RX,
+       MT_EP_IN_CMD_RESP,
+       __MT_EP_IN_MAX,
+};
+
+enum mt_usb_ep_out {
+       MT_EP_OUT_INBAND_CMD,
+       MT_EP_OUT_AC_BK,
+       MT_EP_OUT_AC_BE,
+       MT_EP_OUT_AC_VI,
+       MT_EP_OUT_AC_VO,
+       MT_EP_OUT_HCCA,
+       __MT_EP_OUT_MAX,
+};
+
+static inline struct usb_device *mt7601u_to_usb_dev(struct mt7601u_dev *mt7601u)
+{
+       return interface_to_usbdev(to_usb_interface(mt7601u->dev));
+}
+
+static inline bool mt7601u_urb_has_error(struct urb *urb)
+{
+       return urb->status &&
+               urb->status != -ENOENT &&
+               urb->status != -ECONNRESET &&
+               urb->status != -ESHUTDOWN;
+}
+
+bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len,
+                          struct mt7601u_dma_buf *buf);
+void mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf);
+int mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx,
+                          struct mt7601u_dma_buf *buf, gfp_t gfp,
+                          usb_complete_t complete_fn, void *context);
+void mt7601u_complete_urb(struct urb *urb);
+
+int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
+                          const u8 direction, const u16 val, const u16 offset,
+                          void *buf, const size_t buflen);
+void mt7601u_vendor_reset(struct mt7601u_dev *dev);
+int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
+                            const u16 offset, const u32 val);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/util.c b/drivers/net/wireless/mediatek/mt7601u/util.c
new file mode 100644 (file)
index 0000000..7c1787c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ *
+ * 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.
+ */
+
+#include "mt7601u.h"
+
+void mt76_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 mt76_insert_hdr_pad(struct sk_buff *skb)
+{
+       int len = ieee80211_get_hdrlen_from_skb(skb);
+       int ret;
+
+       if (len % 4 == 0)
+               return 0;
+
+       ret = skb_cow(skb, 2);
+       if (ret)
+               return ret;
+
+       skb_push(skb, 2);
+       memmove(skb->data, skb->data + 2, len);
+
+       skb->data[len] = 0;
+       skb->data[len + 1] = 0;
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt7601u/util.h b/drivers/net/wireless/mediatek/mt7601u/util.h
new file mode 100644 (file)
index 0000000..b89140b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
+ * 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
+
+/*
+ * Power of two check, this will check
+ * if the mask that has been given contains and contiguous set of bits.
+ * Note that we cannot use the is_power_of_2() function since this
+ * check must be done at compile-time.
+ */
+#define is_power_of_two(x)     ( !((x) & ((x)-1)) )
+#define low_bit_mask(x)                ( ((x)-1) & ~(x) )
+#define is_valid_mask(x)       is_power_of_two(1LU + (x) + low_bit_mask(x))
+
+/*
+ * Macros to find first set bit in a variable.
+ * These macros behave the same as the __ffs() functions but
+ * the most important difference that this is done during
+ * compile-time rather then run-time.
+ */
+#define compile_ffs2(__x) \
+       __builtin_choose_expr(((__x) & 0x1), 0, 1)
+
+#define compile_ffs4(__x) \
+       __builtin_choose_expr(((__x) & 0x3), \
+                             (compile_ffs2((__x))), \
+                             (compile_ffs2((__x) >> 2) + 2))
+
+#define compile_ffs8(__x) \
+       __builtin_choose_expr(((__x) & 0xf), \
+                             (compile_ffs4((__x))), \
+                             (compile_ffs4((__x) >> 4) + 4))
+
+#define compile_ffs16(__x) \
+       __builtin_choose_expr(((__x) & 0xff), \
+                             (compile_ffs8((__x))), \
+                             (compile_ffs8((__x) >> 8) + 8))
+
+#define compile_ffs32(__x) \
+       __builtin_choose_expr(((__x) & 0xffff), \
+                             (compile_ffs16((__x))), \
+                             (compile_ffs16((__x) >> 16) + 16))
+
+/*
+ * This macro will check the requirements for the FIELD{8,16,32} macros
+ * The mask should be a constant non-zero contiguous set of bits which
+ * does not exceed the given typelimit.
+ */
+#define FIELD_CHECK(__mask) \
+       BUILD_BUG_ON(!(__mask) || !is_valid_mask(__mask))
+
+#define MT76_SET(_mask, _val)                                          \
+       ({                                                              \
+               FIELD_CHECK(_mask);                                     \
+               (((u32) (_val)) << compile_ffs32(_mask)) & _mask;       \
+       })
+
+#define MT76_GET(_mask, _val)                                          \
+       ({                                                              \
+               FIELD_CHECK(_mask);                                     \
+               (u32) (((_val) & _mask) >> compile_ffs32(_mask));       \
+       })
+
+#endif