提交 c869f77d 编写于 作者: J Jakub Kicinski 提交者: Kalle Valo

add mt7601u driver

Add support for the simplest of MediaTek Wi-Fi devices - MT7601U.
It is a single stream bgn chip with no bells or whistles.
This driver is partially based on Felix's mt76 but IMHO it doesn't
make sense to merge the two right now because MT7601U is a design
somewhere between old Ralink devices and new Mediatek chips.  There
wouldn't be all that much code sharing with the devices mt76 supports.
Situation may obviously change when someone decides to extend m76 with
support for the more recent USB dongles.

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

This driver sat on GitHub for quite a while and got some testing there:
http://github.com/kuba-moo/mt7601uSigned-off-by: NJakub Kicinski <kubakici@wp.pl>
Signed-off-by: NKalle Valo <kvalo@codeaurora.org>
上级 00e27eeb
......@@ -6365,6 +6365,12 @@ F: include/uapi/linux/meye.h
F: include/uapi/linux/ivtv*
F: include/uapi/linux/uvcvideo.h
MEDIATEK MT7601U WIRELESS LAN DRIVER
M: Jakub Kicinski <kubakici@wp.pl>
L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/mediatek/mt7601u/
MEGARAID SCSI/SAS DRIVERS
M: Kashyap Desai <kashyap.desai@avagotech.com>
M: Sumit Saxena <sumit.saxena@avagotech.com>
......
......@@ -277,6 +277,7 @@ source "drivers/net/wireless/libertas/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
......
......@@ -45,6 +45,8 @@ obj-$(CONFIG_IWLWIFI) += iwlwifi/
obj-$(CONFIG_IWLEGACY) += iwlegacy/
obj-$(CONFIG_RT2X00) += rt2x00/
obj-$(CONFIG_WL_MEDIATEK) += mediatek/
obj-$(CONFIG_P54_COMMON) += p54/
obj-$(CONFIG_ATH_CARDS) += ath/
......
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
obj-$(CONFIG_MT7601U) += mt7601u/
config MT7601U
tristate "MediaTek MT7601U (USB) support"
depends on MAC80211
depends on USB
---help---
This adds support for MT7601U-based wireless USB dongles.
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)
/*
* 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;
}
/*
* 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);
}
/*
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mt7601u.h"
#include "dma.h"
#include "usb.h"
#include "trace.h"
static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev,
struct mt7601u_dma_buf_rx *e, gfp_t gfp);
static unsigned int ieee80211_get_hdrlen_from_buf(const u8 *data, unsigned len)
{
const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)data;
unsigned int hdrlen;
if (unlikely(len < 10))
return 0;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (unlikely(hdrlen > len))
return 0;
return hdrlen;
}
static struct sk_buff *
mt7601u_rx_skb_from_seg(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi,
u8 *data, u32 seg_len)
{
struct sk_buff *skb;
u32 true_len;
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
seg_len -= 2;
skb = alloc_skb(seg_len, GFP_ATOMIC);
if (!skb)
return NULL;
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) {
int hdr_len = ieee80211_get_hdrlen_from_buf(data, seg_len);
memcpy(skb_put(skb, hdr_len), data, hdr_len);
data += hdr_len + 2;
seg_len -= hdr_len;
}
memcpy(skb_put(skb, seg_len), data, seg_len);
true_len = mt76_mac_process_rx(dev, skb, skb->data, rxwi);
skb_trim(skb, true_len);
return skb;
}
static struct sk_buff *
mt7601u_rx_skb_from_seg_paged(struct mt7601u_dev *dev,
struct mt7601u_rxwi *rxwi, void *data,
u32 seg_len, u32 truesize, struct page *p)
{
unsigned int hdr_len = ieee80211_get_hdrlen_from_buf(data, seg_len);
unsigned int true_len, copy, frag;
struct sk_buff *skb;
skb = alloc_skb(128, GFP_ATOMIC);
if (!skb)
return NULL;
true_len = mt76_mac_process_rx(dev, skb, data, rxwi);
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) {
memcpy(skb_put(skb, hdr_len), data, hdr_len);
data += hdr_len + 2;
true_len -= hdr_len;
hdr_len = 0;
}
copy = (true_len <= skb_tailroom(skb)) ? true_len : hdr_len + 8;
frag = true_len - copy;
memcpy(skb_put(skb, copy), data, copy);
data += copy;
if (frag) {
skb_add_rx_frag(skb, 0, p, data - page_address(p),
frag, truesize);
get_page(p);
}
return skb;
}
static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data,
u32 seg_len, struct page *p, bool paged)
{
struct sk_buff *skb;
struct mt7601u_rxwi *rxwi;
u32 fce_info, truesize = seg_len;
/* DMA_INFO field at the beginning of the segment contains only some of
* the information, we need to read the FCE descriptor from the end.
*/
fce_info = get_unaligned_le32(data + seg_len - MT_FCE_INFO_LEN);
seg_len -= MT_FCE_INFO_LEN;
data += MT_DMA_HDR_LEN;
seg_len -= MT_DMA_HDR_LEN;
rxwi = (struct mt7601u_rxwi *) data;
data += sizeof(struct mt7601u_rxwi);
seg_len -= sizeof(struct mt7601u_rxwi);
if (unlikely(rxwi->zero[0] || rxwi->zero[1] || rxwi->zero[2]))
dev_err_once(dev->dev, "Error: RXWI zero fields are set\n");
if (unlikely(MT76_GET(MT_RXD_INFO_TYPE, fce_info)))
dev_err_once(dev->dev, "Error: RX path seen a non-pkt urb\n");
trace_mt_rx(dev, rxwi, fce_info);
if (paged)
skb = mt7601u_rx_skb_from_seg_paged(dev, rxwi, data, seg_len,
truesize, p);
else
skb = mt7601u_rx_skb_from_seg(dev, rxwi, data, seg_len);
if (!skb)
return;
ieee80211_rx_ni(dev->hw, skb);
}
static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len)
{
u32 min_seg_len = MT_DMA_HDR_LEN + MT_RX_INFO_LEN +
sizeof(struct mt7601u_rxwi) + MT_FCE_INFO_LEN;
u16 dma_len = get_unaligned_le16(data);
if (data_len < min_seg_len ||
WARN_ON(!dma_len) ||
WARN_ON(dma_len + MT_DMA_HDRS > data_len) ||
WARN_ON(dma_len & 0x3))
return 0;
return MT_DMA_HDRS + dma_len;
}
static void
mt7601u_rx_process_entry(struct mt7601u_dev *dev, struct mt7601u_dma_buf_rx *e)
{
u32 seg_len, data_len = e->urb->actual_length;
u8 *data = page_address(e->p);
struct page *new_p = NULL;
bool paged = true;
int cnt = 0;
if (!test_bit(MT7601U_STATE_INITIALIZED, &dev->state))
return;
/* Copy if there is very little data in the buffer. */
if (data_len < 512) {
paged = false;
} else {
new_p = dev_alloc_pages(MT_RX_ORDER);
if (!new_p)
paged = false;
}
while ((seg_len = mt7601u_rx_next_seg_len(data, data_len))) {
mt7601u_rx_process_seg(dev, data, seg_len, e->p, paged);
data_len -= seg_len;
data += seg_len;
cnt++;
}
if (cnt > 1)
trace_mt_rx_dma_aggr(dev, cnt, paged);
if (paged) {
/* we have one extra ref from the allocator */
__free_pages(e->p, MT_RX_ORDER);
e->p = new_p;
}
}
static struct mt7601u_dma_buf_rx *
mt7601u_rx_get_pending_entry(struct mt7601u_dev *dev)
{
struct mt7601u_rx_queue *q = &dev->rx_q;
struct mt7601u_dma_buf_rx *buf = NULL;
unsigned long flags;
spin_lock_irqsave(&dev->rx_lock, flags);
if (!q->pending)
goto out;
buf = &q->e[q->start];
q->pending--;
q->start = (q->start + 1) % q->entries;
out:
spin_unlock_irqrestore(&dev->rx_lock, flags);
return buf;
}
static void mt7601u_complete_rx(struct urb *urb)
{
struct mt7601u_dev *dev = urb->context;
struct mt7601u_rx_queue *q = &dev->rx_q;
unsigned long flags;
spin_lock_irqsave(&dev->rx_lock, flags);
if (mt7601u_urb_has_error(urb))
dev_err(dev->dev, "Error: RX urb failed:%d\n", urb->status);
if (WARN_ONCE(q->e[q->end].urb != urb, "RX urb mismatch"))
goto out;
q->end = (q->end + 1) % q->entries;
q->pending++;
tasklet_schedule(&dev->rx_tasklet);
out:
spin_unlock_irqrestore(&dev->rx_lock, flags);
}
static void mt7601u_rx_tasklet(unsigned long data)
{
struct mt7601u_dev *dev = (struct mt7601u_dev *) data;
struct mt7601u_dma_buf_rx *e;
while ((e = mt7601u_rx_get_pending_entry(dev))) {
if (e->urb->status)
continue;
mt7601u_rx_process_entry(dev, e);
mt7601u_submit_rx_buf(dev, e, GFP_ATOMIC);
}
}
static void mt7601u_complete_tx(struct urb *urb)
{
struct mt7601u_tx_queue *q = urb->context;
struct mt7601u_dev *dev = q->dev;
struct sk_buff *skb;
unsigned long flags;
spin_lock_irqsave(&dev->tx_lock, flags);
if (mt7601u_urb_has_error(urb))
dev_err(dev->dev, "Error: TX urb failed:%d\n", urb->status);
if (WARN_ONCE(q->e[q->start].urb != urb, "TX urb mismatch"))
goto out;
skb = q->e[q->start].skb;
trace_mt_tx_dma_done(dev, skb);
mt7601u_tx_status(dev, skb);
if (q->used == q->entries - q->entries / 8)
ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb));
q->start = (q->start + 1) % q->entries;
q->used--;
if (urb->status)
goto out;
set_bit(MT7601U_STATE_MORE_STATS, &dev->state);
if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state))
queue_delayed_work(dev->stat_wq, &dev->stat_work,
msecs_to_jiffies(10));
out:
spin_unlock_irqrestore(&dev->tx_lock, flags);
}
static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
struct sk_buff *skb, u8 ep)
{
struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
unsigned snd_pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep]);
struct mt7601u_dma_buf_tx *e;
struct mt7601u_tx_queue *q = &dev->tx_q[ep];
unsigned long flags;
int ret;
spin_lock_irqsave(&dev->tx_lock, flags);
if (WARN_ON(q->entries <= q->used)) {
ret = -ENOSPC;
goto out;
}
e = &q->e[q->end];
e->skb = skb;
usb_fill_bulk_urb(e->urb, usb_dev, snd_pipe, skb->data, skb->len,
mt7601u_complete_tx, q);
ret = usb_submit_urb(e->urb, GFP_ATOMIC);
if (ret) {
/* Special-handle ENODEV from TX urb submission because it will
* often be the first ENODEV we see after device is removed.
*/
if (ret == -ENODEV)
set_bit(MT7601U_STATE_REMOVED, &dev->state);
else
dev_err(dev->dev, "Error: TX urb submit failed:%d\n",
ret);
goto out;
}
q->end = (q->end + 1) % q->entries;
q->used++;
if (q->used >= q->entries)
ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
out:
spin_unlock_irqrestore(&dev->tx_lock, flags);
return ret;
}
/* Map hardware Q to USB endpoint number */
static u8 q2ep(u8 qid)
{
/* TODO: take management packets to queue 5 */
return qid + 1;
}
/* Map USB endpoint number to Q id in the DMA engine */
static enum mt76_qsel ep2dmaq(u8 ep)
{
if (ep == 5)
return MT_QSEL_MGMT;
return MT_QSEL_EDCA;
}
int mt7601u_dma_enqueue_tx(struct mt7601u_dev *dev, struct sk_buff *skb,
struct mt76_wcid *wcid, int hw_q)
{
u8 ep = q2ep(hw_q);
u32 dma_flags;
int ret;
dma_flags = MT_TXD_PKT_INFO_80211;
if (wcid->hw_key_idx == 0xff)
dma_flags |= MT_TXD_PKT_INFO_WIV;
ret = mt7601u_dma_skb_wrap_pkt(skb, ep2dmaq(ep), dma_flags);
if (ret)
return ret;
ret = mt7601u_dma_submit_tx(dev, skb, ep);
if (ret) {
ieee80211_free_txskb(dev->hw, skb);
return ret;
}
return 0;
}
static void mt7601u_kill_rx(struct mt7601u_dev *dev)
{
int i;
unsigned long flags;
spin_lock_irqsave(&dev->rx_lock, flags);
for (i = 0; i < dev->rx_q.entries; i++) {
int next = dev->rx_q.end;
spin_unlock_irqrestore(&dev->rx_lock, flags);
usb_poison_urb(dev->rx_q.e[next].urb);
spin_lock_irqsave(&dev->rx_lock, flags);
}
spin_unlock_irqrestore(&dev->rx_lock, flags);
}
static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev,
struct mt7601u_dma_buf_rx *e, gfp_t gfp)
{
struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
u8 *buf = page_address(e->p);
unsigned pipe;
int ret;
pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[MT_EP_IN_PKT_RX]);
usb_fill_bulk_urb(e->urb, usb_dev, pipe, buf, MT_RX_URB_SIZE,
mt7601u_complete_rx, dev);
trace_mt_submit_urb(dev, e->urb);
ret = usb_submit_urb(e->urb, gfp);
if (ret)
dev_err(dev->dev, "Error: submit RX URB failed:%d\n", ret);
return ret;
}
static int mt7601u_submit_rx(struct mt7601u_dev *dev)
{
int i, ret;
for (i = 0; i < dev->rx_q.entries; i++) {
ret = mt7601u_submit_rx_buf(dev, &dev->rx_q.e[i], GFP_KERNEL);
if (ret)
return ret;
}
return 0;
}
static void mt7601u_free_rx(struct mt7601u_dev *dev)
{
int i;
for (i = 0; i < dev->rx_q.entries; i++) {
__free_pages(dev->rx_q.e[i].p, MT_RX_ORDER);
usb_free_urb(dev->rx_q.e[i].urb);
}
}
static int mt7601u_alloc_rx(struct mt7601u_dev *dev)
{
int i;
memset(&dev->rx_q, 0, sizeof(dev->rx_q));
dev->rx_q.dev = dev;
dev->rx_q.entries = N_RX_ENTRIES;
for (i = 0; i < N_RX_ENTRIES; i++) {
dev->rx_q.e[i].urb = usb_alloc_urb(0, GFP_KERNEL);
dev->rx_q.e[i].p = dev_alloc_pages(MT_RX_ORDER);
if (!dev->rx_q.e[i].urb || !dev->rx_q.e[i].p)
return -ENOMEM;
}
return 0;
}
static void mt7601u_free_tx_queue(struct mt7601u_tx_queue *q)
{
int i;
WARN_ON(q->used);
for (i = 0; i < q->entries; i++) {
usb_poison_urb(q->e[i].urb);
usb_free_urb(q->e[i].urb);
}
}
static void mt7601u_free_tx(struct mt7601u_dev *dev)
{
int i;
for (i = 0; i < __MT_EP_OUT_MAX; i++)
mt7601u_free_tx_queue(&dev->tx_q[i]);
}
static int mt7601u_alloc_tx_queue(struct mt7601u_dev *dev,
struct mt7601u_tx_queue *q)
{
int i;
q->dev = dev;
q->entries = N_TX_ENTRIES;
for (i = 0; i < N_TX_ENTRIES; i++) {
q->e[i].urb = usb_alloc_urb(0, GFP_KERNEL);
if (!q->e[i].urb)
return -ENOMEM;
}
return 0;
}
static int mt7601u_alloc_tx(struct mt7601u_dev *dev)
{
int i;
dev->tx_q = devm_kcalloc(dev->dev, __MT_EP_OUT_MAX,
sizeof(*dev->tx_q), GFP_KERNEL);
for (i = 0; i < __MT_EP_OUT_MAX; i++)
if (mt7601u_alloc_tx_queue(dev, &dev->tx_q[i]))
return -ENOMEM;
return 0;
}
int mt7601u_dma_init(struct mt7601u_dev *dev)
{
int ret = -ENOMEM;
tasklet_init(&dev->rx_tasklet, mt7601u_rx_tasklet, (unsigned long) dev);
ret = mt7601u_alloc_tx(dev);
if (ret)
goto err;
ret = mt7601u_alloc_rx(dev);
if (ret)
goto err;
ret = mt7601u_submit_rx(dev);
if (ret)
goto err;
return 0;
err:
mt7601u_dma_cleanup(dev);
return ret;
}
void mt7601u_dma_cleanup(struct mt7601u_dev *dev)
{
mt7601u_kill_rx(dev);
tasklet_kill(&dev->rx_tasklet);
mt7601u_free_rx(dev);
mt7601u_free_tx(dev);
}
/*
* 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
/*
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/of.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include "mt7601u.h"
#include "eeprom.h"
static bool
field_valid(u8 val)
{
return val != 0xff;
}
static s8
field_validate(u8 val)
{
if (!field_valid(val))
return 0;
return val;
}
static int
mt7601u_efuse_read(struct mt7601u_dev *dev, u16 addr, u8 *data,
enum mt7601u_eeprom_access_modes mode)
{
u32 val;
int i;
val = mt76_rr(dev, MT_EFUSE_CTRL);
val &= ~(MT_EFUSE_CTRL_AIN |
MT_EFUSE_CTRL_MODE);
val |= MT76_SET(MT_EFUSE_CTRL_AIN, addr & ~0xf) |
MT76_SET(MT_EFUSE_CTRL_MODE, mode) |
MT_EFUSE_CTRL_KICK;
mt76_wr(dev, MT_EFUSE_CTRL, val);
if (!mt76_poll(dev, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000))
return -ETIMEDOUT;
val = mt76_rr(dev, MT_EFUSE_CTRL);
if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) {
/* Parts of eeprom not in the usage map (0x80-0xc0,0xf0)
* will not return valid data but it's ok.
*/
memset(data, 0xff, 16);
return 0;
}
for (i = 0; i < 4; i++) {
val = mt76_rr(dev, MT_EFUSE_DATA(i));
put_unaligned_le32(val, data + 4 * i);
}
return 0;
}
static int
mt7601u_efuse_physical_size_check(struct mt7601u_dev *dev)
{
const int map_reads = DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16);
u8 data[map_reads * 16];
int ret, i;
u32 start = 0, end = 0, cnt_free;
for (i = 0; i < map_reads; i++) {
ret = mt7601u_efuse_read(dev, MT_EE_USAGE_MAP_START + i * 16,
data + i * 16, MT_EE_PHYSICAL_READ);
if (ret)
return ret;
}
for (i = 0; i < MT_EFUSE_USAGE_MAP_SIZE; i++)
if (!data[i]) {
if (!start)
start = MT_EE_USAGE_MAP_START + i;
end = MT_EE_USAGE_MAP_START + i;
}
cnt_free = end - start + 1;
if (MT_EFUSE_USAGE_MAP_SIZE - cnt_free < 5) {
dev_err(dev->dev, "Error: your device needs default EEPROM file and this driver doesn't support it!\n");
return -EINVAL;
}
return 0;
}
static bool
mt7601u_has_tssi(struct mt7601u_dev *dev, u8 *eeprom)
{
u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1);
return ~nic_conf1 && (nic_conf1 & MT_EE_NIC_CONF_1_TX_ALC_EN);
}
static void
mt7601u_set_chip_cap(struct mt7601u_dev *dev, u8 *eeprom)
{
u16 nic_conf0 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_0);
u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1);
if (!field_valid(nic_conf1 & 0xff))
nic_conf1 &= 0xff00;
dev->ee->tssi_enabled = mt7601u_has_tssi(dev, eeprom) &&
!(nic_conf1 & MT_EE_NIC_CONF_1_TEMP_TX_ALC);
if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL)
dev_err(dev->dev,
"Error: this driver does not support HW RF ctrl\n");
if (!field_valid(nic_conf0 >> 8))
return;
if (MT76_GET(MT_EE_NIC_CONF_0_RX_PATH, nic_conf0) > 1 ||
MT76_GET(MT_EE_NIC_CONF_0_TX_PATH, nic_conf0) > 1)
dev_err(dev->dev,
"Error: device has more than 1 RX/TX stream!\n");
}
static int
mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *eeprom)
{
const void *src = eeprom + MT_EE_MAC_ADDR;
ether_addr_copy(dev->macaddr, src);
if (!is_valid_ether_addr(dev->macaddr)) {
eth_random_addr(dev->macaddr);
dev_info(dev->dev,
"Invalid MAC address, using random address %pM\n",
dev->macaddr);
}
mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr));
mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) |
MT76_SET(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff));
return 0;
}
static void mt7601u_set_channel_target_power(struct mt7601u_dev *dev,
u8 *eeprom, u8 max_pwr)
{
u8 trgt_pwr = eeprom[MT_EE_TX_TSSI_TARGET_POWER];
if (trgt_pwr > max_pwr || !trgt_pwr) {
dev_warn(dev->dev, "Error: EEPROM trgt power invalid %hhx!\n",
trgt_pwr);
trgt_pwr = 0x20;
}
memset(dev->ee->chan_pwr, trgt_pwr, sizeof(dev->ee->chan_pwr));
}
static void
mt7601u_set_channel_power(struct mt7601u_dev *dev, u8 *eeprom)
{
u32 i, val;
u8 max_pwr;
val = mt7601u_rr(dev, MT_TX_ALC_CFG_0);
max_pwr = MT76_GET(MT_TX_ALC_CFG_0_LIMIT_0, val);
if (mt7601u_has_tssi(dev, eeprom)) {
mt7601u_set_channel_target_power(dev, eeprom, max_pwr);
return;
}
for (i = 0; i < 14; i++) {
s8 power = field_validate(eeprom[MT_EE_TX_POWER_OFFSET + i]);
if (power > max_pwr || power < 0)
power = MT7601U_DEFAULT_TX_POWER;
dev->ee->chan_pwr[i] = power;
}
}
static void
mt7601u_set_country_reg(struct mt7601u_dev *dev, u8 *eeprom)
{
/* Note: - region 31 is not valid for mt7601u (see rtmp_init.c)
* - comments in rtmp_def.h are incorrect (see rt_channel.c)
*/
static const struct reg_channel_bounds chan_bounds[] = {
/* EEPROM country regions 0 - 7 */
{ 1, 11 }, { 1, 13 }, { 10, 2 }, { 10, 4 },
{ 14, 1 }, { 1, 14 }, { 3, 7 }, { 5, 9 },
/* EEPROM country regions 32 - 33 */
{ 1, 11 }, { 1, 14 }
};
u8 val = eeprom[MT_EE_COUNTRY_REGION];
int idx = -1;
if (val < 8)
idx = val;
if (val > 31 && val < 33)
idx = val - 32 + 8;
if (idx != -1)
dev_info(dev->dev,
"EEPROM country region %02hhx (channels %hhd-%hhd)\n",
val, chan_bounds[idx].start,
chan_bounds[idx].start + chan_bounds[idx].num - 1);
else
idx = 5; /* channels 1 - 14 */
dev->ee->reg = chan_bounds[idx];
/* TODO: country region 33 is special - phy should be set to B-mode
* before entering channel 14 (see sta/connect.c)
*/
}
static void
mt7601u_set_rf_freq_off(struct mt7601u_dev *dev, u8 *eeprom)
{
u8 comp;
dev->ee->rf_freq_off = field_validate(eeprom[MT_EE_FREQ_OFFSET]);
comp = field_validate(eeprom[MT_EE_FREQ_OFFSET_COMPENSATION]);
if (comp & BIT(7))
dev->ee->rf_freq_off -= comp & 0x7f;
else
dev->ee->rf_freq_off += comp;
}
static void
mt7601u_set_rssi_offset(struct mt7601u_dev *dev, u8 *eeprom)
{
int i;
s8 *rssi_offset = dev->ee->rssi_offset;
for (i = 0; i < 2; i++) {
rssi_offset[i] = eeprom[MT_EE_RSSI_OFFSET + i];
if (rssi_offset[i] < -10 || rssi_offset[i] > 10) {
dev_warn(dev->dev,
"Warning: EEPROM RSSI is invalid %02hhx\n",
rssi_offset[i]);
rssi_offset[i] = 0;
}
}
}
static void
mt7601u_extra_power_over_mac(struct mt7601u_dev *dev)
{
u32 val;
val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_1) & 0x0000ff00) >> 8);
val |= ((mt7601u_rr(dev, MT_TX_PWR_CFG_2) & 0x0000ff00) << 8);
mt7601u_wr(dev, MT_TX_PWR_CFG_7, val);
val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_4) & 0x0000ff00) >> 8);
mt7601u_wr(dev, MT_TX_PWR_CFG_9, val);
}
static void
mt7601u_set_power_rate(struct power_per_rate *rate, s8 delta, u8 value)
{
rate->raw = s6_validate(value);
rate->bw20 = s6_to_int(value);
/* Note: vendor driver does cap the value to s6 right away */
rate->bw40 = rate->bw20 + delta;
}
static void
mt7601u_save_power_rate(struct mt7601u_dev *dev, s8 delta, u32 val, int i)
{
struct mt7601u_rate_power *t = &dev->ee->power_rate_table;
switch (i) {
case 0:
mt7601u_set_power_rate(&t->cck[0], delta, (val >> 0) & 0xff);
mt7601u_set_power_rate(&t->cck[1], delta, (val >> 8) & 0xff);
/* Save cck bw20 for fixups of channel 14 */
dev->ee->real_cck_bw20[0] = t->cck[0].bw20;
dev->ee->real_cck_bw20[1] = t->cck[1].bw20;
mt7601u_set_power_rate(&t->ofdm[0], delta, (val >> 16) & 0xff);
mt7601u_set_power_rate(&t->ofdm[1], delta, (val >> 24) & 0xff);
break;
case 1:
mt7601u_set_power_rate(&t->ofdm[2], delta, (val >> 0) & 0xff);
mt7601u_set_power_rate(&t->ofdm[3], delta, (val >> 8) & 0xff);
mt7601u_set_power_rate(&t->ht[0], delta, (val >> 16) & 0xff);
mt7601u_set_power_rate(&t->ht[1], delta, (val >> 24) & 0xff);
break;
case 2:
mt7601u_set_power_rate(&t->ht[2], delta, (val >> 0) & 0xff);
mt7601u_set_power_rate(&t->ht[3], delta, (val >> 8) & 0xff);
break;
}
}
static s8
get_delta(u8 val)
{
s8 ret;
if (!field_valid(val) || !(val & BIT(7)))
return 0;
ret = val & 0x1f;
if (ret > 8)
ret = 8;
if (val & BIT(6))
ret = -ret;
return ret;
}
static void
mt7601u_config_tx_power_per_rate(struct mt7601u_dev *dev, u8 *eeprom)
{
u32 val;
s8 bw40_delta;
int i;
bw40_delta = get_delta(eeprom[MT_EE_TX_POWER_DELTA_BW40]);
for (i = 0; i < 5; i++) {
val = get_unaligned_le32(eeprom + MT_EE_TX_POWER_BYRATE(i));
mt7601u_save_power_rate(dev, bw40_delta, val, i);
if (~val)
mt7601u_wr(dev, MT_TX_PWR_CFG_0 + i * 4, val);
}
mt7601u_extra_power_over_mac(dev);
}
static void
mt7601u_init_tssi_params(struct mt7601u_dev *dev, u8 *eeprom)
{
struct tssi_data *d = &dev->ee->tssi_data;
if (!dev->ee->tssi_enabled)
return;
d->slope = eeprom[MT_EE_TX_TSSI_SLOPE];
d->tx0_delta_offset = eeprom[MT_EE_TX_TSSI_OFFSET] * 1024;
d->offset[0] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP];
d->offset[1] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 1];
d->offset[2] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 2];
}
int
mt7601u_eeprom_init(struct mt7601u_dev *dev)
{
u8 *eeprom;
int i, ret;
ret = mt7601u_efuse_physical_size_check(dev);
if (ret)
return ret;
dev->ee = devm_kzalloc(dev->dev, sizeof(*dev->ee), GFP_KERNEL);
if (!dev->ee)
return -ENOMEM;
eeprom = kmalloc(MT7601U_EEPROM_SIZE, GFP_KERNEL);
if (!eeprom)
return -ENOMEM;
for (i = 0; i + 16 <= MT7601U_EEPROM_SIZE; i += 16) {
ret = mt7601u_efuse_read(dev, i, eeprom + i, MT_EE_READ);
if (ret)
goto out;
}
if (eeprom[MT_EE_VERSION_EE] > MT7601U_EE_MAX_VER)
dev_warn(dev->dev,
"Warning: unsupported EEPROM version %02hhx\n",
eeprom[MT_EE_VERSION_EE]);
dev_info(dev->dev, "EEPROM ver:%02hhx fae:%02hhx\n",
eeprom[MT_EE_VERSION_EE], eeprom[MT_EE_VERSION_FAE]);
mt7601u_set_macaddr(dev, eeprom);
mt7601u_set_chip_cap(dev, eeprom);
mt7601u_set_channel_power(dev, eeprom);
mt7601u_set_country_reg(dev, eeprom);
mt7601u_set_rf_freq_off(dev, eeprom);
mt7601u_set_rssi_offset(dev, eeprom);
dev->ee->ref_temp = eeprom[MT_EE_REF_TEMP];
dev->ee->lna_gain = eeprom[MT_EE_LNA_GAIN];
mt7601u_config_tx_power_per_rate(dev, eeprom);
mt7601u_init_tssi_params(dev, eeprom);
out:
kfree(eeprom);
return ret;
}
/*
* 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
/*
* (c) Copyright 2002-2010, Ralink Technology, Inc.
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mt7601u.h"
#include "eeprom.h"
#include "trace.h"
#include "mcu.h"
#include "initvals.h"
static void
mt7601u_set_wlan_state(struct mt7601u_dev *dev, u32 val, bool enable)
{
int i;
/* Note: we don't turn off WLAN_CLK because that makes the device
* not respond properly on the probe path.
* In case anyone (PSM?) wants to use this function we can
* bring the clock stuff back and fixup the probe path.
*/
if (enable)
val |= (MT_WLAN_FUN_CTRL_WLAN_EN |
MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
else
val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN);
mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
if (enable) {
set_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state);
} else {
clear_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state);
return;
}
for (i = 200; i; i--) {
val = mt7601u_rr(dev, MT_CMB_CTRL);
if (val & MT_CMB_CTRL_XTAL_RDY && val & MT_CMB_CTRL_PLL_LD)
break;
udelay(20);
}
/* Note: vendor driver tries to disable/enable wlan here and retry
* but the code which does it is so buggy it must have never
* triggered, so don't bother.
*/
if (!i)
dev_err(dev->dev, "Error: PLL and XTAL check failed!\n");
}
static void mt7601u_chip_onoff(struct mt7601u_dev *dev, bool enable, bool reset)
{
u32 val;
mutex_lock(&dev->hw_atomic_mutex);
val = mt7601u_rr(dev, MT_WLAN_FUN_CTRL);
if (reset) {
val |= MT_WLAN_FUN_CTRL_GPIO_OUT_EN;
val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL;
if (val & MT_WLAN_FUN_CTRL_WLAN_EN) {
val |= (MT_WLAN_FUN_CTRL_WLAN_RESET |
MT_WLAN_FUN_CTRL_WLAN_RESET_RF);
mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
val &= ~(MT_WLAN_FUN_CTRL_WLAN_RESET |
MT_WLAN_FUN_CTRL_WLAN_RESET_RF);
}
}
mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
mt7601u_set_wlan_state(dev, val, enable);
mutex_unlock(&dev->hw_atomic_mutex);
}
static void mt7601u_reset_csr_bbp(struct mt7601u_dev *dev)
{
mt7601u_wr(dev, MT_MAC_SYS_CTRL, (MT_MAC_SYS_CTRL_RESET_CSR |
MT_MAC_SYS_CTRL_RESET_BBP));
mt7601u_wr(dev, MT_USB_DMA_CFG, 0);
msleep(1);
mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0);
}
static void mt7601u_init_usb_dma(struct mt7601u_dev *dev)
{
u32 val;
val = MT76_SET(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, MT_USB_AGGR_TIMEOUT) |
MT76_SET(MT_USB_DMA_CFG_RX_BULK_AGG_LMT, MT_USB_AGGR_SIZE_LIMIT) |
MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN;
if (dev->in_max_packet == 512)
val |= MT_USB_DMA_CFG_RX_BULK_AGG_EN;
mt7601u_wr(dev, MT_USB_DMA_CFG, val);
val |= MT_USB_DMA_CFG_UDMA_RX_WL_DROP;
mt7601u_wr(dev, MT_USB_DMA_CFG, val);
val &= ~MT_USB_DMA_CFG_UDMA_RX_WL_DROP;
mt7601u_wr(dev, MT_USB_DMA_CFG, val);
}
static int mt7601u_init_bbp(struct mt7601u_dev *dev)
{
int ret;
ret = mt7601u_wait_bbp_ready(dev);
if (ret)
return ret;
ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_common_vals,
ARRAY_SIZE(bbp_common_vals));
if (ret)
return ret;
return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_chip_vals,
ARRAY_SIZE(bbp_chip_vals));
}
static void
mt76_init_beacon_offsets(struct mt7601u_dev *dev)
{
u16 base = MT_BEACON_BASE;
u32 regs[4] = {};
int i;
for (i = 0; i < 16; i++) {
u16 addr = dev->beacon_offsets[i];
regs[i / 4] |= ((addr - base) / 64) << (8 * (i % 4));
}
for (i = 0; i < 4; i++)
mt7601u_wr(dev, MT_BCN_OFFSET(i), regs[i]);
}
static int mt7601u_write_mac_initvals(struct mt7601u_dev *dev)
{
int ret;
ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, mac_common_vals,
ARRAY_SIZE(mac_common_vals));
if (ret)
return ret;
ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN,
mac_chip_vals, ARRAY_SIZE(mac_chip_vals));
if (ret)
return ret;
mt76_init_beacon_offsets(dev);
mt7601u_wr(dev, MT_AUX_CLK_CFG, 0);
return 0;
}
static int mt7601u_init_wcid_mem(struct mt7601u_dev *dev)
{
u32 *vals;
int i, ret;
vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL);
if (!vals)
return -ENOMEM;
for (i = 0; i < N_WCIDS; i++) {
vals[i * 2] = 0xffffffff;
vals[i * 2 + 1] = 0x00ffffff;
}
ret = mt7601u_burst_write_regs(dev, MT_WCID_ADDR_BASE,
vals, N_WCIDS * 2);
kfree(vals);
return ret;
}
static int mt7601u_init_key_mem(struct mt7601u_dev *dev)
{
u32 vals[4] = {};
return mt7601u_burst_write_regs(dev, MT_SKEY_MODE_BASE_0,
vals, ARRAY_SIZE(vals));
}
static int mt7601u_init_wcid_attr_mem(struct mt7601u_dev *dev)
{
u32 *vals;
int i, ret;
vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL);
if (!vals)
return -ENOMEM;
for (i = 0; i < N_WCIDS * 2; i++)
vals[i] = 1;
ret = mt7601u_burst_write_regs(dev, MT_WCID_ATTR_BASE,
vals, N_WCIDS * 2);
kfree(vals);
return ret;
}
static void mt7601u_reset_counters(struct mt7601u_dev *dev)
{
mt7601u_rr(dev, MT_RX_STA_CNT0);
mt7601u_rr(dev, MT_RX_STA_CNT1);
mt7601u_rr(dev, MT_RX_STA_CNT2);
mt7601u_rr(dev, MT_TX_STA_CNT0);
mt7601u_rr(dev, MT_TX_STA_CNT1);
mt7601u_rr(dev, MT_TX_STA_CNT2);
}
int mt7601u_mac_start(struct mt7601u_dev *dev)
{
mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 200000))
return -ETIMEDOUT;
dev->rxfilter = MT_RX_FILTR_CFG_CRC_ERR |
MT_RX_FILTR_CFG_PHY_ERR | MT_RX_FILTR_CFG_PROMISC |
MT_RX_FILTR_CFG_VER_ERR | MT_RX_FILTR_CFG_DUP |
MT_RX_FILTR_CFG_CFACK | MT_RX_FILTR_CFG_CFEND |
MT_RX_FILTR_CFG_ACK | MT_RX_FILTR_CFG_CTS |
MT_RX_FILTR_CFG_RTS | MT_RX_FILTR_CFG_PSPOLL |
MT_RX_FILTR_CFG_BA | MT_RX_FILTR_CFG_CTRL_RSV;
mt7601u_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
mt7601u_wr(dev, MT_MAC_SYS_CTRL,
MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 50))
return -ETIMEDOUT;
return 0;
}
static void mt7601u_mac_stop_hw(struct mt7601u_dev *dev)
{
int i, ok;
if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
return;
mt76_clear(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_TIMER_EN |
MT_BEACON_TIME_CFG_SYNC_MODE | MT_BEACON_TIME_CFG_TBTT_EN |
MT_BEACON_TIME_CFG_BEACON_TX);
if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_BUSY, 0, 1000))
dev_warn(dev->dev, "Warning: TX DMA did not stop!\n");
/* Page count on TxQ */
i = 200;
while (i-- && ((mt76_rr(dev, 0x0438) & 0xffffffff) ||
(mt76_rr(dev, 0x0a30) & 0x000000ff) ||
(mt76_rr(dev, 0x0a34) & 0x00ff00ff)))
msleep(10);
if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX, 0, 1000))
dev_warn(dev->dev, "Warning: MAC TX did not stop!\n");
mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX |
MT_MAC_SYS_CTRL_ENABLE_TX);
/* Page count on RxQ */
ok = 0;
i = 200;
while (i--) {
if ((mt76_rr(dev, 0x0430) & 0x00ff0000) ||
(mt76_rr(dev, 0x0a30) & 0xffffffff) ||
(mt76_rr(dev, 0x0a34) & 0xffffffff))
ok++;
if (ok > 6)
break;
msleep(1);
}
if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 1000))
dev_warn(dev->dev, "Warning: MAC RX did not stop!\n");
if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_RX_BUSY, 0, 1000))
dev_warn(dev->dev, "Warning: RX DMA did not stop!\n");
}
void mt7601u_mac_stop(struct mt7601u_dev *dev)
{
mt7601u_mac_stop_hw(dev);
flush_delayed_work(&dev->stat_work);
cancel_delayed_work_sync(&dev->stat_work);
}
static void mt7601u_stop_hardware(struct mt7601u_dev *dev)
{
mt7601u_chip_onoff(dev, false, false);
}
int mt7601u_init_hardware(struct mt7601u_dev *dev)
{
static const u16 beacon_offsets[16] = {
/* 512 byte per beacon */
0xc000, 0xc200, 0xc400, 0xc600,
0xc800, 0xca00, 0xcc00, 0xce00,
0xd000, 0xd200, 0xd400, 0xd600,
0xd800, 0xda00, 0xdc00, 0xde00
};
int ret;
dev->beacon_offsets = beacon_offsets;
mt7601u_chip_onoff(dev, true, false);
ret = mt7601u_wait_asic_ready(dev);
if (ret)
goto err;
ret = mt7601u_mcu_init(dev);
if (ret)
goto err;
if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG,
MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) {
ret = -EIO;
goto err;
}
/* Wait for ASIC ready after FW load. */
ret = mt7601u_wait_asic_ready(dev);
if (ret)
goto err;
mt7601u_reset_csr_bbp(dev);
mt7601u_init_usb_dma(dev);
ret = mt7601u_mcu_cmd_init(dev);
if (ret)
goto err;
ret = mt7601u_dma_init(dev);
if (ret)
goto err_mcu;
ret = mt7601u_write_mac_initvals(dev);
if (ret)
goto err_rx;
if (!mt76_poll_msec(dev, MT_MAC_STATUS,
MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 100)) {
ret = -EIO;
goto err_rx;
}
ret = mt7601u_init_bbp(dev);
if (ret)
goto err_rx;
ret = mt7601u_init_wcid_mem(dev);
if (ret)
goto err_rx;
ret = mt7601u_init_key_mem(dev);
if (ret)
goto err_rx;
ret = mt7601u_init_wcid_attr_mem(dev);
if (ret)
goto err_rx;
mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN |
MT_BEACON_TIME_CFG_SYNC_MODE |
MT_BEACON_TIME_CFG_TBTT_EN |
MT_BEACON_TIME_CFG_BEACON_TX));
mt7601u_reset_counters(dev);
mt7601u_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e);
mt7601u_wr(dev, MT_TXOP_CTRL_CFG, MT76_SET(MT_TXOP_TRUN_EN, 0x3f) |
MT76_SET(MT_TXOP_EXT_CCA_DLY, 0x58));
ret = mt7601u_eeprom_init(dev);
if (ret)
goto err_rx;
ret = mt7601u_phy_init(dev);
if (ret)
goto err_rx;
mt7601u_set_rx_path(dev, 0);
mt7601u_set_tx_dac(dev, 0);
mt7601u_mac_set_ctrlch(dev, false);
mt7601u_bbp_set_ctrlch(dev, false);
mt7601u_bbp_set_bw(dev, MT_BW_20);
return 0;
err_rx:
mt7601u_dma_cleanup(dev);
err_mcu:
mt7601u_mcu_cmd_deinit(dev);
err:
mt7601u_chip_onoff(dev, false, false);
return ret;
}
void mt7601u_cleanup(struct mt7601u_dev *dev)
{
mt7601u_stop_hardware(dev);
mt7601u_dma_cleanup(dev);
mt7601u_mcu_cmd_deinit(dev);
}
struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev)
{
struct ieee80211_hw *hw;
struct mt7601u_dev *dev;
hw = ieee80211_alloc_hw(sizeof(*dev), &mt7601u_ops);
if (!hw)
return NULL;
dev = hw->priv;
dev->dev = pdev;
dev->hw = hw;
mutex_init(&dev->vendor_req_mutex);
mutex_init(&dev->reg_atomic_mutex);
mutex_init(&dev->hw_atomic_mutex);
mutex_init(&dev->mutex);
spin_lock_init(&dev->tx_lock);
spin_lock_init(&dev->rx_lock);
spin_lock_init(&dev->lock);
spin_lock_init(&dev->con_mon_lock);
atomic_set(&dev->avg_ampdu_len, 1);
dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0);
if (!dev->stat_wq) {
ieee80211_free_hw(hw);
return NULL;
}
return dev;
}
#define CHAN2G(_idx, _freq) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 30, \
}
static const struct ieee80211_channel mt76_channels_2ghz[] = {
CHAN2G(1, 2412),
CHAN2G(2, 2417),
CHAN2G(3, 2422),
CHAN2G(4, 2427),
CHAN2G(5, 2432),
CHAN2G(6, 2437),
CHAN2G(7, 2442),
CHAN2G(8, 2447),
CHAN2G(9, 2452),
CHAN2G(10, 2457),
CHAN2G(11, 2462),
CHAN2G(12, 2467),
CHAN2G(13, 2472),
CHAN2G(14, 2484),
};
#define CCK_RATE(_idx, _rate) { \
.bitrate = _rate, \
.flags = IEEE80211_RATE_SHORT_PREAMBLE, \
.hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \
.hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \
}
#define OFDM_RATE(_idx, _rate) { \
.bitrate = _rate, \
.hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \
.hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \
}
static struct ieee80211_rate mt76_rates[] = {
CCK_RATE(0, 10),
CCK_RATE(1, 20),
CCK_RATE(2, 55),
CCK_RATE(3, 110),
OFDM_RATE(0, 60),
OFDM_RATE(1, 90),
OFDM_RATE(2, 120),
OFDM_RATE(3, 180),
OFDM_RATE(4, 240),
OFDM_RATE(5, 360),
OFDM_RATE(6, 480),
OFDM_RATE(7, 540),
};
static int
mt76_init_sband(struct mt7601u_dev *dev, struct ieee80211_supported_band *sband,
const struct ieee80211_channel *chan, int n_chan,
struct ieee80211_rate *rates, int n_rates)
{
struct ieee80211_sta_ht_cap *ht_cap;
void *chanlist;
int size;
size = n_chan * sizeof(*chan);
chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
if (!chanlist)
return -ENOMEM;
sband->channels = chanlist;
sband->n_channels = n_chan;
sband->bitrates = rates;
sband->n_bitrates = n_rates;
ht_cap = &sband->ht_cap;
ht_cap->ht_supported = true;
ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
ht_cap->mcs.rx_mask[0] = 0xff;
ht_cap->mcs.rx_mask[4] = 0x1;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_2;
dev->chandef.chan = &sband->channels[0];
return 0;
}
static int
mt76_init_sband_2g(struct mt7601u_dev *dev)
{
dev->sband_2g = devm_kzalloc(dev->dev, sizeof(*dev->sband_2g),
GFP_KERNEL);
dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = dev->sband_2g;
WARN_ON(dev->ee->reg.start - 1 + dev->ee->reg.num >
ARRAY_SIZE(mt76_channels_2ghz));
return mt76_init_sband(dev, dev->sband_2g,
&mt76_channels_2ghz[dev->ee->reg.start - 1],
dev->ee->reg.num,
mt76_rates, ARRAY_SIZE(mt76_rates));
}
int mt7601u_register_device(struct mt7601u_dev *dev)
{
struct ieee80211_hw *hw = dev->hw;
struct wiphy *wiphy = hw->wiphy;
int ret;
/* Reserve WCID 0 for mcast - thanks to this APs WCID will go to
* entry no. 1 like it does in the vendor driver.
*/
dev->wcid_mask[0] |= 1;
/* init fake wcid for monitor interfaces */
dev->mon_wcid = devm_kmalloc(dev->dev, sizeof(*dev->mon_wcid),
GFP_KERNEL);
if (!dev->mon_wcid)
return -ENOMEM;
dev->mon_wcid->idx = 0xff;
dev->mon_wcid->hw_key_idx = -1;
SET_IEEE80211_DEV(hw, dev->dev);
hw->queues = 4;
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_SUPPORTS_HT_CCK_RATES |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_SUPPORTS_RC_TABLE;
hw->max_rates = 1;
hw->max_report_rates = 7;
hw->max_rate_tries = 1;
hw->sta_data_size = sizeof(struct mt76_sta);
hw->vif_data_size = sizeof(struct mt76_vif);
SET_IEEE80211_PERM_ADDR(hw, dev->macaddr);
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
ret = mt76_init_sband_2g(dev);
if (ret)
return ret;
INIT_DELAYED_WORK(&dev->mac_work, mt7601u_mac_work);
INIT_DELAYED_WORK(&dev->stat_work, mt7601u_tx_stat);
ret = ieee80211_register_hw(hw);
if (ret)
return ret;
mt7601u_init_debugfs(dev);
return 0;
}
/*
* (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
/*
* (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
/*
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mt7601u.h"
#include "trace.h"
#include <linux/etherdevice.h>
static void
mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate)
{
u8 idx = MT76_GET(MT_TXWI_RATE_MCS, rate);
txrate->idx = 0;
txrate->flags = 0;
txrate->count = 1;
switch (MT76_GET(MT_TXWI_RATE_PHY_MODE, rate)) {
case MT_PHY_TYPE_OFDM:
txrate->idx = idx + 4;
return;
case MT_PHY_TYPE_CCK:
if (idx >= 8)
idx -= 8;
txrate->idx = idx;
return;
case MT_PHY_TYPE_HT_GF:
txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD;
/* fall through */
case MT_PHY_TYPE_HT:
txrate->flags |= IEEE80211_TX_RC_MCS;
txrate->idx = idx;
break;
default:
WARN_ON(1);
return;
}
if (MT76_GET(MT_TXWI_RATE_BW, rate) == MT_PHY_BW_40)
txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate & MT_TXWI_RATE_SGI)
txrate->flags |= IEEE80211_TX_RC_SHORT_GI;
}
static void
mt76_mac_fill_tx_status(struct mt7601u_dev *dev, struct ieee80211_tx_info *info,
struct mt76_tx_status *st)
{
struct ieee80211_tx_rate *rate = info->status.rates;
int cur_idx, last_rate;
int i;
last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1);
mt76_mac_process_tx_rate(&rate[last_rate], st->rate);
if (last_rate < IEEE80211_TX_MAX_RATES - 1)
rate[last_rate + 1].idx = -1;
cur_idx = rate[last_rate].idx + st->retry;
for (i = 0; i <= last_rate; i++) {
rate[i].flags = rate[last_rate].flags;
rate[i].idx = max_t(int, 0, cur_idx - i);
rate[i].count = 1;
}
if (last_rate > 0)
rate[last_rate - 1].count = st->retry + 1 - last_rate;
info->status.ampdu_len = 1;
info->status.ampdu_ack_len = st->success;
if (st->is_probe)
info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
if (st->aggr)
info->flags |= IEEE80211_TX_CTL_AMPDU |
IEEE80211_TX_STAT_AMPDU;
if (!st->ack_req)
info->flags |= IEEE80211_TX_CTL_NO_ACK;
else if (st->success)
info->flags |= IEEE80211_TX_STAT_ACK;
}
u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev,
const struct ieee80211_tx_rate *rate, u8 *nss_val)
{
u16 rateval;
u8 phy, rate_idx;
u8 nss = 1;
u8 bw = 0;
if (rate->flags & IEEE80211_TX_RC_MCS) {
rate_idx = rate->idx;
nss = 1 + (rate->idx >> 3);
phy = MT_PHY_TYPE_HT;
if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD)
phy = MT_PHY_TYPE_HT_GF;
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
bw = 1;
} else {
const struct ieee80211_rate *r;
int band = dev->chandef.chan->band;
u16 val;
r = &dev->hw->wiphy->bands[band]->bitrates[rate->idx];
if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
val = r->hw_value_short;
else
val = r->hw_value;
phy = val >> 8;
rate_idx = val & 0xff;
bw = 0;
}
rateval = MT76_SET(MT_RXWI_RATE_MCS, rate_idx);
rateval |= MT76_SET(MT_RXWI_RATE_PHY, phy);
rateval |= MT76_SET(MT_RXWI_RATE_BW, bw);
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
rateval |= MT_RXWI_RATE_SGI;
*nss_val = nss;
return rateval;
}
void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid,
const struct ieee80211_tx_rate *rate)
{
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
wcid->tx_rate = mt76_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss);
wcid->tx_rate_set = true;
spin_unlock_irqrestore(&dev->lock, flags);
}
struct mt76_tx_status mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev)
{
struct mt76_tx_status stat = {};
u32 val;
val = mt7601u_rr(dev, MT_TX_STAT_FIFO);
stat.valid = !!(val & MT_TX_STAT_FIFO_VALID);
stat.success = !!(val & MT_TX_STAT_FIFO_SUCCESS);
stat.aggr = !!(val & MT_TX_STAT_FIFO_AGGR);
stat.ack_req = !!(val & MT_TX_STAT_FIFO_ACKREQ);
stat.pktid = MT76_GET(MT_TX_STAT_FIFO_PID_TYPE, val);
stat.wcid = MT76_GET(MT_TX_STAT_FIFO_WCID, val);
stat.rate = MT76_GET(MT_TX_STAT_FIFO_RATE, val);
return stat;
}
void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
{
struct ieee80211_tx_info info = {};
struct ieee80211_sta *sta = NULL;
struct mt76_wcid *wcid = NULL;
void *msta;
rcu_read_lock();
if (stat->wcid < ARRAY_SIZE(dev->wcid))
wcid = rcu_dereference(dev->wcid[stat->wcid]);
if (wcid) {
msta = container_of(wcid, struct mt76_sta, wcid);
sta = container_of(msta, struct ieee80211_sta,
drv_priv);
}
mt76_mac_fill_tx_status(dev, &info, stat);
ieee80211_tx_status_noskb(dev->hw, sta, &info);
rcu_read_unlock();
}
void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot,
int ht_mode)
{
int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION;
bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
u32 prot[6];
bool ht_rts[4] = {};
int i;
prot[0] = MT_PROT_NAV_SHORT |
MT_PROT_TXOP_ALLOW_ALL |
MT_PROT_RTS_THR_EN;
prot[1] = prot[0];
if (legacy_prot)
prot[1] |= MT_PROT_CTRL_CTS2SELF;
prot[2] = prot[4] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_BW20;
prot[3] = prot[5] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_ALL;
if (legacy_prot) {
prot[2] |= MT_PROT_RATE_CCK_11;
prot[3] |= MT_PROT_RATE_CCK_11;
prot[4] |= MT_PROT_RATE_CCK_11;
prot[5] |= MT_PROT_RATE_CCK_11;
} else {
prot[2] |= MT_PROT_RATE_OFDM_24;
prot[3] |= MT_PROT_RATE_DUP_OFDM_24;
prot[4] |= MT_PROT_RATE_OFDM_24;
prot[5] |= MT_PROT_RATE_DUP_OFDM_24;
}
switch (mode) {
case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
break;
case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
break;
case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
ht_rts[1] = ht_rts[3] = true;
break;
case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true;
break;
}
if (non_gf)
ht_rts[2] = ht_rts[3] = true;
for (i = 0; i < 4; i++)
if (ht_rts[i])
prot[i + 2] |= MT_PROT_CTRL_RTS_CTS;
for (i = 0; i < 6; i++)
mt7601u_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]);
}
void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb)
{
if (short_preamb)
mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
else
mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT);
}
void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval)
{
u32 val = mt7601u_rr(dev, MT_BEACON_TIME_CFG);
val &= ~(MT_BEACON_TIME_CFG_TIMER_EN |
MT_BEACON_TIME_CFG_SYNC_MODE |
MT_BEACON_TIME_CFG_TBTT_EN);
if (!enable) {
mt7601u_wr(dev, MT_BEACON_TIME_CFG, val);
return;
}
val &= ~MT_BEACON_TIME_CFG_INTVAL;
val |= MT76_SET(MT_BEACON_TIME_CFG_INTVAL, interval << 4) |
MT_BEACON_TIME_CFG_TIMER_EN |
MT_BEACON_TIME_CFG_SYNC_MODE |
MT_BEACON_TIME_CFG_TBTT_EN;
}
static void mt7601u_check_mac_err(struct mt7601u_dev *dev)
{
u32 val = mt7601u_rr(dev, 0x10f4);
if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5))))
return;
dev_err(dev->dev, "Error: MAC specific condition occurred\n");
mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR);
udelay(10);
mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR);
}
void mt7601u_mac_work(struct work_struct *work)
{
struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev,
mac_work.work);
struct {
u32 addr_base;
u32 span;
u64 *stat_base;
} spans[] = {
{ MT_RX_STA_CNT0, 3, dev->stats.rx_stat },
{ MT_TX_STA_CNT0, 3, dev->stats.tx_stat },
{ MT_TX_AGG_STAT, 1, dev->stats.aggr_stat },
{ MT_MPDU_DENSITY_CNT, 1, dev->stats.zero_len_del },
{ MT_TX_AGG_CNT_BASE0, 8, &dev->stats.aggr_n[0] },
{ MT_TX_AGG_CNT_BASE1, 8, &dev->stats.aggr_n[16] },
};
u32 sum, n;
int i, j, k;
/* Note: using MCU_RANDOM_READ is actually slower then reading all the
* registers by hand. MCU takes ca. 20ms to complete read of 24
* registers while reading them one by one will takes roughly
* 24*200us =~ 5ms.
*/
k = 0;
n = 0;
sum = 0;
for (i = 0; i < ARRAY_SIZE(spans); i++)
for (j = 0; j < spans[i].span; j++) {
u32 val = mt7601u_rr(dev, spans[i].addr_base + j * 4);
spans[i].stat_base[j * 2] += val & 0xffff;
spans[i].stat_base[j * 2 + 1] += val >> 16;
/* Calculate average AMPDU length */
if (spans[i].addr_base != MT_TX_AGG_CNT_BASE0 &&
spans[i].addr_base != MT_TX_AGG_CNT_BASE1)
continue;
n += (val >> 16) + (val & 0xffff);
sum += (val & 0xffff) * (1 + k * 2) +
(val >> 16) * (2 + k * 2);
k++;
}
atomic_set(&dev->avg_ampdu_len, n ? DIV_ROUND_CLOSEST(sum, n) : 1);
mt7601u_check_mac_err(dev);
ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, 10 * HZ);
}
void
mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac)
{
u8 zmac[ETH_ALEN] = {};
u32 attr;
attr = MT76_SET(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) |
MT76_SET(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8));
mt76_wr(dev, MT_WCID_ATTR(idx), attr);
if (mac)
memcpy(zmac, mac, sizeof(zmac));
mt7601u_addr_wr(dev, MT_WCID_ADDR(idx), zmac);
}
void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev)
{
struct ieee80211_sta *sta;
struct mt76_wcid *wcid;
void *msta;
u8 min_factor = 3;
int i;
rcu_read_lock();
for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) {
wcid = rcu_dereference(dev->wcid[i]);
if (!wcid)
continue;
msta = container_of(wcid, struct mt76_sta, wcid);
sta = container_of(msta, struct ieee80211_sta, drv_priv);
min_factor = min(min_factor, sta->ht_cap.ampdu_factor);
}
rcu_read_unlock();
mt7601u_wr(dev, MT_MAX_LEN_CFG, 0xa0fff |
MT76_SET(MT_MAX_LEN_CFG_AMPDU, min_factor));
}
static void
mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
{
u8 idx = MT76_GET(MT_RXWI_RATE_MCS, rate);
switch (MT76_GET(MT_RXWI_RATE_PHY, rate)) {
case MT_PHY_TYPE_OFDM:
if (WARN_ON(idx >= 8))
idx = 0;
idx += 4;
status->rate_idx = idx;
return;
case MT_PHY_TYPE_CCK:
if (idx >= 8) {
idx -= 8;
status->flag |= RX_FLAG_SHORTPRE;
}
if (WARN_ON(idx >= 4))
idx = 0;
status->rate_idx = idx;
return;
case MT_PHY_TYPE_HT_GF:
status->flag |= RX_FLAG_HT_GF;
/* fall through */
case MT_PHY_TYPE_HT:
status->flag |= RX_FLAG_HT;
status->rate_idx = idx;
break;
default:
WARN_ON(1);
return;
}
if (rate & MT_RXWI_RATE_SGI)
status->flag |= RX_FLAG_SHORT_GI;
if (rate & MT_RXWI_RATE_STBC)
status->flag |= 1 << RX_FLAG_STBC_SHIFT;
if (rate & MT_RXWI_RATE_BW)
status->flag |= RX_FLAG_40MHZ;
}
static void
mt7601u_rx_monitor_beacon(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi,
u16 rate, int rssi)
{
dev->bcn_freq_off = rxwi->freq_off;
dev->bcn_phy_mode = MT76_GET(MT_RXWI_RATE_PHY, rate);
dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
}
static int
mt7601u_rx_is_our_beacon(struct mt7601u_dev *dev, u8 *data)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
return ieee80211_is_beacon(hdr->frame_control) &&
ether_addr_equal(hdr->addr2, dev->ap_bssid);
}
u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb,
u8 *data, void *rxi)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct mt7601u_rxwi *rxwi = rxi;
u32 ctl = le32_to_cpu(rxwi->ctl);
u16 rate = le16_to_cpu(rxwi->rate);
int rssi;
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
status->flag |= RX_FLAG_DECRYPTED;
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
}
status->chains = BIT(0);
rssi = mt7601u_phy_get_rssi(dev, rxwi, rate);
status->chain_signal[0] = status->signal = rssi;
status->freq = dev->chandef.chan->center_freq;
status->band = dev->chandef.chan->band;
mt76_mac_process_rate(status, rate);
spin_lock_bh(&dev->con_mon_lock);
if (mt7601u_rx_is_our_beacon(dev, data))
mt7601u_rx_monitor_beacon(dev, rxwi, rate, rssi);
else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M))
dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
spin_unlock_bh(&dev->con_mon_lock);
return MT76_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
}
static enum mt76_cipher_type
mt76_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data)
{
memset(key_data, 0, 32);
if (!key)
return MT_CIPHER_NONE;
if (key->keylen > 32)
return MT_CIPHER_NONE;
memcpy(key_data, key->key, key->keylen);
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
return MT_CIPHER_WEP40;
case WLAN_CIPHER_SUITE_WEP104:
return MT_CIPHER_WEP104;
case WLAN_CIPHER_SUITE_TKIP:
return MT_CIPHER_TKIP;
case WLAN_CIPHER_SUITE_CCMP:
return MT_CIPHER_AES_CCMP;
default:
return MT_CIPHER_NONE;
}
}
int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx,
struct ieee80211_key_conf *key)
{
enum mt76_cipher_type cipher;
u8 key_data[32];
u8 iv_data[8];
u32 val;
cipher = mt76_mac_get_key_info(key, key_data);
if (cipher == MT_CIPHER_NONE && key)
return -EINVAL;
trace_set_key(dev, idx);
mt7601u_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data));
memset(iv_data, 0, sizeof(iv_data));
if (key) {
iv_data[3] = key->keyidx << 6;
if (cipher >= MT_CIPHER_TKIP) {
/* Note: start with 1 to comply with spec,
* (see comment on common/cmm_wpa.c:4291).
*/
iv_data[0] |= 1;
iv_data[3] |= 0x20;
}
}
mt7601u_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data));
val = mt7601u_rr(dev, MT_WCID_ATTR(idx));
val &= ~MT_WCID_ATTR_PKEY_MODE & ~MT_WCID_ATTR_PKEY_MODE_EXT;
val |= MT76_SET(MT_WCID_ATTR_PKEY_MODE, cipher & 7) |
MT76_SET(MT_WCID_ATTR_PKEY_MODE_EXT, cipher >> 3);
val &= ~MT_WCID_ATTR_PAIRWISE;
val |= MT_WCID_ATTR_PAIRWISE *
!!(key && key->flags & IEEE80211_KEY_FLAG_PAIRWISE);
mt7601u_wr(dev, MT_WCID_ATTR(idx), val);
return 0;
}
int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx,
struct ieee80211_key_conf *key)
{
enum mt76_cipher_type cipher;
u8 key_data[32];
u32 val;
cipher = mt76_mac_get_key_info(key, key_data);
if (cipher == MT_CIPHER_NONE && key)
return -EINVAL;
trace_set_shared_key(dev, vif_idx, key_idx);
mt7601u_wr_copy(dev, MT_SKEY(vif_idx, key_idx),
key_data, sizeof(key_data));
val = mt76_rr(dev, MT_SKEY_MODE(vif_idx));
val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx));
val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx);
mt76_wr(dev, MT_SKEY_MODE(vif_idx), val);
return 0;
}
/*
* 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
/*
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mt7601u.h"
#include "mac.h"
#include <linux/etherdevice.h>
#include <linux/version.h>
static int mt7601u_start(struct ieee80211_hw *hw)
{
struct mt7601u_dev *dev = hw->priv;
int ret;
mutex_lock(&dev->mutex);
ret = mt7601u_mac_start(dev);
if (ret)
goto out;
ieee80211_queue_delayed_work(dev->hw, &dev->mac_work,
MT_CALIBRATE_INTERVAL);
ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
MT_CALIBRATE_INTERVAL);
out:
mutex_unlock(&dev->mutex);
return ret;
}
static void mt7601u_stop(struct ieee80211_hw *hw)
{
struct mt7601u_dev *dev = hw->priv;
mutex_lock(&dev->mutex);
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mac_work);
mt7601u_mac_stop(dev);
mutex_unlock(&dev->mutex);
}
static int mt7601u_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
unsigned int idx = 0;
unsigned int wcid = GROUP_WCID(idx);
/* Note: for AP do the AP-STA things mt76 does:
* - beacon offsets
* - do mac address tricks
* - shift vif idx
*/
mvif->idx = idx;
if (dev->wcid_mask[wcid / BITS_PER_LONG] & BIT(wcid % BITS_PER_LONG))
return -ENOSPC;
dev->wcid_mask[wcid / BITS_PER_LONG] |= BIT(wcid % BITS_PER_LONG);
mvif->group_wcid.idx = wcid;
mvif->group_wcid.hw_key_idx = -1;
return 0;
}
static void mt7601u_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
unsigned int wcid = mvif->group_wcid.idx;
dev->wcid_mask[wcid / BITS_PER_LONG] &= ~BIT(wcid % BITS_PER_LONG);
}
static int mt7601u_config(struct ieee80211_hw *hw, u32 changed)
{
struct mt7601u_dev *dev = hw->priv;
int ret = 0;
mutex_lock(&dev->mutex);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
ieee80211_stop_queues(hw);
ret = mt7601u_phy_set_channel(dev, &hw->conf.chandef);
ieee80211_wake_queues(hw);
}
mutex_unlock(&dev->mutex);
return ret;
}
static void
mt76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
unsigned int *total_flags, u64 multicast)
{
struct mt7601u_dev *dev = hw->priv;
u32 flags = 0;
#define MT76_FILTER(_flag, _hw) do { \
flags |= *total_flags & FIF_##_flag; \
dev->rxfilter &= ~(_hw); \
dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \
} while (0)
mutex_lock(&dev->mutex);
dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS;
MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR);
MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR);
MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK |
MT_RX_FILTR_CFG_CTS |
MT_RX_FILTR_CFG_CFEND |
MT_RX_FILTR_CFG_CFACK |
MT_RX_FILTR_CFG_BA |
MT_RX_FILTR_CFG_CTRL_RSV);
MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL);
*total_flags = flags;
mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
mutex_unlock(&dev->mutex);
}
static void
mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info, u32 changed)
{
struct mt7601u_dev *dev = hw->priv;
mutex_lock(&dev->mutex);
if (changed & BSS_CHANGED_ASSOC)
mt7601u_phy_con_cal_onoff(dev, info);
if (changed & BSS_CHANGED_BSSID) {
mt7601u_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid);
/* Note: this is a hack because beacon_int is not changed
* on leave nor is any more appropriate event generated.
* rt2x00 doesn't seem to be bothered though.
*/
if (is_zero_ether_addr(info->bssid))
mt7601u_mac_config_tsf(dev, false, 0);
}
if (changed & BSS_CHANGED_BASIC_RATES) {
mt7601u_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates);
mt7601u_wr(dev, MT_HT_FBK_CFG0, 0x65432100);
mt7601u_wr(dev, MT_HT_FBK_CFG1, 0xedcba980);
mt7601u_wr(dev, MT_LG_FBK_CFG0, 0xedcba988);
mt7601u_wr(dev, MT_LG_FBK_CFG1, 0x00002100);
}
if (changed & BSS_CHANGED_BEACON_INT)
mt7601u_mac_config_tsf(dev, true, info->beacon_int);
if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
mt7601u_mac_set_protection(dev, info->use_cts_prot,
info->ht_operation_mode);
if (changed & BSS_CHANGED_ERP_PREAMBLE)
mt7601u_mac_set_short_preamble(dev, info->use_short_preamble);
if (changed & BSS_CHANGED_ERP_SLOT) {
int slottime = info->use_short_slot ? 9 : 20;
mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
}
if (changed & BSS_CHANGED_ASSOC)
mt7601u_phy_recalibrate_after_assoc(dev);
mutex_unlock(&dev->mutex);
}
static int
mt76_wcid_alloc(struct mt7601u_dev *dev)
{
int i, idx = 0;
for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) {
idx = ffs(~dev->wcid_mask[i]);
if (!idx)
continue;
idx--;
dev->wcid_mask[i] |= BIT(idx);
break;
}
idx = i * BITS_PER_LONG + idx;
if (idx > 119)
return -1;
return idx;
}
static int
mt7601u_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
int ret = 0;
int idx = 0;
mutex_lock(&dev->mutex);
idx = mt76_wcid_alloc(dev);
if (idx < 0) {
ret = -ENOSPC;
goto out;
}
msta->wcid.idx = idx;
msta->wcid.hw_key_idx = -1;
mt7601u_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
rcu_assign_pointer(dev->wcid[idx], &msta->wcid);
mt7601u_mac_set_ampdu_factor(dev);
out:
mutex_unlock(&dev->mutex);
return ret;
}
static int
mt7601u_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
int idx = msta->wcid.idx;
mutex_lock(&dev->mutex);
rcu_assign_pointer(dev->wcid[idx], NULL);
mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG);
mt7601u_mac_wcid_setup(dev, idx, 0, NULL);
mt7601u_mac_set_ampdu_factor(dev);
mutex_unlock(&dev->mutex);
return 0;
}
static void
mt7601u_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
{
}
static void
mt7601u_sw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const u8 *mac_addr)
{
struct mt7601u_dev *dev = hw->priv;
mt7601u_agc_save(dev);
set_bit(MT7601U_STATE_SCANNING, &dev->state);
}
static void
mt7601u_sw_scan_complete(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mt7601u_dev *dev = hw->priv;
mt7601u_agc_restore(dev);
clear_bit(MT7601U_STATE_SCANNING, &dev->state);
}
static int
mt7601u_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
struct mt76_sta *msta = sta ? (struct mt76_sta *) sta->drv_priv : NULL;
struct mt76_wcid *wcid = msta ? &msta->wcid : &mvif->group_wcid;
int idx = key->keyidx;
int ret;
if (cmd == SET_KEY) {
key->hw_key_idx = wcid->idx;
wcid->hw_key_idx = idx;
} else {
if (idx == wcid->hw_key_idx)
wcid->hw_key_idx = -1;
key = NULL;
}
if (!msta) {
if (key || wcid->hw_key_idx == idx) {
ret = mt76_mac_wcid_set_key(dev, wcid->idx, key);
if (ret)
return ret;
}
return mt76_mac_shared_key_setup(dev, mvif->idx, idx, key);
}
return mt76_mac_wcid_set_key(dev, msta->wcid.idx, key);
}
static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{
struct mt7601u_dev *dev = hw->priv;
mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value);
return 0;
}
static int
mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
WARN_ON(msta->wcid.idx > GROUP_WCID(0));
switch (action) {
case IEEE80211_AMPDU_RX_START:
mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid));
break;
case IEEE80211_AMPDU_RX_STOP:
mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4,
BIT(16 + tid));
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]);
break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
break;
case IEEE80211_AMPDU_TX_START:
msta->agg_ssn[tid] = *ssn << 4;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP_CONT:
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
}
return 0;
}
static void
mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7601u_dev *dev = hw->priv;
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
struct ieee80211_sta_rates *rates;
struct ieee80211_tx_rate rate = {};
rcu_read_lock();
rates = rcu_dereference(sta->rates);
if (!rates)
goto out;
rate.idx = rates->rate[0].idx;
rate.flags = rates->rate[0].flags;
mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate);
out:
rcu_read_unlock();
}
const struct ieee80211_ops mt7601u_ops = {
.tx = mt7601u_tx,
.start = mt7601u_start,
.stop = mt7601u_stop,
.add_interface = mt7601u_add_interface,
.remove_interface = mt7601u_remove_interface,
.config = mt7601u_config,
.configure_filter = mt76_configure_filter,
.bss_info_changed = mt7601u_bss_info_changed,
.sta_add = mt7601u_sta_add,
.sta_remove = mt7601u_sta_remove,
.sta_notify = mt7601u_sta_notify,
.set_key = mt7601u_set_key,
.conf_tx = mt7601u_conf_tx,
.sw_scan_start = mt7601u_sw_scan,
.sw_scan_complete = mt7601u_sw_scan_complete,
.ampdu_action = mt76_ampdu_action,
.sta_rate_tbl_update = mt76_sta_rate_tbl_update,
.set_rts_threshold = mt7601u_set_rts_threshold,
};
/*
* (c) Copyright 2002-2010, Ralink Technology, Inc.
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/usb.h>
#include <linux/skbuff.h>
#include "mt7601u.h"
#include "dma.h"
#include "mcu.h"
#include "usb.h"
#include "trace.h"
#define MCU_FW_URB_MAX_PAYLOAD 0x3800
#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12)
#define MCU_RESP_URB_SIZE 1024
static inline int firmware_running(struct mt7601u_dev *dev)
{
return mt7601u_rr(dev, MT_MCU_COM_REG0) == 1;
}
static inline void skb_put_le32(struct sk_buff *skb, u32 val)
{
put_unaligned_le32(val, skb_put(skb, 4));
}
static inline void mt7601u_dma_skb_wrap_cmd(struct sk_buff *skb,
u8 seq, enum mcu_cmd cmd)
{
WARN_ON(mt7601u_dma_skb_wrap(skb, CPU_TX_PORT, DMA_COMMAND,
MT76_SET(MT_TXD_CMD_INFO_SEQ, seq) |
MT76_SET(MT_TXD_CMD_INFO_TYPE, cmd)));
}
static inline void trace_mt_mcu_msg_send_cs(struct mt7601u_dev *dev,
struct sk_buff *skb, bool need_resp)
{
u32 i, csum = 0;
for (i = 0; i < skb->len / 4; i++)
csum ^= get_unaligned_le32(skb->data + i * 4);
trace_mt_mcu_msg_send(dev, skb, csum, need_resp);
}
static struct sk_buff *
mt7601u_mcu_msg_alloc(struct mt7601u_dev *dev, const void *data, int len)
{
struct sk_buff *skb;
WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */
skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
skb_reserve(skb, MT_DMA_HDR_LEN);
memcpy(skb_put(skb, len), data, len);
return skb;
}
static int mt7601u_mcu_wait_resp(struct mt7601u_dev *dev, u8 seq)
{
struct urb *urb = dev->mcu.resp.urb;
u32 rxfce;
int urb_status, ret, i = 5;
while (i--) {
if (!wait_for_completion_timeout(&dev->mcu.resp_cmpl,
msecs_to_jiffies(300))) {
dev_warn(dev->dev, "Warning: %s retrying\n", __func__);
continue;
}
/* Make copies of important data before reusing the urb */
rxfce = get_unaligned_le32(dev->mcu.resp.buf);
urb_status = urb->status * mt7601u_urb_has_error(urb);
ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP,
&dev->mcu.resp, GFP_KERNEL,
mt7601u_complete_urb,
&dev->mcu.resp_cmpl);
if (ret)
return ret;
if (urb_status)
dev_err(dev->dev, "Error: MCU resp urb failed:%d\n",
urb_status);
if (MT76_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce) == seq &&
MT76_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce) == CMD_DONE)
return 0;
dev_err(dev->dev, "Error: MCU resp evt:%hhx seq:%hhx-%hhx!\n",
MT76_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce),
seq, MT76_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce));
}
dev_err(dev->dev, "Error: %s timed out\n", __func__);
return -ETIMEDOUT;
}
static int
mt7601u_mcu_msg_send(struct mt7601u_dev *dev, struct sk_buff *skb,
enum mcu_cmd cmd, bool wait_resp)
{
struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
unsigned cmd_pipe = usb_sndbulkpipe(usb_dev,
dev->out_eps[MT_EP_OUT_INBAND_CMD]);
int sent, ret;
u8 seq = 0;
if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
return 0;
mutex_lock(&dev->mcu.mutex);
if (wait_resp)
while (!seq)
seq = ++dev->mcu.msg_seq & 0xf;
mt7601u_dma_skb_wrap_cmd(skb, seq, cmd);
if (dev->mcu.resp_cmpl.done)
dev_err(dev->dev, "Error: MCU response pre-completed!\n");
trace_mt_mcu_msg_send_cs(dev, skb, wait_resp);
trace_mt_submit_urb_sync(dev, cmd_pipe, skb->len);
ret = usb_bulk_msg(usb_dev, cmd_pipe, skb->data, skb->len, &sent, 500);
if (ret) {
dev_err(dev->dev, "Error: send MCU cmd failed:%d\n", ret);
goto out;
}
if (sent != skb->len)
dev_err(dev->dev, "Error: %s sent != skb->len\n", __func__);
if (wait_resp)
ret = mt7601u_mcu_wait_resp(dev, seq);
out:
mutex_unlock(&dev->mcu.mutex);
consume_skb(skb);
return ret;
}
static int mt7601u_mcu_function_select(struct mt7601u_dev *dev,
enum mcu_function func, u32 val)
{
struct sk_buff *skb;
struct {
__le32 id;
__le32 value;
} __packed __aligned(4) msg = {
.id = cpu_to_le32(func),
.value = cpu_to_le32(val),
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5);
}
int mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga)
{
int ret;
if (!test_bit(MT7601U_STATE_MCU_RUNNING, &dev->state))
return 0;
ret = mt7601u_mcu_function_select(dev, ATOMIC_TSSI_SETTING,
use_hvga);
if (ret) {
dev_warn(dev->dev, "Warning: MCU TSSI read kick failed\n");
return ret;
}
dev->tssi_read_trig = true;
return 0;
}
int
mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val)
{
struct sk_buff *skb;
struct {
__le32 id;
__le32 value;
} __packed __aligned(4) msg = {
.id = cpu_to_le32(cal),
.value = cpu_to_le32(val),
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true);
}
int mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base,
const struct mt76_reg_pair *data, int n)
{
const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8;
struct sk_buff *skb;
int cnt, i, ret;
if (!n)
return 0;
cnt = min(max_vals_per_cmd, n);
skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
if (!skb)
return -ENOMEM;
skb_reserve(skb, MT_DMA_HDR_LEN);
for (i = 0; i < cnt; i++) {
skb_put_le32(skb, base + data[i].reg);
skb_put_le32(skb, data[i].value);
}
ret = mt7601u_mcu_msg_send(dev, skb, CMD_RANDOM_WRITE, cnt == n);
if (ret)
return ret;
return mt7601u_write_reg_pairs(dev, base, data + cnt, n - cnt);
}
int mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset,
const u32 *data, int n)
{
const int max_regs_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1;
struct sk_buff *skb;
int cnt, i, ret;
if (!n)
return 0;
cnt = min(max_regs_per_cmd, n);
skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
if (!skb)
return -ENOMEM;
skb_reserve(skb, MT_DMA_HDR_LEN);
skb_put_le32(skb, MT_MCU_MEMMAP_WLAN + offset);
for (i = 0; i < cnt; i++)
skb_put_le32(skb, data[i]);
ret = mt7601u_mcu_msg_send(dev, skb, CMD_BURST_WRITE, cnt == n);
if (ret)
return ret;
return mt7601u_burst_write_regs(dev, offset + cnt * 4,
data + cnt, n - cnt);
}
struct mt76_fw_header {
__le32 ilm_len;
__le32 dlm_len;
__le16 build_ver;
__le16 fw_ver;
u8 pad[4];
char build_time[16];
};
struct mt76_fw {
struct mt76_fw_header hdr;
u8 ivb[MT_MCU_IVB_SIZE];
u8 ilm[];
};
static int __mt7601u_dma_fw(struct mt7601u_dev *dev,
const struct mt7601u_dma_buf *dma_buf,
const void *data, u32 len, u32 dst_addr)
{
DECLARE_COMPLETION_ONSTACK(cmpl);
struct mt7601u_dma_buf buf = *dma_buf; /* we need to fake length */
__le32 reg;
u32 val;
int ret;
reg = cpu_to_le32(MT76_SET(MT_TXD_INFO_TYPE, DMA_PACKET) |
MT76_SET(MT_TXD_INFO_D_PORT, CPU_TX_PORT) |
MT76_SET(MT_TXD_INFO_LEN, len));
memcpy(buf.buf, &reg, sizeof(reg));
memcpy(buf.buf + sizeof(reg), data, len);
memset(buf.buf + sizeof(reg) + len, 0, 8);
ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE,
MT_FCE_DMA_ADDR, dst_addr);
if (ret)
return ret;
len = roundup(len, 4);
ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE,
MT_FCE_DMA_LEN, len << 16);
if (ret)
return ret;
buf.len = MT_DMA_HDR_LEN + len + 4;
ret = mt7601u_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD,
&buf, GFP_KERNEL,
mt7601u_complete_urb, &cmpl);
if (ret)
return ret;
if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) {
dev_err(dev->dev, "Error: firmware upload timed out\n");
usb_kill_urb(buf.urb);
return -ETIMEDOUT;
}
if (mt7601u_urb_has_error(buf.urb)) {
dev_err(dev->dev, "Error: firmware upload urb failed:%d\n",
buf.urb->status);
return buf.urb->status;
}
val = mt7601u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX);
val++;
mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val);
return 0;
}
static int
mt7601u_dma_fw(struct mt7601u_dev *dev, struct mt7601u_dma_buf *dma_buf,
const void *data, int len, u32 dst_addr)
{
int n, ret;
if (len == 0)
return 0;
n = min(MCU_FW_URB_MAX_PAYLOAD, len);
ret = __mt7601u_dma_fw(dev, dma_buf, data, n, dst_addr);
if (ret)
return ret;
if (!mt76_poll_msec(dev, MT_MCU_COM_REG1, BIT(31), BIT(31), 500))
return -ETIMEDOUT;
return mt7601u_dma_fw(dev, dma_buf, data + n, len - n, dst_addr + n);
}
static int
mt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw)
{
struct mt7601u_dma_buf dma_buf;
void *ivb;
u32 ilm_len, dlm_len;
int i, ret;
ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL);
if (!ivb || mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) {
ret = -ENOMEM;
goto error;
}
ilm_len = le32_to_cpu(fw->hdr.ilm_len) - sizeof(fw->ivb);
dev_dbg(dev->dev, "loading FW - ILM %u + IVB %zu\n",
ilm_len, sizeof(fw->ivb));
ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm, ilm_len, sizeof(fw->ivb));
if (ret)
goto error;
dlm_len = le32_to_cpu(fw->hdr.dlm_len);
dev_dbg(dev->dev, "loading FW - DLM %u\n", dlm_len);
ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm + ilm_len,
dlm_len, MT_MCU_DLM_OFFSET);
if (ret)
goto error;
ret = mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT,
0x12, 0, ivb, sizeof(fw->ivb));
if (ret < 0)
goto error;
ret = 0;
for (i = 100; i && !firmware_running(dev); i--)
msleep(10);
if (!i) {
ret = -ETIMEDOUT;
goto error;
}
dev_dbg(dev->dev, "Firmware running!\n");
error:
kfree(ivb);
mt7601u_usb_free_buf(dev, &dma_buf);
return ret;
}
static int mt7601u_load_firmware(struct mt7601u_dev *dev)
{
const struct firmware *fw;
const struct mt76_fw_header *hdr;
int len, ret;
u32 val;
mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN));
if (firmware_running(dev))
return 0;
ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev);
if (ret)
return ret;
if (!fw || !fw->data || fw->size < sizeof(*hdr))
goto err_inv_fw;
hdr = (const struct mt76_fw_header *) fw->data;
if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE)
goto err_inv_fw;
len = sizeof(*hdr);
len += le32_to_cpu(hdr->ilm_len);
len += le32_to_cpu(hdr->dlm_len);
if (fw->size != len)
goto err_inv_fw;
val = le16_to_cpu(hdr->fw_ver);
dev_info(dev->dev,
"Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n",
(val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf,
le16_to_cpu(hdr->build_ver), hdr->build_time);
len = le32_to_cpu(hdr->ilm_len);
mt7601u_wr(dev, 0x94c, 0);
mt7601u_wr(dev, MT_FCE_PSE_CTRL, 0);
mt7601u_vendor_reset(dev);
msleep(5);
mt7601u_wr(dev, 0xa44, 0);
mt7601u_wr(dev, 0x230, 0x84210);
mt7601u_wr(dev, 0x400, 0x80c00);
mt7601u_wr(dev, 0x800, 1);
mt7601u_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN |
MT_PBF_CFG_TX1Q_EN |
MT_PBF_CFG_TX2Q_EN |
MT_PBF_CFG_TX3Q_EN));
mt7601u_wr(dev, MT_FCE_PSE_CTRL, 1);
mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN));
val = mt76_set(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_CLR);
val &= ~MT_USB_DMA_CFG_TX_CLR;
mt7601u_wr(dev, MT_USB_DMA_CFG, val);
/* FCE tx_fs_base_ptr */
mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
/* FCE tx_fs_max_cnt */
mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1);
/* FCE pdma enable */
mt7601u_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
/* FCE skip_fs_en */
mt7601u_wr(dev, MT_FCE_SKIP_FS, 3);
ret = mt7601u_upload_firmware(dev, (const struct mt76_fw *)fw->data);
release_firmware(fw);
return ret;
err_inv_fw:
dev_err(dev->dev, "Invalid firmware image\n");
release_firmware(fw);
return -ENOENT;
}
int mt7601u_mcu_init(struct mt7601u_dev *dev)
{
int ret;
mutex_init(&dev->mcu.mutex);
ret = mt7601u_load_firmware(dev);
if (ret)
return ret;
set_bit(MT7601U_STATE_MCU_RUNNING, &dev->state);
return 0;
}
int mt7601u_mcu_cmd_init(struct mt7601u_dev *dev)
{
int ret;
ret = mt7601u_mcu_function_select(dev, Q_SELECT, 1);
if (ret)
return ret;
init_completion(&dev->mcu.resp_cmpl);
if (mt7601u_usb_alloc_buf(dev, MCU_RESP_URB_SIZE, &dev->mcu.resp)) {
mt7601u_usb_free_buf(dev, &dev->mcu.resp);
return -ENOMEM;
}
ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP,
&dev->mcu.resp, GFP_KERNEL,
mt7601u_complete_urb, &dev->mcu.resp_cmpl);
if (ret) {
mt7601u_usb_free_buf(dev, &dev->mcu.resp);
return ret;
}
return 0;
}
void mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev)
{
usb_kill_urb(dev->mcu.resp.urb);
mt7601u_usb_free_buf(dev, &dev->mcu.resp);
}
/*
* 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
/*
* 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
此差异已折叠。
此差异已折叠。
/*
* 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
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* 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
/*
* 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;
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册