mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
backport: mediatek: mt7601u: from v4.2-rc3
Signed-off-by: Robert Nelson <robertcnelson@gmail.com>
This commit is contained in:
committed by
popcornmix
parent
1c1c7183d6
commit
be552deed4
@@ -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/rtl8192cu/Kconfig"
|
||||
source "drivers/net/wireless/ti/Kconfig"
|
||||
|
||||
@@ -46,6 +46,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/
|
||||
|
||||
10
drivers/net/wireless/mediatek/Kconfig
Normal file
10
drivers/net/wireless/mediatek/Kconfig
Normal file
@@ -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
|
||||
1
drivers/net/wireless/mediatek/Makefile
Normal file
1
drivers/net/wireless/mediatek/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_MT7601U) += mt7601u/
|
||||
6
drivers/net/wireless/mediatek/mt7601u/Kconfig
Normal file
6
drivers/net/wireless/mediatek/mt7601u/Kconfig
Normal file
@@ -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.
|
||||
9
drivers/net/wireless/mediatek/mt7601u/Makefile
Normal file
9
drivers/net/wireless/mediatek/mt7601u/Makefile
Normal file
@@ -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)
|
||||
78
drivers/net/wireless/mediatek/mt7601u/core.c
Normal file
78
drivers/net/wireless/mediatek/mt7601u/core.c
Normal file
@@ -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;
|
||||
}
|
||||
172
drivers/net/wireless/mediatek/mt7601u/debugfs.c
Normal file
172
drivers/net/wireless/mediatek/mt7601u/debugfs.c
Normal file
@@ -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);
|
||||
}
|
||||
505
drivers/net/wireless/mediatek/mt7601u/dma.c
Normal file
505
drivers/net/wireless/mediatek/mt7601u/dma.c
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* 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,
|
||||
void *data, u32 seg_len, u32 truesize, struct page *p)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u32 true_len, hdr_len = 0, copy, frag;
|
||||
|
||||
skb = alloc_skb(p ? 128 : seg_len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
true_len = mt76_mac_process_rx(dev, skb, data, rxwi);
|
||||
if (!true_len || true_len > seg_len)
|
||||
goto bad_frame;
|
||||
|
||||
hdr_len = ieee80211_get_hdrlen_from_buf(data, true_len);
|
||||
if (!hdr_len)
|
||||
goto bad_frame;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* If not doing paged RX allocated skb will always have enough space */
|
||||
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;
|
||||
|
||||
bad_frame:
|
||||
dev_err_ratelimited(dev->dev, "Error: incorrect frame len:%u hdr:%u\n",
|
||||
true_len, hdr_len);
|
||||
dev_kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data,
|
||||
u32 seg_len, struct page *p)
|
||||
{
|
||||
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);
|
||||
|
||||
skb = mt7601u_rx_skb_from_seg(dev, rxwi, data, seg_len, truesize, p);
|
||||
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;
|
||||
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)
|
||||
new_p = dev_alloc_pages(MT_RX_ORDER);
|
||||
|
||||
while ((seg_len = mt7601u_rx_next_seg_len(data, data_len))) {
|
||||
mt7601u_rx_process_seg(dev, data, seg_len, new_p ? e->p : NULL);
|
||||
|
||||
data_len -= seg_len;
|
||||
data += seg_len;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt > 1)
|
||||
trace_mt_rx_dma_aggr(dev, cnt, !!new_p);
|
||||
|
||||
if (new_p) {
|
||||
/* 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);
|
||||
}
|
||||
127
drivers/net/wireless/mediatek/mt7601u/dma.h
Normal file
127
drivers/net/wireless/mediatek/mt7601u/dma.h
Normal file
@@ -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
|
||||
418
drivers/net/wireless/mediatek/mt7601u/eeprom.c
Normal file
418
drivers/net/wireless/mediatek/mt7601u/eeprom.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/* Invalid? Note: vendor driver does not handle this */
|
||||
if (value == 0xff)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
151
drivers/net/wireless/mediatek/mt7601u/eeprom.h
Normal file
151
drivers/net/wireless/mediatek/mt7601u/eeprom.h
Normal file
@@ -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
|
||||
628
drivers/net/wireless/mediatek/mt7601u/init.c
Normal file
628
drivers/net/wireless/mediatek/mt7601u/init.c
Normal file
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* (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)
|
||||
{
|
||||
if (!test_and_clear_bit(MT7601U_STATE_INITIALIZED, &dev->state))
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
164
drivers/net/wireless/mediatek/mt7601u/initvals.h
Normal file
164
drivers/net/wireless/mediatek/mt7601u/initvals.h
Normal file
@@ -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
|
||||
291
drivers/net/wireless/mediatek/mt7601u/initvals_phy.h
Normal file
291
drivers/net/wireless/mediatek/mt7601u/initvals_phy.h
Normal file
@@ -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
|
||||
573
drivers/net/wireless/mediatek/mt7601u/mac.c
Normal file
573
drivers/net/wireless/mediatek/mt7601u/mac.c
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* 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 len, ctl = le32_to_cpu(rxwi->ctl);
|
||||
u16 rate = le16_to_cpu(rxwi->rate);
|
||||
int rssi;
|
||||
|
||||
len = MT76_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
|
||||
if (len < 10)
|
||||
return 0;
|
||||
|
||||
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 len;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
178
drivers/net/wireless/mediatek/mt7601u/mac.h
Normal file
178
drivers/net/wireless/mediatek/mt7601u/mac.h
Normal file
@@ -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
|
||||
413
drivers/net/wireless/mediatek/mt7601u/main.c
Normal file
413
drivers/net/wireless/mediatek/mt7601u/main.c
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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(OTHER_BSS, MT_RX_FILTR_CFG_PROMISC);
|
||||
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,
|
||||
};
|
||||
534
drivers/net/wireless/mediatek/mt7601u/mcu.c
Normal file
534
drivers/net/wireless/mediatek/mt7601u/mcu.c
Normal file
@@ -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, ®, 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);
|
||||
}
|
||||
94
drivers/net/wireless/mediatek/mt7601u/mcu.h
Normal file
94
drivers/net/wireless/mediatek/mt7601u/mcu.h
Normal file
@@ -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
|
||||
390
drivers/net/wireless/mediatek/mt7601u/mt7601u.h
Normal file
390
drivers/net/wireless/mediatek/mt7601u/mt7601u.h
Normal file
@@ -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
|
||||
1251
drivers/net/wireless/mediatek/mt7601u/phy.c
Normal file
1251
drivers/net/wireless/mediatek/mt7601u/phy.c
Normal file
File diff suppressed because it is too large
Load Diff
636
drivers/net/wireless/mediatek/mt7601u/regs.h
Normal file
636
drivers/net/wireless/mediatek/mt7601u/regs.h
Normal file
@@ -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
|
||||
21
drivers/net/wireless/mediatek/mt7601u/trace.c
Normal file
21
drivers/net/wireless/mediatek/mt7601u/trace.c
Normal file
@@ -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
|
||||
400
drivers/net/wireless/mediatek/mt7601u/trace.h
Normal file
400
drivers/net/wireless/mediatek/mt7601u/trace.h
Normal file
@@ -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>
|
||||
319
drivers/net/wireless/mediatek/mt7601u/tx.c
Normal file
319
drivers/net/wireless/mediatek/mt7601u/tx.c
Normal file
@@ -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;
|
||||
}
|
||||
367
drivers/net/wireless/mediatek/mt7601u/usb.c
Normal file
367
drivers/net/wireless/mediatek/mt7601u/usb.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* 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, ®, 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);
|
||||
int ret;
|
||||
|
||||
ret = mt7601u_init_hardware(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_bit(MT7601U_STATE_INITIALIZED, &dev->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
77
drivers/net/wireless/mediatek/mt7601u/usb.h
Normal file
77
drivers/net/wireless/mediatek/mt7601u/usb.h
Normal file
@@ -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
|
||||
42
drivers/net/wireless/mediatek/mt7601u/util.c
Normal file
42
drivers/net/wireless/mediatek/mt7601u/util.c
Normal file
@@ -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;
|
||||
}
|
||||
77
drivers/net/wireless/mediatek/mt7601u/util.h
Normal file
77
drivers/net/wireless/mediatek/mt7601u/util.h
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user