mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 05:16:57 +00:00
@@ -0,0 +1,9 @@
|
||||
EXTRA_CFLAGS += -Werror
|
||||
|
||||
obj-m := mt76pci.o
|
||||
|
||||
mt76pci-y := \
|
||||
pci.o dma.o \
|
||||
main.o init.o debugfs.o tx.o util.o \
|
||||
core.o mac.o eeprom.o mcu.o phy.o \
|
||||
trace.o
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include "mt76.h"
|
||||
#include "trace.h"
|
||||
|
||||
bool mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
|
||||
int timeout)
|
||||
{
|
||||
u32 cur;
|
||||
|
||||
timeout /= 10;
|
||||
do {
|
||||
cur = mt76_rr(dev, offset) & mask;
|
||||
if (cur == val)
|
||||
return true;
|
||||
|
||||
udelay(10);
|
||||
} while (timeout-- > 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
|
||||
int timeout)
|
||||
{
|
||||
u32 cur;
|
||||
|
||||
timeout /= 10;
|
||||
do {
|
||||
cur = mt76_rr(dev, offset) & mask;
|
||||
if (cur == val)
|
||||
return true;
|
||||
|
||||
msleep(10);
|
||||
} while (timeout-- > 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void mt76_write_reg_pairs(struct mt76_dev *dev,
|
||||
const struct mt76_reg_pair *data, int len)
|
||||
{
|
||||
while (len > 0) {
|
||||
mt76_wr(dev, data->reg, data->value);
|
||||
len--;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
void mt76_set_irq_mask(struct mt76_dev *dev, u32 clear, u32 set)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->irq_lock, flags);
|
||||
dev->irqmask &= ~clear;
|
||||
dev->irqmask |= set;
|
||||
mt76_wr(dev, MT_INT_MASK_CSR, dev->irqmask);
|
||||
spin_unlock_irqrestore(&dev->irq_lock, flags);
|
||||
}
|
||||
|
||||
irqreturn_t mt76_irq_handler(int irq, void *dev_instance)
|
||||
{
|
||||
struct mt76_dev *dev = dev_instance;
|
||||
u32 intr;
|
||||
|
||||
intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
|
||||
mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
|
||||
|
||||
if (!test_bit(MT76_STATE_INITAILIZED, &dev->state))
|
||||
return IRQ_NONE;
|
||||
|
||||
trace_dev_irq(dev, intr, dev->irqmask);
|
||||
|
||||
intr &= dev->irqmask;
|
||||
|
||||
if (intr & MT_INT_TX_DONE_ALL) {
|
||||
mt76_irq_disable(dev, MT_INT_TX_DONE_ALL);
|
||||
tasklet_schedule(&dev->tx_tasklet);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_RX_DONE(0)) {
|
||||
mt76_irq_disable(dev, MT_INT_RX_DONE(0));
|
||||
napi_schedule(&dev->napi);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_RX_DONE(1)) {
|
||||
mt76_irq_disable(dev, MT_INT_RX_DONE(1));
|
||||
tasklet_schedule(&dev->rx_tasklet);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_PRE_TBTT)
|
||||
tasklet_schedule(&dev->pre_tbtt_tasklet);
|
||||
|
||||
/* send buffered multicast frames now */
|
||||
if (intr & MT_INT_TBTT)
|
||||
mt76_kick_queue(dev, &dev->q_tx[MT_TXQ_PSD]);
|
||||
|
||||
if (intr & MT_INT_TX_STAT) {
|
||||
mt76_mac_poll_tx_status(dev, true);
|
||||
tasklet_schedule(&dev->tx_tasklet);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mt76_set_channel(struct mt76_dev *dev, struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int ret;
|
||||
|
||||
tasklet_disable(&dev->pre_tbtt_tasklet);
|
||||
cancel_delayed_work_sync(&dev->cal_work);
|
||||
|
||||
mt76_mac_stop(dev, true);
|
||||
ret = mt76_phy_set_channel(dev, chandef);
|
||||
mt76_mac_resume(dev);
|
||||
tasklet_enable(&dev->pre_tbtt_tasklet);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
|
||||
static int
|
||||
mt76_reg_set(void *data, u64 val)
|
||||
{
|
||||
struct mt76_dev *dev = data;
|
||||
|
||||
mt76_wr(dev, dev->debugfs_reg, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_reg_get(void *data, u64 *val)
|
||||
{
|
||||
struct mt76_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
|
||||
mt76_ampdu_stat_read(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mt76_dev *dev = file->private;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
seq_puts(file, "Length: ");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_printf(file, "%8d | ", i * 8 + j + 1);
|
||||
seq_puts(file, "\n");
|
||||
seq_puts(file, "Count: ");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_printf(file, "%8d | ", dev->aggr_stats[i * 8 + j]);
|
||||
seq_puts(file, "\n");
|
||||
seq_puts(file, "--------");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_puts(file, "-----------");
|
||||
seq_puts(file, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_ampdu_stat_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
return single_open(f, mt76_ampdu_stat_read, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_ampdu_stat = {
|
||||
.open = mt76_ampdu_stat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void mt76_init_debugfs(struct mt76_dev *dev)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = debugfs_create_dir("mt76", dev->hw->wiphy->debugfsdir);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
debugfs_create_blob("eeprom", S_IRUSR, dir, &dev->eeprom);
|
||||
debugfs_create_u8("temperature", S_IRUSR, dir, &dev->cal.temp);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
#include "dma.h"
|
||||
|
||||
struct mt76_txwi_cache {
|
||||
struct mt76_txwi txwi;
|
||||
dma_addr_t dma_addr;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static inline int
|
||||
mt76_rx_buf_offset(struct mt76_dev *dev)
|
||||
{
|
||||
BUILD_BUG_ON(MT_RX_HEADROOM < sizeof(struct mt76_rxwi));
|
||||
|
||||
return MT_RX_HEADROOM - sizeof(struct mt76_rxwi);
|
||||
}
|
||||
|
||||
static struct mt76_txwi_cache *
|
||||
mt76_alloc_txwi(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_txwi_cache *t;
|
||||
dma_addr_t addr;
|
||||
int size;
|
||||
|
||||
size = (sizeof(*t) + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1);
|
||||
t = devm_kzalloc(dev->dev, size, GFP_ATOMIC);
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
addr = dma_map_single(dev->dev, &t->txwi, sizeof(t->txwi), DMA_TO_DEVICE);
|
||||
t->dma_addr = addr;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct mt76_txwi_cache *
|
||||
__mt76_get_txwi(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_txwi_cache *t = NULL;
|
||||
|
||||
spin_lock_bh(&dev->lock);
|
||||
if (!list_empty(&dev->txwi_cache)) {
|
||||
t = list_first_entry(&dev->txwi_cache, struct mt76_txwi_cache,
|
||||
list);
|
||||
list_del(&t->list);
|
||||
}
|
||||
spin_unlock_bh(&dev->lock);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct mt76_txwi_cache *
|
||||
mt76_get_txwi(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_txwi_cache *t = __mt76_get_txwi(dev);
|
||||
|
||||
if (t)
|
||||
return t;
|
||||
|
||||
return mt76_alloc_txwi(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
|
||||
{
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&dev->lock);
|
||||
list_add(&t->list, &dev->txwi_cache);
|
||||
spin_unlock_bh(&dev->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q)
|
||||
{
|
||||
int size;
|
||||
int i;
|
||||
|
||||
size = q->ndesc * sizeof(struct mt76_desc);
|
||||
q->desc = dmam_alloc_coherent(dev->dev, size, &q->desc_dma, GFP_KERNEL);
|
||||
if (!q->desc)
|
||||
return -ENOMEM;
|
||||
|
||||
size = q->ndesc * sizeof(*q->entry);
|
||||
q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
|
||||
if (!q->entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* clear descriptors */
|
||||
for (i = 0; i < q->ndesc; i++)
|
||||
q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
|
||||
|
||||
iowrite32(q->desc_dma, &q->regs->desc_base);
|
||||
iowrite32(0, &q->regs->cpu_idx);
|
||||
iowrite32(0, &q->regs->dma_idx);
|
||||
iowrite32(q->ndesc, &q->regs->ring_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
|
||||
{
|
||||
iowrite32(q->head, &q->regs->cpu_idx);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_queue_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
u32 buf0, int len0, u32 buf1, int len1, u32 info)
|
||||
{
|
||||
struct mt76_desc *desc;
|
||||
u32 ctrl;
|
||||
int idx;
|
||||
|
||||
ctrl = MT76_SET(MT_DMA_CTL_SD_LEN0, len0) |
|
||||
MT76_SET(MT_DMA_CTL_SD_LEN1, len1) |
|
||||
(len1 ? MT_DMA_CTL_LAST_SEC1 : MT_DMA_CTL_LAST_SEC0);
|
||||
|
||||
idx = q->head;
|
||||
q->head = (q->head + 1) % q->ndesc;
|
||||
|
||||
desc = &q->desc[idx];
|
||||
|
||||
ACCESS_ONCE(desc->buf0) = cpu_to_le32(buf0);
|
||||
ACCESS_ONCE(desc->buf1) = cpu_to_le32(buf1);
|
||||
ACCESS_ONCE(desc->info) = cpu_to_le32(info);
|
||||
ACCESS_ONCE(desc->ctrl) = cpu_to_le32(ctrl);
|
||||
|
||||
q->queued++;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
void *buf;
|
||||
int frames = 0;
|
||||
int len = SKB_WITH_OVERHEAD(q->buf_size);
|
||||
int idx;
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
|
||||
while (q->queued < q->ndesc - 1) {
|
||||
int offset = mt76_rx_buf_offset(dev);
|
||||
|
||||
buf = kzalloc(len, GFP_ATOMIC);
|
||||
if (!buf)
|
||||
break;
|
||||
|
||||
addr = dma_map_single(dev->dev, buf, len, DMA_FROM_DEVICE);
|
||||
|
||||
if (dma_mapping_error(dev->dev, addr)) {
|
||||
kfree(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
idx = mt76_queue_add_buf(dev, q, addr + offset, len - offset,
|
||||
0, 0, 0);
|
||||
|
||||
q->entry[idx].buf = buf;
|
||||
frames++;
|
||||
}
|
||||
|
||||
if (frames)
|
||||
mt76_kick_queue(dev, q);
|
||||
|
||||
spin_unlock_bh(&q->lock);
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
int __mt76_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
|
||||
struct sk_buff *skb, u32 tx_info)
|
||||
{
|
||||
struct mt76_queue *q = &dev->q_tx[qid];
|
||||
dma_addr_t addr;
|
||||
int idx;
|
||||
|
||||
addr = dma_map_single(dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev->dev, addr))
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
idx = mt76_queue_add_buf(dev, q, addr, skb->len, 0, 0, tx_info);
|
||||
q->entry[idx].skb = skb;
|
||||
mt76_kick_queue(dev, q);
|
||||
spin_unlock_bh(&q->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
struct sk_buff *skb, struct mt76_wcid *wcid,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct mt76_txwi_cache *t;
|
||||
dma_addr_t addr;
|
||||
u32 tx_info = 0;
|
||||
int idx, ret, len;
|
||||
int qsel = MT_QSEL_EDCA;
|
||||
|
||||
ret = -ENOMEM;
|
||||
t = mt76_get_txwi(dev);
|
||||
if (!t)
|
||||
goto free;
|
||||
|
||||
dma_sync_single_for_cpu(dev->dev, t->dma_addr, sizeof(t->txwi),
|
||||
DMA_TO_DEVICE);
|
||||
mt76_mac_write_txwi(dev, &t->txwi, skb, wcid, sta);
|
||||
dma_sync_single_for_device(dev->dev, t->dma_addr, sizeof(t->txwi),
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
ret = mt76_insert_hdr_pad(skb);
|
||||
if (ret)
|
||||
goto put_txwi;
|
||||
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
qsel = 0;
|
||||
|
||||
len = skb->len + sizeof(t->txwi);
|
||||
len += mt76_mac_skb_tx_overhead(dev, skb);
|
||||
tx_info = MT76_SET(MT_TXD_INFO_LEN, len) |
|
||||
MT76_SET(MT_TXD_INFO_QSEL, qsel) |
|
||||
MT_TXD_INFO_80211;
|
||||
|
||||
if (!wcid || wcid->hw_key_idx < 0)
|
||||
tx_info |= MT_TXD_INFO_WIV;
|
||||
|
||||
addr = dma_map_single(dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev->dev, addr))
|
||||
goto put_txwi;
|
||||
|
||||
idx = mt76_queue_add_buf(dev, q, t->dma_addr, sizeof(t->txwi),
|
||||
addr, skb->len, tx_info);
|
||||
q->entry[idx].skb = skb;
|
||||
q->entry[idx].txwi = t;
|
||||
|
||||
return idx;
|
||||
|
||||
put_txwi:
|
||||
mt76_put_txwi(dev, t);
|
||||
free:
|
||||
ieee80211_free_txskb(dev->hw, skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
|
||||
if (!q->queued)
|
||||
goto out;
|
||||
|
||||
if (!flush && !(q->desc[q->tail].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
|
||||
goto out;
|
||||
|
||||
ret = q->tail;
|
||||
q->tail = (q->tail + 1) % q->ndesc;
|
||||
q->queued--;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&q->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_tx_cleanup_entry(struct mt76_dev *dev, struct mt76_queue *q, int idx,
|
||||
bool flush)
|
||||
{
|
||||
struct mt76_queue_entry *e = &q->entry[idx];
|
||||
struct sk_buff *skb = e->skb;
|
||||
struct mt76_txwi_cache *txwi = e->txwi;
|
||||
dma_addr_t skb_addr;
|
||||
bool schedule = e->schedule;
|
||||
|
||||
if (txwi)
|
||||
skb_addr = ACCESS_ONCE(q->desc[idx].buf1);
|
||||
else
|
||||
skb_addr = ACCESS_ONCE(q->desc[idx].buf0);
|
||||
|
||||
dma_unmap_single(dev->dev, skb_addr, skb->len, DMA_TO_DEVICE);
|
||||
e->skb = NULL;
|
||||
e->txwi = NULL;
|
||||
e->schedule = false;
|
||||
|
||||
if (txwi) {
|
||||
skb_orphan(skb);
|
||||
mt76_mac_queue_txdone(dev, skb, &txwi->txwi);
|
||||
mt76_put_txwi(dev, txwi);
|
||||
|
||||
if (schedule) {
|
||||
spin_lock_bh(&q->lock);
|
||||
q->swq_queued--;
|
||||
if (!flush)
|
||||
mt76_txq_schedule(dev, q);
|
||||
spin_unlock_bh(&q->lock);
|
||||
}
|
||||
} else {
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_tx_cleanup_queue(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
|
||||
{
|
||||
int idx;
|
||||
|
||||
do {
|
||||
idx = mt76_dma_dequeue(dev, q, flush);
|
||||
if (idx < 0)
|
||||
break;
|
||||
|
||||
mt76_tx_cleanup_entry(dev, q, idx, flush);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static void *
|
||||
mt76_rx_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx, int *len)
|
||||
{
|
||||
struct mt76_queue_entry *e;
|
||||
dma_addr_t buf_addr;
|
||||
void *buf;
|
||||
|
||||
e = &q->entry[idx];
|
||||
buf = e->buf;
|
||||
buf_addr = ACCESS_ONCE(q->desc[idx].buf0);
|
||||
if (len) {
|
||||
u32 ctl = ACCESS_ONCE(q->desc[idx].ctrl);
|
||||
*len = MT76_GET(MT_DMA_CTL_SD_LEN0, ctl);
|
||||
}
|
||||
dma_unmap_single(dev->dev, buf_addr, q->buf_size, DMA_FROM_DEVICE);
|
||||
e->buf = NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
|
||||
{
|
||||
void *buf;
|
||||
int idx;
|
||||
|
||||
do {
|
||||
idx = mt76_dma_dequeue(dev, q, true);
|
||||
if (idx < 0)
|
||||
break;
|
||||
|
||||
buf = mt76_rx_get_buf(dev, q, idx, NULL);
|
||||
kfree(buf);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_init_tx_queue(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
int idx, int n_desc, bool mcu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
q->regs = dev->regs + MT_TX_RING_BASE + idx * MT_RING_SIZE;
|
||||
q->ndesc = n_desc;
|
||||
|
||||
ret = mt76_alloc_queue(dev, q);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_irq_enable(dev, MT_INT_TX_DONE(idx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_process_rx_skb(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
struct sk_buff *skb, u32 info)
|
||||
{
|
||||
void *rxwi = skb->data - sizeof(struct mt76_rxwi);
|
||||
|
||||
if (q == &dev->mcu.q_rx) {
|
||||
u32 *rxfce;
|
||||
|
||||
/* No RXWI header, data starts with payload */
|
||||
skb_push(skb, sizeof(struct mt76_rxwi));
|
||||
|
||||
rxfce = (u32 *) skb->cb;
|
||||
*rxfce = info;
|
||||
|
||||
skb_queue_tail(&dev->mcu.res_q, skb);
|
||||
wake_up(&dev->mcu.wait);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(skb->cb, 0, sizeof(skb->cb));
|
||||
if (mt76_mac_process_rx(dev, skb, rxwi)) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
mt76_rx(dev, skb);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q, int budget)
|
||||
{
|
||||
struct mt76_desc *desc;
|
||||
struct sk_buff *skb;
|
||||
unsigned char *data;
|
||||
int idx, len;
|
||||
int done = 0;
|
||||
|
||||
while (done < budget) {
|
||||
idx = mt76_dma_dequeue(dev, q, false);
|
||||
if (idx < 0)
|
||||
break;
|
||||
|
||||
data = mt76_rx_get_buf(dev, q, idx, &len);
|
||||
skb = build_skb(data, 0);
|
||||
if (!skb) {
|
||||
kfree(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
skb_reserve(skb, MT_RX_HEADROOM);
|
||||
skb_put(skb, len);
|
||||
|
||||
desc = &q->desc[idx];
|
||||
mt76_process_rx_skb(dev, q, skb, le32_to_cpu(desc->info));
|
||||
done++;
|
||||
}
|
||||
|
||||
mt76_dma_rx_fill(dev, q);
|
||||
return done;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_init_rx_queue(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
int idx, int n_desc, int bufsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
q->regs = dev->regs + MT_RX_RING_BASE + idx * MT_RING_SIZE;
|
||||
q->ndesc = n_desc;
|
||||
q->buf_size = bufsize;
|
||||
|
||||
ret = mt76_alloc_queue(dev, q);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_irq_enable(dev, MT_INT_RX_DONE(idx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_tx_tasklet(unsigned long data)
|
||||
{
|
||||
struct mt76_dev *dev = (struct mt76_dev *) data;
|
||||
int i;
|
||||
|
||||
mt76_mac_process_tx_status_fifo(dev);
|
||||
|
||||
for (i = ARRAY_SIZE(dev->q_tx) - 1; i >= 0; i--)
|
||||
mt76_tx_cleanup_queue(dev, &dev->q_tx[i], false);
|
||||
|
||||
mt76_mac_poll_tx_status(dev, false);
|
||||
mt76_irq_enable(dev, MT_INT_TX_DONE_ALL);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_dma_rx_poll(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct mt76_dev *dev = container_of(napi, struct mt76_dev, napi);
|
||||
int done;
|
||||
|
||||
done = mt76_process_rx_queue(dev, &dev->q_rx, budget);
|
||||
|
||||
if (done < budget) {
|
||||
napi_complete(napi);
|
||||
mt76_irq_enable(dev, MT_INT_RX_DONE(0));
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_rx_tasklet(unsigned long data)
|
||||
{
|
||||
struct mt76_dev *dev = (struct mt76_dev *) data;
|
||||
|
||||
mt76_process_rx_queue(dev, &dev->mcu.q_rx, dev->mcu.q_rx.ndesc);
|
||||
|
||||
mt76_irq_enable(dev, MT_INT_RX_DONE(1));
|
||||
}
|
||||
|
||||
int mt76_dma_init(struct mt76_dev *dev)
|
||||
{
|
||||
static const u8 wmm_queue_map[] = {
|
||||
[IEEE80211_AC_BE] = 0,
|
||||
[IEEE80211_AC_BK] = 1,
|
||||
[IEEE80211_AC_VI] = 2,
|
||||
[IEEE80211_AC_VO] = 3,
|
||||
};
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
init_waitqueue_head(&dev->mcu.wait);
|
||||
skb_queue_head_init(&dev->mcu.res_q);
|
||||
|
||||
spin_lock_init(&dev->q_rx.lock);
|
||||
spin_lock_init(&dev->mcu.q_rx.lock);
|
||||
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) {
|
||||
spin_lock_init(&dev->q_tx[i].lock);
|
||||
INIT_LIST_HEAD(&dev->q_tx[i].swq);
|
||||
}
|
||||
|
||||
init_dummy_netdev(&dev->napi_dev);
|
||||
netif_napi_add(&dev->napi_dev, &dev->napi, mt76_dma_rx_poll, 64);
|
||||
|
||||
tasklet_init(&dev->tx_tasklet, mt76_tx_tasklet, (unsigned long) dev);
|
||||
tasklet_init(&dev->rx_tasklet, mt76_rx_tasklet, (unsigned long) dev);
|
||||
|
||||
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) {
|
||||
ret = mt76_init_tx_queue(dev, &dev->q_tx[i], wmm_queue_map[i],
|
||||
MT_TX_RING_SIZE, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mt76_init_tx_queue(dev, &dev->q_tx[MT_TXQ_PSD],
|
||||
MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_init_tx_queue(dev, &dev->q_tx[MT_TXQ_MCU],
|
||||
MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_init_rx_queue(dev, &dev->mcu.q_rx, 1, MT_MCU_RING_SIZE,
|
||||
MT_RX_BUF_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_init_rx_queue(dev, &dev->q_rx, 0,
|
||||
MT_RX_RING_SIZE, MT_RX_BUF_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_dma_rx_fill(dev, &dev->q_rx);
|
||||
mt76_dma_rx_fill(dev, &dev->mcu.q_rx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76_dma_cleanup(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_txwi_cache *t;
|
||||
int i;
|
||||
|
||||
tasklet_kill(&dev->tx_tasklet);
|
||||
tasklet_kill(&dev->rx_tasklet);
|
||||
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++)
|
||||
mt76_tx_cleanup_queue(dev, &dev->q_tx[i], true);
|
||||
mt76_rx_cleanup(dev, &dev->q_rx);
|
||||
mt76_rx_cleanup(dev, &dev->mcu.q_rx);
|
||||
|
||||
while ((t = __mt76_get_txwi(dev)) != NULL)
|
||||
dma_unmap_single(dev->dev, t->dma_addr, sizeof(t->txwi),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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_DMA_H
|
||||
#define __MT76_DMA_H
|
||||
|
||||
#define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0)
|
||||
#define MT_DMA_CTL_LAST_SEC1 BIT(14)
|
||||
#define MT_DMA_CTL_BURST BIT(15)
|
||||
#define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16)
|
||||
#define MT_DMA_CTL_LAST_SEC0 BIT(30)
|
||||
#define MT_DMA_CTL_DMA_DONE BIT(31)
|
||||
|
||||
#define MT_TXD_INFO_LEN GENMASK(13, 0)
|
||||
#define MT_TXD_INFO_NEXT_VLD BIT(16)
|
||||
#define MT_TXD_INFO_TX_BURST BIT(17)
|
||||
#define MT_TXD_INFO_80211 BIT(19)
|
||||
#define MT_TXD_INFO_TSO BIT(20)
|
||||
#define MT_TXD_INFO_CSO BIT(21)
|
||||
#define MT_TXD_INFO_WIV BIT(24)
|
||||
#define MT_TXD_INFO_QSEL GENMASK(26, 25)
|
||||
#define MT_TXD_INFO_TCO BIT(29)
|
||||
#define MT_TXD_INFO_UCO BIT(30)
|
||||
#define MT_TXD_INFO_ICO BIT(31)
|
||||
|
||||
#define MT_RX_FCE_INFO_LEN GENMASK(13, 0)
|
||||
#define MT_RX_FCE_INFO_SELF_GEN BIT(15)
|
||||
#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16)
|
||||
#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20)
|
||||
#define MT_RX_FCE_INFO_PCIE_INTR BIT(24)
|
||||
#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25)
|
||||
#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27)
|
||||
#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30)
|
||||
|
||||
enum mt76_qsel {
|
||||
MT_QSEL_MGMT,
|
||||
MT_QSEL_HCCA,
|
||||
MT_QSEL_EDCA,
|
||||
MT_QSEL_EDCA_2,
|
||||
};
|
||||
|
||||
struct mt76_desc {
|
||||
__le32 buf0;
|
||||
__le32 ctrl;
|
||||
__le32 buf1;
|
||||
__le32 info;
|
||||
} __packed __aligned(4);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
#define EE_FIELD(_name, _value) [MT_EE_##_name] = (_value) | 1
|
||||
|
||||
static int
|
||||
mt76_eeprom_copy(struct mt76_dev *dev, enum mt76_eeprom_field field,
|
||||
void *dest, int len)
|
||||
{
|
||||
if (field + len > dev->eeprom.size)
|
||||
return -1;
|
||||
|
||||
memcpy(dest, dev->eeprom.data + field, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_eeprom_get_macaddr(struct mt76_dev *dev)
|
||||
{
|
||||
void *src = dev->eeprom.data + MT_EE_MAC_ADDR;
|
||||
|
||||
memcpy(dev->macaddr, src, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt76_get_of_eeprom(struct mt76_dev *dev, int len)
|
||||
{
|
||||
int ret = -ENOENT;
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node *np = dev->dev->of_node;
|
||||
struct mtd_info *mtd;
|
||||
const __be32 *list;
|
||||
const char *part;
|
||||
phandle phandle;
|
||||
int offset = 0;
|
||||
int size, retlen;
|
||||
|
||||
if (!np)
|
||||
return -ENOENT;
|
||||
|
||||
list = of_get_property(np, "mediatek,mtd-eeprom", &size);
|
||||
if (!list)
|
||||
return -ENOENT;
|
||||
|
||||
phandle = be32_to_cpup(list++);
|
||||
if (!phandle)
|
||||
return -ENOENT;
|
||||
|
||||
np = of_find_node_by_phandle(phandle);
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
part = of_get_property(np, "label", NULL);
|
||||
if (!part)
|
||||
part = np->name;
|
||||
|
||||
mtd = get_mtd_device_nm(part);
|
||||
if (IS_ERR(mtd))
|
||||
return PTR_ERR(mtd);
|
||||
|
||||
if (size <= sizeof(*list))
|
||||
return -EINVAL;
|
||||
|
||||
offset = be32_to_cpup(list);
|
||||
ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data);
|
||||
put_mtd_device(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (retlen < len)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_eeprom_parse_hw_cap(struct mt76_dev *dev)
|
||||
{
|
||||
u16 val = MT_EE_NIC_CONF_0;
|
||||
|
||||
switch (MT76_GET(MT_EE_NIC_CONF_0_BOARD_TYPE, val)) {
|
||||
case BOARD_TYPE_5GHZ:
|
||||
dev->cap.has_5ghz = true;
|
||||
break;
|
||||
case BOARD_TYPE_2GHZ:
|
||||
dev->cap.has_2ghz = true;
|
||||
break;
|
||||
default:
|
||||
dev->cap.has_2ghz = true;
|
||||
dev->cap.has_5ghz = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_get_of_overrides(struct mt76_dev *dev)
|
||||
{
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node *np = dev->dev->of_node;
|
||||
const __be32 *val;
|
||||
int size;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
val = of_get_property(np, "mediatek,2ghz", &size);
|
||||
if (val)
|
||||
dev->cap.has_2ghz = be32_to_cpup(val);
|
||||
|
||||
val = of_get_property(np, "mediatek,5ghz", &size);
|
||||
if (val)
|
||||
dev->cap.has_5ghz = be32_to_cpup(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_efuse_read(struct mt76_dev *dev, u16 addr, u8 *data)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
val = mt76_rr(dev, MT_EFUSE_CTRL);
|
||||
val &= ~(MT_EFUSE_CTRL_AIN |
|
||||
MT_EFUSE_CTRL_MODE);
|
||||
val |= MT76_SET(MT_EFUSE_CTRL_AIN, addr & ~0xf);
|
||||
val |= MT_EFUSE_CTRL_KICK;
|
||||
mt76_wr(dev, MT_EFUSE_CTRL, val);
|
||||
|
||||
if (!mt76_poll(dev, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
udelay(2);
|
||||
|
||||
val = mt76_rr(dev, MT_EFUSE_CTRL);
|
||||
if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) {
|
||||
memset(data, 0xff, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
val = mt76_rr(dev, MT_EFUSE_DATA(i));
|
||||
put_unaligned_le32(val, data + 4 * i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_get_efuse_data(struct mt76_dev *dev, int len)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i + 16 <= len; i += 16) {
|
||||
ret = mt76_efuse_read(dev, i, dev->eeprom.data + i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_eeprom_load(struct mt76_dev *dev)
|
||||
{
|
||||
int len = MT7662_EEPROM_SIZE;
|
||||
|
||||
dev->eeprom.size = len;
|
||||
dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
|
||||
if (!dev->eeprom.data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!mt76_get_of_eeprom(dev, len))
|
||||
return 0;
|
||||
|
||||
if (!mt76_get_efuse_data(dev, len))
|
||||
return 0;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mt76_sign_extend(u32 val, unsigned size)
|
||||
{
|
||||
bool sign = val & BIT(size - 1);
|
||||
val &= BIT(size - 1) - 1;
|
||||
return sign ? val : -val;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mt76_sign_extend_optional(u32 val, unsigned size)
|
||||
{
|
||||
bool enable = val & BIT(size);
|
||||
return enable ? mt76_sign_extend(val, size) : 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
field_valid(u8 val)
|
||||
{
|
||||
return val != 0 && val != 0xff;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_set_rx_gain_group(struct mt76_dev *dev, u8 val)
|
||||
{
|
||||
s8 *dest = dev->cal.rx.high_gain;
|
||||
|
||||
if (!field_valid(val)) {
|
||||
dest[0] = 0;
|
||||
dest[1] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dest[0] = mt76_sign_extend(val, 4);
|
||||
dest[1] = mt76_sign_extend(val >> 4, 4);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_set_rssi_offset(struct mt76_dev *dev, int chain, u8 val)
|
||||
{
|
||||
s8 *dest = dev->cal.rx.rssi_offset;
|
||||
|
||||
if (!field_valid(val)) {
|
||||
dest[chain] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dest[chain] = mt76_sign_extend(val, 6);
|
||||
}
|
||||
|
||||
static enum mt76_cal_channel_group
|
||||
mt76_get_cal_channel_group(int channel)
|
||||
{
|
||||
if (channel >= 184 && channel <= 196)
|
||||
return MT_CH_5G_JAPAN;
|
||||
if (channel <= 48)
|
||||
return MT_CH_5G_UNII_1;
|
||||
if (channel <= 64)
|
||||
return MT_CH_5G_UNII_2;
|
||||
if (channel <= 114)
|
||||
return MT_CH_5G_UNII_2E_1;
|
||||
if (channel <= 144)
|
||||
return MT_CH_5G_UNII_2E_2;
|
||||
return MT_CH_5G_UNII_3;
|
||||
}
|
||||
|
||||
static u8
|
||||
mt76_get_5g_rx_gain(struct mt76_dev *dev, u8 channel)
|
||||
{
|
||||
enum mt76_cal_channel_group group = mt76_get_cal_channel_group(channel);
|
||||
|
||||
switch (group) {
|
||||
case MT_CH_5G_JAPAN:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN);
|
||||
case MT_CH_5G_UNII_1:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN) >> 8;
|
||||
case MT_CH_5G_UNII_2:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN);
|
||||
case MT_CH_5G_UNII_2E_1:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN) >> 8;
|
||||
case MT_CH_5G_UNII_2E_2:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN);
|
||||
default:
|
||||
return mt76_eeprom_get(dev, MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
void mt76_read_rx_gain(struct mt76_dev *dev)
|
||||
{
|
||||
struct ieee80211_channel *chan = dev->chandef.chan;
|
||||
int channel = chan->hw_value;
|
||||
s8 lna_5g[3], lna_2g;
|
||||
u16 val;
|
||||
|
||||
if (chan->band == IEEE80211_BAND_2GHZ)
|
||||
val = mt76_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN) >> 8;
|
||||
else
|
||||
val = mt76_get_5g_rx_gain(dev, channel);
|
||||
|
||||
mt76_set_rx_gain_group(dev, val);
|
||||
|
||||
if (chan->band == IEEE80211_BAND_2GHZ) {
|
||||
val = mt76_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_0);
|
||||
mt76_set_rssi_offset(dev, 0, val);
|
||||
mt76_set_rssi_offset(dev, 1, val >> 8);
|
||||
} else {
|
||||
val = mt76_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_0);
|
||||
mt76_set_rssi_offset(dev, 0, val);
|
||||
mt76_set_rssi_offset(dev, 1, val >> 8);
|
||||
}
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_LNA_GAIN);
|
||||
lna_2g = val & 0xff;
|
||||
lna_5g[0] = val >> 8;
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_1);
|
||||
lna_5g[1] = val >> 8;
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_1);
|
||||
lna_5g[2] = val >> 8;
|
||||
|
||||
if (!field_valid(lna_5g[1]))
|
||||
lna_5g[1] = lna_5g[0];
|
||||
|
||||
if (!field_valid(lna_5g[2]))
|
||||
lna_5g[2] = lna_5g[0];
|
||||
|
||||
dev->cal.rx.mcu_gain = (lna_2g & 0xff);
|
||||
dev->cal.rx.mcu_gain |= (lna_5g[0] & 0xff) << 8;
|
||||
dev->cal.rx.mcu_gain |= (lna_5g[1] & 0xff) << 16;
|
||||
dev->cal.rx.mcu_gain |= (lna_5g[2] & 0xff) << 24;
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_NIC_CONF_1);
|
||||
if (val & MT_EE_NIC_CONF_1_LNA_EXT_2G)
|
||||
lna_2g = 0;
|
||||
if (val & MT_EE_NIC_CONF_1_LNA_EXT_5G)
|
||||
memset(lna_5g, 0, sizeof(lna_5g));
|
||||
|
||||
if (chan->band == IEEE80211_BAND_2GHZ)
|
||||
dev->cal.rx.lna_gain = lna_2g;
|
||||
else if (channel <= 64)
|
||||
dev->cal.rx.lna_gain = lna_5g[0];
|
||||
else if (channel <= 128)
|
||||
dev->cal.rx.lna_gain = lna_5g[1];
|
||||
else
|
||||
dev->cal.rx.lna_gain = lna_5g[2];
|
||||
|
||||
}
|
||||
|
||||
static s8
|
||||
mt76_rate_power_val(u8 val)
|
||||
{
|
||||
if (!field_valid(val))
|
||||
return 0;
|
||||
|
||||
return mt76_sign_extend_optional(val, 7);
|
||||
}
|
||||
|
||||
void mt76_get_rate_power(struct mt76_dev *dev, struct mt76_rate_power *t)
|
||||
{
|
||||
bool is_5ghz = false;
|
||||
u16 val;
|
||||
|
||||
is_5ghz = dev->chandef.chan->band == IEEE80211_BAND_5GHZ;
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_CCK);
|
||||
t->cck[0] = mt76_rate_power_val(val);
|
||||
t->cck[1] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
if (is_5ghz)
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_6M);
|
||||
else
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_6M);
|
||||
t->ofdm[0] = mt76_rate_power_val(val);
|
||||
t->ofdm[1] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
if (is_5ghz)
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_24M);
|
||||
else
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_24M);
|
||||
t->ofdm[2] = mt76_rate_power_val(val);
|
||||
t->ofdm[3] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS0);
|
||||
t->ht[0] = mt76_rate_power_val(val);
|
||||
t->ht[1] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS4);
|
||||
t->ht[2] = mt76_rate_power_val(val);
|
||||
t->ht[3] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS8);
|
||||
t->ht[4] = mt76_rate_power_val(val);
|
||||
t->ht[5] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS12);
|
||||
t->ht[6] = mt76_rate_power_val(val);
|
||||
t->ht[7] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS0);
|
||||
t->vht[0] = mt76_rate_power_val(val);
|
||||
t->vht[1] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS4);
|
||||
t->vht[2] = mt76_rate_power_val(val);
|
||||
t->vht[3] = mt76_rate_power_val(val >> 8);
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8);
|
||||
if (!is_5ghz)
|
||||
val >>= 8;
|
||||
t->vht[4] = mt76_rate_power_val(val);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_get_power_info_2g(struct mt76_dev *dev, struct mt76_tx_power_info *t,
|
||||
int chain, int offset)
|
||||
{
|
||||
int channel = dev->chandef.chan->hw_value;
|
||||
int delta_idx;
|
||||
u8 data[6];
|
||||
u16 val;
|
||||
|
||||
if (channel < 6)
|
||||
delta_idx = 3;
|
||||
else if (channel < 11)
|
||||
delta_idx = 4;
|
||||
else
|
||||
delta_idx = 5;
|
||||
|
||||
mt76_eeprom_copy(dev, offset, data, sizeof(data));
|
||||
|
||||
t->chain[chain].tssi_slope = data[0];
|
||||
t->chain[chain].tssi_offset = data[1];
|
||||
t->chain[chain].target_power = data[2];
|
||||
t->chain[chain].delta = data[delta_idx];
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_RF_2G_TSSI_OFF_TXPOWER);
|
||||
t->target_power = val >> 8;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_get_power_info_5g(struct mt76_dev *dev, struct mt76_tx_power_info *t,
|
||||
int chain, int offset)
|
||||
{
|
||||
int channel = dev->chandef.chan->hw_value;
|
||||
enum mt76_cal_channel_group group = mt76_get_cal_channel_group(channel);
|
||||
int delta_idx;
|
||||
u16 val;
|
||||
u8 data[5];
|
||||
|
||||
offset += group * MT_TX_POWER_GROUP_SIZE_5G;
|
||||
|
||||
if (channel >= 192)
|
||||
delta_idx = 4;
|
||||
else if (channel >= 484)
|
||||
delta_idx = 3;
|
||||
else if (channel < 44)
|
||||
delta_idx = 3;
|
||||
else if (channel < 52)
|
||||
delta_idx = 4;
|
||||
else if (channel < 58)
|
||||
delta_idx = 3;
|
||||
else if (channel < 98)
|
||||
delta_idx = 4;
|
||||
else if (channel < 106)
|
||||
delta_idx = 3;
|
||||
else if (channel < 116)
|
||||
delta_idx = 4;
|
||||
else if (channel < 130)
|
||||
delta_idx = 3;
|
||||
else if (channel < 149)
|
||||
delta_idx = 4;
|
||||
else if (channel < 157)
|
||||
delta_idx = 3;
|
||||
else
|
||||
delta_idx = 4;
|
||||
|
||||
mt76_eeprom_copy(dev, offset, data, sizeof(data));
|
||||
|
||||
t->chain[chain].tssi_slope = data[0];
|
||||
t->chain[chain].tssi_offset = data[1];
|
||||
t->chain[chain].target_power = data[2];
|
||||
t->chain[chain].delta = data[delta_idx];
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN);
|
||||
t->target_power = val & 0xff;
|
||||
}
|
||||
|
||||
void mt76_get_power_info(struct mt76_dev *dev, struct mt76_tx_power_info *t)
|
||||
{
|
||||
u16 bw40, bw80;
|
||||
memset(t, 0, sizeof(*t));
|
||||
|
||||
bw40 = mt76_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40);
|
||||
bw80 = mt76_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80);
|
||||
|
||||
if (dev->chandef.chan->band == IEEE80211_BAND_5GHZ) {
|
||||
bw40 >>= 8;
|
||||
mt76_get_power_info_5g(dev, t, 0, MT_EE_TX_POWER_0_START_5G);
|
||||
mt76_get_power_info_5g(dev, t, 1, MT_EE_TX_POWER_1_START_5G);
|
||||
} else {
|
||||
mt76_get_power_info_2g(dev, t, 0, MT_EE_TX_POWER_0_START_2G);
|
||||
mt76_get_power_info_2g(dev, t, 1, MT_EE_TX_POWER_1_START_2G);
|
||||
}
|
||||
|
||||
if (mt76_tssi_enabled(dev) || !field_valid(t->target_power))
|
||||
t->target_power = t->chain[0].target_power;
|
||||
|
||||
t->delta_bw40 = mt76_rate_power_val(bw40);
|
||||
t->delta_bw80 = mt76_rate_power_val(bw80);
|
||||
}
|
||||
|
||||
int mt76_get_temp_comp(struct mt76_dev *dev, struct mt76_temp_comp *t)
|
||||
{
|
||||
enum ieee80211_band band = dev->chandef.chan->band;
|
||||
u16 val, slope;
|
||||
u8 bounds;
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_NIC_CONF_1);
|
||||
if (!(val & MT_EE_NIC_CONF_1_TEMP_TX_ALC))
|
||||
return -EINVAL;
|
||||
|
||||
if (!mt76_ext_pa_enabled(dev, band))
|
||||
return -EINVAL;
|
||||
|
||||
val = mt76_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G) >> 8;
|
||||
if (!(val & BIT(7)))
|
||||
return -EINVAL;
|
||||
|
||||
t->temp_25_ref = val & 0x7f;
|
||||
if (band == IEEE80211_BAND_5GHZ) {
|
||||
slope = mt76_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_5G);
|
||||
bounds = mt76_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G);
|
||||
} else {
|
||||
slope = mt76_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_2G);
|
||||
bounds = mt76_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80) >> 8;
|
||||
}
|
||||
|
||||
t->high_slope = slope & 0xff;
|
||||
t->low_slope = slope >> 8;
|
||||
t->lower_bound = 0 - (bounds & 0xf);
|
||||
t->upper_bound = (bounds >> 4) & 0xf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool mt76_ext_pa_enabled(struct mt76_dev *dev, enum ieee80211_band band)
|
||||
{
|
||||
u16 conf1 = mt76_eeprom_get(dev, MT_EE_NIC_CONF_0);
|
||||
|
||||
if (band == IEEE80211_BAND_5GHZ)
|
||||
return !(conf1 & MT_EE_NIC_CONF_0_PA_INT_5G);
|
||||
else
|
||||
return !(conf1 & MT_EE_NIC_CONF_0_PA_INT_2G);
|
||||
}
|
||||
|
||||
int mt76_eeprom_init(struct mt76_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mt76_eeprom_load(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_eeprom_parse_hw_cap(dev);
|
||||
mt76_get_of_overrides(dev);
|
||||
|
||||
mt76_eeprom_get_macaddr(dev);
|
||||
if (!is_valid_ether_addr(dev->macaddr)) {
|
||||
eth_random_addr(dev->macaddr);
|
||||
dev_printk(KERN_INFO, dev->dev,
|
||||
"Invalid MAC address, using random address %pM\n",
|
||||
dev->macaddr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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_EEPROM_H
|
||||
#define __MT76_EEPROM_H
|
||||
|
||||
#include "mt76.h"
|
||||
|
||||
enum mt76_eeprom_field {
|
||||
MT_EE_CHIP_ID = 0x000,
|
||||
MT_EE_VERSION = 0x002,
|
||||
MT_EE_MAC_ADDR = 0x004,
|
||||
MT_EE_NIC_CONF_0 = 0x034,
|
||||
MT_EE_NIC_CONF_1 = 0x036,
|
||||
MT_EE_NIC_CONF_2 = 0x042,
|
||||
|
||||
MT_EE_XTAL_TRIM_1 = 0x03a,
|
||||
MT_EE_XTAL_TRIM_2 = 0x09e,
|
||||
|
||||
MT_EE_LNA_GAIN = 0x044,
|
||||
MT_EE_RSSI_OFFSET_2G_0 = 0x046,
|
||||
MT_EE_RSSI_OFFSET_2G_1 = 0x048,
|
||||
MT_EE_RSSI_OFFSET_5G_0 = 0x04a,
|
||||
MT_EE_RSSI_OFFSET_5G_1 = 0x04c,
|
||||
|
||||
MT_EE_TX_POWER_DELTA_BW40 = 0x050,
|
||||
MT_EE_TX_POWER_DELTA_BW80 = 0x052,
|
||||
|
||||
MT_EE_TX_POWER_EXT_PA_5G = 0x054,
|
||||
|
||||
MT_EE_TX_POWER_0_START_2G = 0x056,
|
||||
MT_EE_TX_POWER_1_START_2G = 0x05c,
|
||||
|
||||
/* used as byte arrays */
|
||||
#define MT_TX_POWER_GROUP_SIZE_5G 5
|
||||
#define MT_TX_POWER_GROUPS_5G 6
|
||||
MT_EE_TX_POWER_0_START_5G = 0x062,
|
||||
MT_EE_TX_POWER_1_START_5G = 0x080,
|
||||
|
||||
|
||||
MT_EE_TX_POWER_CCK = 0x0a0,
|
||||
MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2,
|
||||
MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4,
|
||||
MT_EE_TX_POWER_OFDM_5G_6M = 0x0b2,
|
||||
MT_EE_TX_POWER_OFDM_5G_24M = 0x0b4,
|
||||
MT_EE_TX_POWER_HT_MCS0 = 0x0a6,
|
||||
MT_EE_TX_POWER_HT_MCS4 = 0x0a8,
|
||||
MT_EE_TX_POWER_HT_MCS8 = 0x0aa,
|
||||
MT_EE_TX_POWER_HT_MCS12 = 0x0ac,
|
||||
MT_EE_TX_POWER_VHT_MCS0 = 0x0ba,
|
||||
MT_EE_TX_POWER_VHT_MCS4 = 0x0bc,
|
||||
MT_EE_TX_POWER_VHT_MCS8 = 0x0be,
|
||||
|
||||
MT_EE_RF_TEMP_COMP_SLOPE_5G = 0x0f2,
|
||||
MT_EE_RF_TEMP_COMP_SLOPE_2G = 0x0f4,
|
||||
|
||||
MT_EE_RF_2G_TSSI_OFF_TXPOWER = 0x0f6,
|
||||
MT_EE_RF_2G_RX_HIGH_GAIN = 0x0f8,
|
||||
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN = 0x0fa,
|
||||
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN = 0x0fc,
|
||||
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN = 0x0fe,
|
||||
|
||||
MT_EE_BT_RCAL_RESULT = 0x138,
|
||||
|
||||
__MT_EE_MAX
|
||||
};
|
||||
|
||||
#define MT_EE_NIC_CONF_0_PA_INT_2G BIT(8)
|
||||
#define MT_EE_NIC_CONF_0_PA_INT_5G BIT(9)
|
||||
#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12)
|
||||
|
||||
#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1)
|
||||
#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2)
|
||||
#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3)
|
||||
#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13)
|
||||
|
||||
#define MT_EE_NIC_CONF_2_RX_STREAM GENMASK(3, 0)
|
||||
#define MT_EE_NIC_CONF_2_TX_STREAM GENMASK(7, 4)
|
||||
#define MT_EE_NIC_CONF_2_HW_ANTDIV BIT(8)
|
||||
#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9)
|
||||
#define MT_EE_NIC_CONF_2_TEMP_DISABLE BIT(11)
|
||||
#define MT_EE_NIC_CONF_2_COEX_METHOD GENMASK(15, 13)
|
||||
|
||||
enum mt76_board_type {
|
||||
BOARD_TYPE_2GHZ = 1,
|
||||
BOARD_TYPE_5GHZ = 2,
|
||||
};
|
||||
|
||||
enum mt76_cal_channel_group {
|
||||
MT_CH_5G_JAPAN,
|
||||
MT_CH_5G_UNII_1,
|
||||
MT_CH_5G_UNII_2,
|
||||
MT_CH_5G_UNII_2E_1,
|
||||
MT_CH_5G_UNII_2E_2,
|
||||
MT_CH_5G_UNII_3,
|
||||
__MT_CH_MAX
|
||||
};
|
||||
|
||||
struct mt76_rate_power {
|
||||
s8 cck[2];
|
||||
s8 ofdm[4];
|
||||
s8 ht[8];
|
||||
s8 vht[5];
|
||||
};
|
||||
|
||||
struct mt76_tx_power_info {
|
||||
u8 target_power;
|
||||
|
||||
s8 delta_bw40;
|
||||
s8 delta_bw80;
|
||||
|
||||
struct {
|
||||
s8 tssi_slope;
|
||||
s8 tssi_offset;
|
||||
s8 target_power;
|
||||
s8 delta;
|
||||
} chain[MT_MAX_CHAINS];
|
||||
};
|
||||
|
||||
struct mt76_temp_comp {
|
||||
u8 temp_25_ref;
|
||||
int lower_bound; /* J */
|
||||
int upper_bound; /* J */
|
||||
unsigned int high_slope; /* J / dB */
|
||||
unsigned int low_slope; /* J / dB */
|
||||
};
|
||||
|
||||
static inline int
|
||||
mt76_eeprom_get(struct mt76_dev *dev, enum mt76_eeprom_field field)
|
||||
{
|
||||
if ((field & 1) || field >= __MT_EE_MAX)
|
||||
return -1;
|
||||
|
||||
return get_unaligned_le16(dev->eeprom.data + field);
|
||||
}
|
||||
|
||||
void mt76_get_rate_power(struct mt76_dev *dev, struct mt76_rate_power *t);
|
||||
void mt76_get_power_info(struct mt76_dev *dev, struct mt76_tx_power_info *t);
|
||||
int mt76_get_temp_comp(struct mt76_dev *dev, struct mt76_temp_comp *t);
|
||||
bool mt76_ext_pa_enabled(struct mt76_dev *dev, enum ieee80211_band band);
|
||||
void mt76_read_rx_gain(struct mt76_dev *dev);
|
||||
|
||||
static inline bool
|
||||
mt76_temp_tx_alc_enabled(struct mt76_dev *dev)
|
||||
{
|
||||
return mt76_eeprom_get(dev, MT_EE_NIC_CONF_1) &
|
||||
MT_EE_NIC_CONF_1_TEMP_TX_ALC;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mt76_tssi_enabled(struct mt76_dev *dev)
|
||||
{
|
||||
return !mt76_temp_tx_alc_enabled(dev) &&
|
||||
(mt76_eeprom_get(dev, MT_EE_NIC_CONF_1) &
|
||||
MT_EE_NIC_CONF_1_TX_ALC_EN);
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
mt76_has_ext_lna(struct mt76_dev *dev)
|
||||
{
|
||||
u32 val = mt76_eeprom_get(dev, MT_EE_NIC_CONF_1);
|
||||
|
||||
if (dev->chandef.chan->band == IEEE80211_BAND_2GHZ)
|
||||
return val & MT_EE_NIC_CONF_1_LNA_EXT_2G;
|
||||
else
|
||||
return val & MT_EE_NIC_CONF_1_LNA_EXT_5G;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
Copyright (c) 2014, Ralink, A MediaTek Company
|
||||
All rights reserved.
|
||||
|
||||
Redistribution. Redistribution and use in binary form, without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions must reproduce the above copyright notice and the
|
||||
following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
* Neither the name of Ralink Technology Corporation nor the names of its
|
||||
suppliers may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
* No reverse engineering, decompilation, or disassembly of this software
|
||||
is permitted.
|
||||
|
||||
Limited patent license. Ralink Technology Corporation grants a world-wide,
|
||||
royalty-free, non-exclusive license under patents it now or hereafter
|
||||
owns or controls to make, have made, use, import, offer to sell and
|
||||
sell ("Utilize") this software, but solely to the extent that any
|
||||
such patent is necessary to Utilize the software alone, or in
|
||||
combination with an operating system licensed under an approved Open
|
||||
Source license as listed by the Open Source Initiative at
|
||||
http://opensource.org/licenses. The patent license shall not apply to
|
||||
any other combinations which include this software. No hardware per
|
||||
se is licensed hereunder.
|
||||
|
||||
DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,849 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include "mt76.h"
|
||||
#include "eeprom.h"
|
||||
#include "mcu.h"
|
||||
|
||||
static bool
|
||||
mt76_wait_for_mac(struct mt76_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 500; i++) {
|
||||
switch (mt76_rr(dev, MT_MAC_CSR0)) {
|
||||
case 0:
|
||||
case ~0:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
wait_for_wpdma(struct mt76_dev *dev)
|
||||
{
|
||||
return mt76_poll(dev, MT_WPDMA_GLO_CFG,
|
||||
MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
|
||||
MT_WPDMA_GLO_CFG_RX_DMA_BUSY,
|
||||
0, 1000);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_mac_pbf_init(struct mt76_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = MT_PBF_SYS_CTRL_MCU_RESET |
|
||||
MT_PBF_SYS_CTRL_DMA_RESET |
|
||||
MT_PBF_SYS_CTRL_MAC_RESET |
|
||||
MT_PBF_SYS_CTRL_PBF_RESET |
|
||||
MT_PBF_SYS_CTRL_ASY_RESET;
|
||||
|
||||
mt76_set(dev, MT_PBF_SYS_CTRL, val);
|
||||
mt76_clear(dev, MT_PBF_SYS_CTRL, val);
|
||||
|
||||
mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f);
|
||||
mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_write_mac_initvals(struct mt76_dev *dev)
|
||||
{
|
||||
static const struct mt76_reg_pair vals[] = {
|
||||
/* Copied from MediaTek reference source */
|
||||
{ MT_PBF_SYS_CTRL, 0x00080c00 },
|
||||
{ MT_PBF_CFG, 0x1efebcff },
|
||||
{ MT_FCE_PSE_CTRL, 0x00000001 },
|
||||
{ MT_MAC_SYS_CTRL, 0x0000000c },
|
||||
{ MT_MAX_LEN_CFG, 0x003e3fff },
|
||||
{ MT_AMPDU_MAX_LEN_20M1S, 0xaaa99887 },
|
||||
{ MT_AMPDU_MAX_LEN_20M2S, 0x000000aa },
|
||||
{ MT_XIFS_TIME_CFG, 0x33a40d0a },
|
||||
{ MT_BKOFF_SLOT_CFG, 0x00000209 },
|
||||
{ MT_TBTT_SYNC_CFG, 0x00422010 },
|
||||
{ MT_PWR_PIN_CFG, 0x00000000 },
|
||||
{ 0x1238, 0x001700c8 },
|
||||
{ MT_TX_SW_CFG0, 0x00101001 },
|
||||
{ MT_TX_SW_CFG1, 0x00010000 },
|
||||
{ MT_TX_SW_CFG2, 0x00000000 },
|
||||
{ MT_TXOP_CTRL_CFG, 0x0000583f },
|
||||
{ MT_TX_RTS_CFG, 0x00092b20 },
|
||||
{ MT_TX_TIMEOUT_CFG, 0x000a2290 },
|
||||
{ MT_TX_RETRY_CFG, 0x47f01f0f },
|
||||
{ MT_EXP_ACK_TIME, 0x002c00dc },
|
||||
{ MT_TX_PROT_CFG6, 0xe3f42004 },
|
||||
{ MT_TX_PROT_CFG7, 0xe3f42084 },
|
||||
{ MT_TX_PROT_CFG8, 0xe3f42104 },
|
||||
{ MT_PIFS_TX_CFG, 0x00060fff },
|
||||
{ MT_RX_FILTR_CFG, 0x00015f97 },
|
||||
{ MT_LEGACY_BASIC_RATE, 0x0000017f },
|
||||
{ MT_HT_BASIC_RATE, 0x00008003 },
|
||||
{ MT_PN_PAD_MODE, 0x00000002 },
|
||||
{ MT_TXOP_HLDR_ET, 0x00000002 },
|
||||
{ 0xa44, 0x00000000 },
|
||||
{ MT_HEADER_TRANS_CTRL_REG, 0x00000000 },
|
||||
{ MT_TSO_CTRL, 0x00000000 },
|
||||
{ MT_AUX_CLK_CFG, 0x00000000 },
|
||||
{ MT_DACCLK_EN_DLY_CFG, 0x00000000 },
|
||||
{ MT_TX_ALC_CFG_4, 0x00000000 },
|
||||
{ MT_TX_ALC_VGA3, 0x00000000 },
|
||||
{ MT_TX_PWR_CFG_0, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_1, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_2, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_3, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_4, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_7, 0x3a3a3a3a },
|
||||
{ MT_TX_PWR_CFG_8, 0x0000003a },
|
||||
{ MT_TX_PWR_CFG_9, 0x0000003a },
|
||||
{ MT_EFUSE_CTRL, 0x0000d000 },
|
||||
{ MT_PAUSE_ENABLE_CONTROL1, 0x0000000a },
|
||||
{ MT_FCE_WLAN_FLOW_CONTROL1, 0x60401c18 },
|
||||
{ MT_WPDMA_DELAY_INT_CFG, 0x94ff0000 },
|
||||
{ MT_TX_SW_CFG3, 0x00000004 },
|
||||
{ MT_HT_FBK_TO_LEGACY, 0x00001818 },
|
||||
{ MT_VHT_HT_FBK_CFG1, 0xedcba980 },
|
||||
};
|
||||
|
||||
mt76_write_reg_pairs(dev, vals, ARRAY_SIZE(vals));
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_fixup_xtal(struct mt76_dev *dev)
|
||||
{
|
||||
u16 eep_val;
|
||||
s8 offset = 0;
|
||||
|
||||
eep_val = mt76_eeprom_get(dev, MT_EE_XTAL_TRIM_2);
|
||||
|
||||
offset = eep_val & 0x7f;
|
||||
if ((eep_val & 0xff) == 0xff)
|
||||
offset = 0;
|
||||
else if (eep_val & 0x80)
|
||||
offset = 0 - offset;
|
||||
|
||||
eep_val >>= 8;
|
||||
if (eep_val == 0x00 || eep_val == 0xff) {
|
||||
eep_val = mt76_eeprom_get(dev, MT_EE_XTAL_TRIM_1);
|
||||
eep_val &= 0xff;
|
||||
|
||||
if (eep_val == 0x00 || eep_val == 0xff)
|
||||
eep_val = 0x14;
|
||||
}
|
||||
|
||||
eep_val &= 0x7f;
|
||||
mt76_rmw_field(dev, MT_XO_CTRL5, MT_XO_CTRL5_C2_VAL, eep_val + offset);
|
||||
mt76_set(dev, MT_XO_CTRL6, MT_XO_CTRL6_C2_CTRL);
|
||||
|
||||
eep_val = mt76_eeprom_get(dev, MT_EE_NIC_CONF_2);
|
||||
switch (MT76_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) {
|
||||
case 0:
|
||||
mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80);
|
||||
break;
|
||||
case 1:
|
||||
mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_init_beacon_offsets(struct mt76_dev *dev)
|
||||
{
|
||||
u16 base = MT_BEACON_BASE;
|
||||
u32 regs[4] = {};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
u16 addr = dev->beacon_offsets[i];
|
||||
|
||||
regs[i / 4] |= ((addr - base) / 64) << (8 * (i % 4));
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]);
|
||||
}
|
||||
|
||||
int mt76_mac_reset(struct mt76_dev *dev, bool hard)
|
||||
{
|
||||
static const u8 null_addr[ETH_ALEN] = {};
|
||||
u32 val;
|
||||
int i, k;
|
||||
|
||||
if (!mt76_wait_for_mac(dev))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
val = mt76_rr(dev, MT_WPDMA_GLO_CFG);
|
||||
|
||||
val &= ~(MT_WPDMA_GLO_CFG_TX_DMA_EN |
|
||||
MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
|
||||
MT_WPDMA_GLO_CFG_RX_DMA_EN |
|
||||
MT_WPDMA_GLO_CFG_RX_DMA_BUSY |
|
||||
MT_WPDMA_GLO_CFG_DMA_BURST_SIZE);
|
||||
val |= MT76_SET(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3);
|
||||
|
||||
mt76_wr(dev, MT_WPDMA_GLO_CFG, val);
|
||||
|
||||
mt76_mac_pbf_init(dev);
|
||||
mt76_write_mac_initvals(dev);
|
||||
mt76_fixup_xtal(dev);
|
||||
|
||||
mt76_clear(dev, MT_MAC_SYS_CTRL,
|
||||
MT_MAC_SYS_CTRL_RESET_CSR |
|
||||
MT_MAC_SYS_CTRL_RESET_BBP);
|
||||
|
||||
if (is_mt7612(dev))
|
||||
mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN);
|
||||
|
||||
mt76_set(dev, MT_EXT_CCA_CFG, 0x0000f000);
|
||||
mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31));
|
||||
|
||||
mt76_wr(dev, MT_RF_BYPASS_0, 0x06000000);
|
||||
mt76_wr(dev, MT_RF_SETTING_0, 0x08800000);
|
||||
msleep(5);
|
||||
mt76_wr(dev, MT_RF_BYPASS_0, 0x00000000);
|
||||
|
||||
mt76_wr(dev, MT_MCU_CLOCK_CTL, 0x1401);
|
||||
mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN);
|
||||
|
||||
mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr));
|
||||
mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4));
|
||||
|
||||
mt76_wr(dev, MT_MAC_BSSID_DW0, get_unaligned_le32(dev->macaddr));
|
||||
mt76_wr(dev, MT_MAC_BSSID_DW1, get_unaligned_le16(dev->macaddr + 4) |
|
||||
MT76_SET(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */
|
||||
MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT);
|
||||
|
||||
/* Fire a pre-TBTT interrupt 8 ms before TBTT */
|
||||
mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT,
|
||||
8 << 4);
|
||||
mt76_wr(dev, MT_INT_TIMER_EN, 0);
|
||||
|
||||
mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff);
|
||||
if (!hard)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
mt76_mac_wcid_setup(dev, i, 0, NULL);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
for (k = 0; k < 4; k++)
|
||||
mt76_mac_shared_key_setup(dev, i, k, NULL);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
mt76_mac_set_bssid(dev, i, null_addr);
|
||||
mt76_mac_set_beacon(dev, i, NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
mt76_rr(dev, MT_TX_STAT_FIFO);
|
||||
|
||||
mt76_set(dev, MT_MAC_APC_BSSID_H(0), MT_MAC_APC_BSSID0_H_EN);
|
||||
mt76_init_beacon_offsets(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_mac_start(struct mt76_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
mt76_rr(dev, MT_TX_AGG_CNT(i));
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
mt76_rr(dev, MT_TX_STAT_FIFO);
|
||||
|
||||
memset(dev->aggr_stats, 0, sizeof(dev->aggr_stats));
|
||||
|
||||
mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
|
||||
wait_for_wpdma(dev);
|
||||
udelay(50);
|
||||
|
||||
mt76_set(dev, MT_WPDMA_GLO_CFG,
|
||||
MT_WPDMA_GLO_CFG_TX_DMA_EN |
|
||||
MT_WPDMA_GLO_CFG_RX_DMA_EN |
|
||||
MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
|
||||
|
||||
mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
|
||||
|
||||
mt76_wr(dev, MT_MAC_SYS_CTRL,
|
||||
MT_MAC_SYS_CTRL_ENABLE_TX |
|
||||
MT_MAC_SYS_CTRL_ENABLE_RX);
|
||||
|
||||
mt76_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL | MT_INT_TX_STAT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76_mac_stop(struct mt76_dev *dev, bool force)
|
||||
{
|
||||
bool stopped = false;
|
||||
u32 rts_cfg;
|
||||
int i;
|
||||
|
||||
mt76_wr(dev, MT_MAC_SYS_CTRL, 0);
|
||||
|
||||
rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG);
|
||||
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT);
|
||||
|
||||
/* Wait for MAC to become idle */
|
||||
for (i = 0; i < 300; i++) {
|
||||
if (mt76_rr(dev, MT_MAC_STATUS) &
|
||||
(MT_MAC_STATUS_RX | MT_MAC_STATUS_TX))
|
||||
continue;
|
||||
|
||||
if (mt76_rr(dev, MT_BBP(IBI, 12)))
|
||||
continue;
|
||||
|
||||
stopped = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (force && !stopped) {
|
||||
mt76_set(dev, MT_BBP(CORE, 4), BIT(2));
|
||||
mt76_clear(dev, MT_BBP(CORE, 4), BIT(2));
|
||||
|
||||
mt76_set(dev, MT_BBP(CORE, 4), BIT(1));
|
||||
mt76_clear(dev, MT_BBP(CORE, 4), BIT(1));
|
||||
}
|
||||
|
||||
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg);
|
||||
}
|
||||
|
||||
void mt76_mac_resume(struct mt76_dev *dev)
|
||||
{
|
||||
mt76_wr(dev, MT_MAC_SYS_CTRL,
|
||||
MT_MAC_SYS_CTRL_ENABLE_TX |
|
||||
MT_MAC_SYS_CTRL_ENABLE_RX);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mt76_power_on_rf_patch(struct mt76_dev *dev)
|
||||
{
|
||||
mt76_set(dev, 0x10130, BIT(0) | BIT(16));
|
||||
udelay(1);
|
||||
|
||||
mt76_clear(dev, 0x1001c, 0xff);
|
||||
mt76_set(dev, 0x1001c, 0x30);
|
||||
|
||||
mt76_wr(dev, 0x10014, 0x484f);
|
||||
udelay(1);
|
||||
|
||||
mt76_set(dev, 0x10130, BIT(17));
|
||||
udelay(125);
|
||||
|
||||
mt76_clear(dev, 0x10130, BIT(16));
|
||||
udelay(50);
|
||||
|
||||
mt76_set(dev, 0x1014c, BIT(19) | BIT(20));
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_power_on_rf(struct mt76_dev *dev, int unit)
|
||||
{
|
||||
int shift = unit ? 8 : 0;
|
||||
|
||||
/* Enable RF BG */
|
||||
mt76_set(dev, 0x10130, BIT(0) << shift);
|
||||
udelay(10);
|
||||
|
||||
/* Enable RFDIG LDO/AFE/ABB/ADDA */
|
||||
mt76_set(dev, 0x10130, (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift);
|
||||
udelay(10);
|
||||
|
||||
/* Switch RFDIG power to internal LDO */
|
||||
mt76_clear(dev, 0x10130, BIT(2) << shift);
|
||||
udelay(10);
|
||||
|
||||
mt76_power_on_rf_patch(dev);
|
||||
|
||||
mt76_set(dev, 0x530, 0xf);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_power_on(struct mt76_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Turn on WL MTCMOS */
|
||||
mt76_set(dev, MT_WLAN_MTC_CTRL, MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP);
|
||||
|
||||
val = MT_WLAN_MTC_CTRL_STATE_UP |
|
||||
MT_WLAN_MTC_CTRL_PWR_ACK |
|
||||
MT_WLAN_MTC_CTRL_PWR_ACK_S;
|
||||
|
||||
mt76_poll(dev, MT_WLAN_MTC_CTRL, val, val, 1000);
|
||||
|
||||
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0x7f << 16);
|
||||
udelay(10);
|
||||
|
||||
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xf << 24);
|
||||
udelay(10);
|
||||
|
||||
mt76_set(dev, MT_WLAN_MTC_CTRL, 0xf << 24);
|
||||
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xfff);
|
||||
|
||||
/* Turn on AD/DA power down */
|
||||
mt76_clear(dev, 0x11204, BIT(3));
|
||||
|
||||
/* WLAN function enable */
|
||||
mt76_set(dev, 0x10080, BIT(0));
|
||||
|
||||
/* Release BBP software reset */
|
||||
mt76_clear(dev, 0x10064, BIT(18));
|
||||
|
||||
mt76_power_on_rf(dev, 0);
|
||||
mt76_power_on_rf(dev, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_set_wlan_state(struct mt76_dev *dev, bool enable)
|
||||
{
|
||||
u32 val = mt76_rr(dev, MT_WLAN_FUN_CTRL);
|
||||
|
||||
if (enable)
|
||||
val |= (MT_WLAN_FUN_CTRL_WLAN_EN |
|
||||
MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
|
||||
else
|
||||
val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN |
|
||||
MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
|
||||
|
||||
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_reset_wlan(struct mt76_dev *dev, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = mt76_rr(dev, MT_WLAN_FUN_CTRL);
|
||||
|
||||
val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL;
|
||||
|
||||
if (val & MT_WLAN_FUN_CTRL_WLAN_EN) {
|
||||
val |= MT_WLAN_FUN_CTRL_WLAN_RESET_RF;
|
||||
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
|
||||
udelay(20);
|
||||
|
||||
val &= ~MT_WLAN_FUN_CTRL_WLAN_RESET_RF;
|
||||
}
|
||||
|
||||
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
|
||||
udelay(20);
|
||||
|
||||
mt76_set_wlan_state(dev, enable);
|
||||
}
|
||||
|
||||
int mt76_init_hardware(struct mt76_dev *dev)
|
||||
{
|
||||
static const u16 beacon_offsets[16] = {
|
||||
/* 1024 byte per beacon */
|
||||
0xc000,
|
||||
0xc400,
|
||||
0xc800,
|
||||
0xcc00,
|
||||
0xd000,
|
||||
0xd400,
|
||||
0xd800,
|
||||
0xdc00,
|
||||
|
||||
/* BSS idx 8-15 not used for beacons */
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
0xc000,
|
||||
};
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
dev->beacon_offsets = beacon_offsets;
|
||||
tasklet_init(&dev->pre_tbtt_tasklet, mt76_pre_tbtt_tasklet,
|
||||
(unsigned long) dev);
|
||||
|
||||
dev->chainmask = 0x202;
|
||||
|
||||
val = mt76_rr(dev, MT_WPDMA_GLO_CFG);
|
||||
val &= MT_WPDMA_GLO_CFG_DMA_BURST_SIZE |
|
||||
MT_WPDMA_GLO_CFG_BIG_ENDIAN |
|
||||
MT_WPDMA_GLO_CFG_HDR_SEG_LEN;
|
||||
val |= MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE;
|
||||
mt76_wr(dev, MT_WPDMA_GLO_CFG, val);
|
||||
|
||||
mt76_reset_wlan(dev, true);
|
||||
mt76_power_on(dev);
|
||||
|
||||
ret = mt76_eeprom_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_mac_reset(dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_dma_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_bit(MT76_STATE_INITAILIZED, &dev->state);
|
||||
ret = mt76_mac_start(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76_mcu_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_mac_stop(dev, false);
|
||||
dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76_stop_hardware(struct mt76_dev *dev)
|
||||
{
|
||||
cancel_delayed_work_sync(&dev->cal_work);
|
||||
cancel_delayed_work_sync(&dev->mac_work);
|
||||
mt76_mcu_set_radio_state(dev, false);
|
||||
mt76_mac_stop(dev, false);
|
||||
}
|
||||
|
||||
void mt76_cleanup(struct mt76_dev *dev)
|
||||
{
|
||||
mt76_stop_hardware(dev);
|
||||
mt76_dma_cleanup(dev);
|
||||
mt76_mcu_cleanup(dev);
|
||||
}
|
||||
|
||||
struct mt76_dev *mt76_alloc_device(struct device *pdev)
|
||||
{
|
||||
struct ieee80211_hw *hw;
|
||||
struct mt76_dev *dev;
|
||||
|
||||
hw = ieee80211_alloc_hw(sizeof(*dev), &mt76_ops);
|
||||
if (!hw)
|
||||
return NULL;
|
||||
|
||||
dev = hw->priv;
|
||||
dev->dev = pdev;
|
||||
dev->hw = hw;
|
||||
mutex_init(&dev->mutex);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->irq_lock);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
#define CHAN2G(_idx, _freq) { \
|
||||
.band = IEEE80211_BAND_2GHZ, \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_idx), \
|
||||
.max_power = 30, \
|
||||
}
|
||||
|
||||
#define CHAN5G(_idx, _freq) { \
|
||||
.band = IEEE80211_BAND_5GHZ, \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_idx), \
|
||||
.max_power = 30, \
|
||||
}
|
||||
|
||||
static const struct ieee80211_channel mt76_channels_2ghz[] = {
|
||||
CHAN2G(1, 2412),
|
||||
CHAN2G(2, 2417),
|
||||
CHAN2G(3, 2422),
|
||||
CHAN2G(4, 2427),
|
||||
CHAN2G(5, 2432),
|
||||
CHAN2G(6, 2437),
|
||||
CHAN2G(7, 2442),
|
||||
CHAN2G(8, 2447),
|
||||
CHAN2G(9, 2452),
|
||||
CHAN2G(10, 2457),
|
||||
CHAN2G(11, 2462),
|
||||
CHAN2G(12, 2467),
|
||||
CHAN2G(13, 2472),
|
||||
CHAN2G(14, 2484),
|
||||
};
|
||||
|
||||
static const struct ieee80211_channel mt76_channels_5ghz[] = {
|
||||
CHAN5G(36, 5180),
|
||||
CHAN5G(40, 5200),
|
||||
CHAN5G(44, 5220),
|
||||
CHAN5G(48, 5240),
|
||||
|
||||
CHAN5G(52, 5260),
|
||||
CHAN5G(56, 5280),
|
||||
CHAN5G(60, 5300),
|
||||
CHAN5G(64, 5320),
|
||||
|
||||
CHAN5G(100, 5500),
|
||||
CHAN5G(104, 5520),
|
||||
CHAN5G(108, 5540),
|
||||
CHAN5G(112, 5560),
|
||||
CHAN5G(116, 5580),
|
||||
CHAN5G(120, 5600),
|
||||
CHAN5G(124, 5620),
|
||||
CHAN5G(128, 5640),
|
||||
CHAN5G(132, 5660),
|
||||
CHAN5G(136, 5680),
|
||||
CHAN5G(140, 5700),
|
||||
|
||||
CHAN5G(149, 5745),
|
||||
CHAN5G(153, 5765),
|
||||
CHAN5G(157, 5785),
|
||||
CHAN5G(161, 5805),
|
||||
CHAN5G(165, 5825),
|
||||
};
|
||||
|
||||
#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 mt76_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;
|
||||
struct ieee80211_sta_vht_cap *vht_cap;
|
||||
void *chanlist;
|
||||
u16 mcs_map;
|
||||
int size;
|
||||
|
||||
size = n_chan * sizeof(*chan);
|
||||
chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
|
||||
if (!chanlist)
|
||||
return -ENOMEM;
|
||||
|
||||
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 |
|
||||
IEEE80211_HT_CAP_LDPC_CODING |
|
||||
IEEE80211_HT_CAP_TX_STBC |
|
||||
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
|
||||
|
||||
ht_cap->mcs.rx_mask[0] = 0xff;
|
||||
ht_cap->mcs.rx_mask[1] = 0xff;
|
||||
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
|
||||
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
|
||||
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
|
||||
|
||||
vht_cap = &sband->vht_cap;
|
||||
vht_cap->vht_supported = true;
|
||||
|
||||
mcs_map = (IEEE80211_VHT_MCS_SUPPORT_0_9 << (0 * 2)) |
|
||||
(IEEE80211_VHT_MCS_SUPPORT_0_9 << (1 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (2 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (3 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (4 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (5 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (6 * 2)) |
|
||||
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (7 * 2));
|
||||
|
||||
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
|
||||
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
|
||||
vht_cap->cap = IEEE80211_VHT_CAP_RXLDPC |
|
||||
IEEE80211_VHT_CAP_TXSTBC |
|
||||
IEEE80211_VHT_CAP_RXSTBC_1 |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_80;
|
||||
|
||||
dev->chandef.chan = &sband->channels[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_init_sband_2g(struct mt76_dev *dev)
|
||||
{
|
||||
if (!dev->cap.has_2ghz)
|
||||
return 0;
|
||||
|
||||
dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &dev->sband_2g;
|
||||
return mt76_init_sband(dev, &dev->sband_2g,
|
||||
mt76_channels_2ghz,
|
||||
ARRAY_SIZE(mt76_channels_2ghz),
|
||||
mt76_rates, ARRAY_SIZE(mt76_rates));
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_init_sband_5g(struct mt76_dev *dev)
|
||||
{
|
||||
if (!dev->cap.has_5ghz)
|
||||
return 0;
|
||||
|
||||
dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &dev->sband_5g;
|
||||
return mt76_init_sband(dev, &dev->sband_5g,
|
||||
mt76_channels_5ghz,
|
||||
ARRAY_SIZE(mt76_channels_5ghz),
|
||||
mt76_rates + 4, ARRAY_SIZE(mt76_rates) - 4);
|
||||
}
|
||||
|
||||
static const struct ieee80211_iface_limit if_limits[] = {
|
||||
{
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_ADHOC)
|
||||
}, {
|
||||
.max = 8,
|
||||
.types = BIT(NL80211_IFTYPE_STATION) |
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
BIT(NL80211_IFTYPE_MESH_POINT) |
|
||||
#endif
|
||||
BIT(NL80211_IFTYPE_AP)
|
||||
},
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_combination if_comb[] = {
|
||||
{
|
||||
.limits = if_limits,
|
||||
.n_limits = ARRAY_SIZE(if_limits),
|
||||
.max_interfaces = 8,
|
||||
.num_different_channels = 1,
|
||||
.beacon_int_infra_match = true,
|
||||
}
|
||||
};
|
||||
|
||||
int mt76_register_device(struct mt76_dev *dev)
|
||||
{
|
||||
struct ieee80211_hw *hw = dev->hw;
|
||||
struct wiphy *wiphy = hw->wiphy;
|
||||
void *status_fifo;
|
||||
int fifo_size;
|
||||
int i, ret;
|
||||
|
||||
fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76_tx_status));
|
||||
status_fifo = devm_kzalloc(dev->dev, fifo_size, GFP_KERNEL);
|
||||
if (!status_fifo)
|
||||
return -ENOMEM;
|
||||
|
||||
kfifo_init(&dev->txstatus_fifo, status_fifo, fifo_size);
|
||||
|
||||
ret = mt76_init_hardware(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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_HOST_BROADCAST_PS_BUFFERING |
|
||||
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);
|
||||
hw->txq_data_size = sizeof(struct mt76_txq);
|
||||
|
||||
dev->macaddr[0] &= ~BIT(1);
|
||||
SET_IEEE80211_PERM_ADDR(hw, dev->macaddr);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) {
|
||||
u8 *addr = dev->macaddr_list[i].addr;
|
||||
memcpy(addr, dev->macaddr, ETH_ALEN);
|
||||
|
||||
if (!i)
|
||||
continue;
|
||||
|
||||
addr[0] |= BIT(1);
|
||||
addr[0] ^= ((i - 1) << 2);
|
||||
}
|
||||
wiphy->addresses = dev->macaddr_list;
|
||||
wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list);
|
||||
|
||||
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
|
||||
|
||||
wiphy->interface_modes =
|
||||
BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
BIT(NL80211_IFTYPE_MESH_POINT) |
|
||||
#endif
|
||||
BIT(NL80211_IFTYPE_ADHOC);
|
||||
|
||||
wiphy->iface_combinations = if_comb;
|
||||
wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
|
||||
|
||||
ret = mt76_init_sband_2g(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = mt76_init_sband_5g(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
INIT_LIST_HEAD(&dev->txwi_cache);
|
||||
INIT_DELAYED_WORK(&dev->cal_work, mt76_phy_calibrate);
|
||||
INIT_DELAYED_WORK(&dev->mac_work, mt76_mac_work);
|
||||
|
||||
ret = ieee80211_register_hw(hw);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
mt76_init_debugfs(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
mt76_stop_hardware(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include "mt76.h"
|
||||
#include "mcu.h"
|
||||
#include "eeprom.h"
|
||||
#include "trace.h"
|
||||
|
||||
void mt76_mac_set_bssid(struct mt76_dev *dev, u8 idx, const u8 *addr)
|
||||
{
|
||||
idx &= 7;
|
||||
mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr));
|
||||
mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR,
|
||||
get_unaligned_le16(addr + 4));
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
|
||||
{
|
||||
u8 idx = MT76_GET(MT_RXWI_RATE_INDEX, rate);
|
||||
|
||||
switch (MT76_GET(MT_RXWI_RATE_PHY, rate)) {
|
||||
case MT_PHY_TYPE_OFDM:
|
||||
if (idx >= 8)
|
||||
idx = 0;
|
||||
|
||||
if (status->band == IEEE80211_BAND_2GHZ)
|
||||
idx += 4;
|
||||
|
||||
status->rate_idx = idx;
|
||||
return;
|
||||
case MT_PHY_TYPE_CCK:
|
||||
if (idx >= 8) {
|
||||
idx -= 8;
|
||||
status->flag |= RX_FLAG_SHORTPRE;
|
||||
}
|
||||
|
||||
if (idx >= 4)
|
||||
idx = 0;
|
||||
|
||||
status->rate_idx = idx;
|
||||
return;
|
||||
case MT_PHY_TYPE_HT_GF:
|
||||
status->flag |= RX_FLAG_HT_GF;
|
||||
/* fall through */
|
||||
case MT_PHY_TYPE_HT:
|
||||
status->flag |= RX_FLAG_HT;
|
||||
status->rate_idx = idx;
|
||||
break;
|
||||
case MT_PHY_TYPE_VHT:
|
||||
status->flag |= RX_FLAG_VHT;
|
||||
status->rate_idx = MT76_GET(MT_RATE_INDEX_VHT_IDX, idx);
|
||||
status->vht_nss = MT76_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rate & MT_RXWI_RATE_LDPC)
|
||||
status->flag |= RX_FLAG_LDPC;
|
||||
|
||||
if (rate & MT_RXWI_RATE_SGI)
|
||||
status->flag |= RX_FLAG_SHORT_GI;
|
||||
|
||||
if (rate & MT_RXWI_RATE_STBC)
|
||||
status->flag |= 1 << RX_FLAG_STBC_SHIFT;
|
||||
|
||||
switch (MT76_GET(MT_RXWI_RATE_BW, rate)) {
|
||||
case MT_PHY_BW_20:
|
||||
break;
|
||||
case MT_PHY_BW_40:
|
||||
status->flag |= RX_FLAG_40MHZ;
|
||||
break;
|
||||
case MT_PHY_BW_80:
|
||||
status->vht_flag |= RX_VHT_FLAG_80MHZ;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static __le16
|
||||
mt76_mac_tx_rate_val(struct mt76_dev *dev, const struct ieee80211_tx_rate *rate,
|
||||
u8 *nss_val)
|
||||
{
|
||||
u16 rateval;
|
||||
u8 phy, rate_idx;
|
||||
u8 nss = 1;
|
||||
u8 bw = 0;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
rate_idx = rate->idx;
|
||||
nss = 1 + (rate->idx >> 4);
|
||||
phy = MT_PHY_TYPE_VHT;
|
||||
if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||
bw = 2;
|
||||
else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
bw = 1;
|
||||
} else if (rate->flags & IEEE80211_TX_RC_MCS) {
|
||||
rate_idx = rate->idx;
|
||||
nss = 1 + (rate->idx >> 3);
|
||||
phy = MT_PHY_TYPE_HT;
|
||||
if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD)
|
||||
phy = MT_PHY_TYPE_HT_GF;
|
||||
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
bw = 1;
|
||||
} else {
|
||||
const struct ieee80211_rate *r;
|
||||
int band = dev->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_INDEX, 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 cpu_to_le16(rateval);
|
||||
}
|
||||
|
||||
void mt76_mac_wcid_set_rate(struct mt76_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);
|
||||
}
|
||||
|
||||
void mt76_mac_write_txwi(struct mt76_dev *dev, struct mt76_txwi *txwi,
|
||||
struct sk_buff *skb, struct mt76_wcid *wcid,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_tx_rate *rate = &info->control.rates[0];
|
||||
unsigned long flags;
|
||||
u16 rate_ht_mask = MT76_SET(MT_RXWI_RATE_PHY, BIT(1) | BIT(2));
|
||||
u16 txwi_flags = 0;
|
||||
u8 nss;
|
||||
|
||||
memset(txwi, 0, sizeof(*txwi));
|
||||
|
||||
if (wcid)
|
||||
txwi->wcid = wcid->idx;
|
||||
else
|
||||
txwi->wcid = 0xff;
|
||||
|
||||
txwi->pktid = 1;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (rate->idx < 0 || !rate->count) {
|
||||
txwi->rate = wcid->tx_rate;
|
||||
nss = wcid->tx_rate_nss;
|
||||
} else {
|
||||
txwi->rate = mt76_mac_tx_rate_val(dev, rate, &nss);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E4)
|
||||
txwi->txstream = 0x03;
|
||||
else if (mt76xx_rev(dev) >= MT76XX_REV_E3 &&
|
||||
!(txwi->rate & cpu_to_le16(rate_ht_mask)))
|
||||
txwi->txstream = 0x83;
|
||||
|
||||
if (info->flags & IEEE80211_TX_CTL_LDPC)
|
||||
txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC);
|
||||
if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1)
|
||||
txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC);
|
||||
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
|
||||
txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ;
|
||||
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
|
||||
txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ;
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
txwi->pktid |= MT_TXWI_PKTID_PROBE;
|
||||
if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) {
|
||||
u8 ba_size = IEEE80211_MIN_AMPDU_BUF;
|
||||
ba_size <<= sta->ht_cap.ampdu_factor;
|
||||
ba_size = min_t(int, 63, ba_size);
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
ba_size = 0;
|
||||
txwi->ack_ctl |= MT76_SET(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size);
|
||||
|
||||
txwi_flags |= MT_TXWI_FLAGS_AMPDU |
|
||||
MT76_SET(MT_TXWI_FLAGS_MPDU_DENSITY,
|
||||
sta->ht_cap.ampdu_density);
|
||||
}
|
||||
|
||||
txwi->flags |= cpu_to_le16(txwi_flags);
|
||||
txwi->len_ctl = cpu_to_le16(skb->len);
|
||||
}
|
||||
|
||||
int mt76_mac_process_rx(struct mt76_dev *dev, struct sk_buff *skb, void *rxi)
|
||||
{
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
struct mt76_rxwi *rxwi = rxi;
|
||||
u32 ctl = le32_to_cpu(rxwi->ctl);
|
||||
u16 rate = le16_to_cpu(rxwi->rate);
|
||||
int len;
|
||||
|
||||
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
|
||||
mt76_remove_hdr_pad(skb);
|
||||
|
||||
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
|
||||
status->flag |= RX_FLAG_DECRYPTED;
|
||||
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
|
||||
}
|
||||
|
||||
len = MT76_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
|
||||
skb_trim(skb, len);
|
||||
|
||||
status->chains = BIT(0) | BIT(1);
|
||||
status->chain_signal[0] = mt76_phy_get_rssi(dev, rxwi->rssi[0], 0);
|
||||
status->chain_signal[1] = mt76_phy_get_rssi(dev, rxwi->rssi[1], 1);
|
||||
status->signal = max(status->chain_signal[0], status->chain_signal[1]);
|
||||
status->freq = dev->chandef.chan->center_freq;
|
||||
status->band = dev->chandef.chan->band;
|
||||
|
||||
mt76_mac_process_rate(status, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
|
||||
enum ieee80211_band band)
|
||||
{
|
||||
u8 idx = MT76_GET(MT_RXWI_RATE_INDEX, rate);
|
||||
|
||||
txrate->idx = 0;
|
||||
txrate->flags = 0;
|
||||
txrate->count = 1;
|
||||
|
||||
switch (MT76_GET(MT_RXWI_RATE_PHY, rate)) {
|
||||
case MT_PHY_TYPE_OFDM:
|
||||
if (band == IEEE80211_BAND_2GHZ)
|
||||
idx += 4;
|
||||
|
||||
txrate->idx = idx;
|
||||
return;
|
||||
case MT_PHY_TYPE_CCK:
|
||||
if (idx >= 8)
|
||||
idx -= 8;
|
||||
|
||||
txrate->idx = idx;
|
||||
return;
|
||||
case MT_PHY_TYPE_HT_GF:
|
||||
txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD;
|
||||
/* fall through */
|
||||
case MT_PHY_TYPE_HT:
|
||||
txrate->flags |= IEEE80211_TX_RC_MCS;
|
||||
txrate->idx = idx;
|
||||
break;
|
||||
case MT_PHY_TYPE_VHT:
|
||||
txrate->flags |= IEEE80211_TX_RC_VHT_MCS;
|
||||
txrate->idx = idx;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (MT76_GET(MT_RXWI_RATE_BW, rate)) {
|
||||
case MT_PHY_BW_20:
|
||||
break;
|
||||
case MT_PHY_BW_40:
|
||||
txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||
break;
|
||||
case MT_PHY_BW_80:
|
||||
txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate & MT_RXWI_RATE_SGI)
|
||||
txrate->flags |= IEEE80211_TX_RC_SHORT_GI;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_mac_fill_tx_status(struct mt76_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,
|
||||
dev->chandef.chan->band);
|
||||
if (last_rate < IEEE80211_TX_MAX_RATES - 1)
|
||||
rate[last_rate + 1].idx = -1;
|
||||
|
||||
cur_idx = rate[last_rate].idx + st->retry;
|
||||
for (i = 0; i <= last_rate; i++) {
|
||||
rate[i].flags = rate[last_rate].flags;
|
||||
rate[i].idx = max_t(int, 0, cur_idx - i);
|
||||
rate[i].count = 1;
|
||||
}
|
||||
|
||||
if (last_rate > 0)
|
||||
rate[last_rate - 1].count = st->retry + 1 - last_rate;
|
||||
|
||||
info->status.ampdu_len = 1;
|
||||
info->status.ampdu_ack_len = st->success;
|
||||
|
||||
if (st->pktid & MT_TXWI_PKTID_PROBE)
|
||||
info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
|
||||
|
||||
if (st->aggr)
|
||||
info->flags |= IEEE80211_TX_CTL_AMPDU |
|
||||
IEEE80211_TX_STAT_AMPDU;
|
||||
|
||||
if (!st->ack_req)
|
||||
info->flags |= IEEE80211_TX_CTL_NO_ACK;
|
||||
else if (st->success)
|
||||
info->flags |= IEEE80211_TX_STAT_ACK;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_send_tx_status(struct mt76_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 mt76_mac_poll_tx_status(struct mt76_dev *dev, bool irq)
|
||||
{
|
||||
struct mt76_tx_status stat = {};
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_bit(MT76_STATE_RUNNING, &dev->state))
|
||||
return;
|
||||
|
||||
trace_mac_txstat_poll(dev);
|
||||
|
||||
spin_lock_irqsave(&dev->irq_lock, flags);
|
||||
while (!irq || !kfifo_is_full(&dev->txstatus_fifo)) {
|
||||
u32 stat1, stat2;
|
||||
|
||||
stat1 = mt76_rr(dev, MT_TX_STAT_FIFO);
|
||||
if (!(stat1 & MT_TX_STAT_FIFO_VALID))
|
||||
break;
|
||||
|
||||
stat2 = mt76_rr(dev, MT_TX_STAT_FIFO_EXT);
|
||||
|
||||
stat.valid = 1;
|
||||
stat.success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS);
|
||||
stat.aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR);
|
||||
stat.ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ);
|
||||
stat.wcid = MT76_GET(MT_TX_STAT_FIFO_WCID, stat1);
|
||||
stat.rate = MT76_GET(MT_TX_STAT_FIFO_RATE, stat1);
|
||||
stat.retry = MT76_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2);
|
||||
stat.pktid = MT76_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2);
|
||||
trace_mac_txstat_fetch(dev, &stat);
|
||||
|
||||
if (!irq) {
|
||||
mt76_send_tx_status(dev, &stat);
|
||||
continue;
|
||||
}
|
||||
|
||||
kfifo_put(&dev->txstatus_fifo, stat);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->irq_lock, flags);
|
||||
}
|
||||
|
||||
void mt76_mac_queue_txdone(struct mt76_dev *dev, struct sk_buff *skb,
|
||||
struct mt76_txwi *txwi)
|
||||
{
|
||||
struct mt76_tx_info *txi = mt76_skb_tx_info(skb);
|
||||
|
||||
mt76_mac_poll_tx_status(dev, false);
|
||||
|
||||
txi->tries = 0;
|
||||
txi->jiffies = jiffies;
|
||||
txi->wcid = txwi->wcid;
|
||||
txi->pktid = txwi->pktid;
|
||||
trace_mac_txdone_add(dev, txwi->wcid, txwi->pktid);
|
||||
mt76_tx_complete(dev, skb);
|
||||
}
|
||||
|
||||
void mt76_mac_process_tx_status_fifo(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_tx_status stat;
|
||||
|
||||
while (kfifo_get(&dev->txstatus_fifo, &stat))
|
||||
mt76_send_tx_status(dev, &stat);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void mt76_mac_wcid_setup(struct mt76_dev *dev, u8 idx, u8 vif_idx, u8 *mac)
|
||||
{
|
||||
struct mt76_wcid_addr addr = {};
|
||||
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(addr.macaddr, mac, ETH_ALEN);
|
||||
|
||||
mt76_wr_copy(dev, MT_WCID_ADDR(idx), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
int mt76_mac_wcid_set_key(struct mt76_dev *dev, u8 idx,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
enum mt76_cipher_type cipher;
|
||||
u8 key_data[32];
|
||||
u8 iv_data[8];
|
||||
|
||||
cipher = mt76_mac_get_key_info(key, key_data);
|
||||
if (cipher == MT_CIPHER_NONE && key)
|
||||
return -EINVAL;
|
||||
|
||||
mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PKEY_MODE, cipher);
|
||||
mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data));
|
||||
|
||||
memset(iv_data, 0, sizeof(iv_data));
|
||||
if (key) {
|
||||
mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE,
|
||||
!!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE));
|
||||
iv_data[3] = key->keyidx << 6;
|
||||
if (cipher >= WLAN_CIPHER_SUITE_TKIP)
|
||||
iv_data[3] |= 0x20;
|
||||
}
|
||||
|
||||
mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_mac_shared_key_setup(struct mt76_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;
|
||||
|
||||
val = mt76_rr(dev, MT_SKEY_MODE(vif_idx));
|
||||
val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx));
|
||||
val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx);
|
||||
mt76_wr(dev, MT_SKEY_MODE(vif_idx), val);
|
||||
|
||||
mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), key_data, sizeof(key_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_mac_skb_tx_overhead(struct mt76_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_key_conf *key = info->control.hw_key;
|
||||
int overhead;
|
||||
|
||||
if (!key)
|
||||
return 0;
|
||||
|
||||
overhead = key->icv_len;
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV))
|
||||
overhead += key->iv_len;
|
||||
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
|
||||
key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
overhead += 8;
|
||||
|
||||
return overhead;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_write_beacon(struct mt76_dev *dev, int offset, struct sk_buff *skb)
|
||||
{
|
||||
int beacon_len = dev->beacon_offsets[1] - dev->beacon_offsets[0];
|
||||
struct mt76_txwi txwi;
|
||||
|
||||
if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76_txwi)))
|
||||
return -ENOSPC;
|
||||
|
||||
mt76_mac_write_txwi(dev, &txwi, skb, NULL, NULL);
|
||||
txwi.flags |= cpu_to_le16(MT_TXWI_FLAGS_TS);
|
||||
|
||||
mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
|
||||
offset += sizeof(txwi);
|
||||
|
||||
mt76_wr_copy(dev, offset, skb->data, skb->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
__mt76_mac_set_beacon(struct mt76_dev *dev, u8 bcn_idx, struct sk_buff *skb)
|
||||
{
|
||||
int beacon_len = dev->beacon_offsets[1] - dev->beacon_offsets[0];
|
||||
int beacon_addr = dev->beacon_offsets[bcn_idx];
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
/* Prevent corrupt transmissions during update */
|
||||
mt76_set(dev, MT_BCN_BYPASS_MASK, BIT(bcn_idx));
|
||||
|
||||
if (skb) {
|
||||
ret = mt76_write_beacon(dev, beacon_addr, skb);
|
||||
if (!ret)
|
||||
dev->beacon_data_mask |= BIT(bcn_idx) & dev->beacon_mask;
|
||||
} else {
|
||||
dev->beacon_data_mask &= ~BIT(bcn_idx);
|
||||
for (i = 0; i < beacon_len; i += 4)
|
||||
mt76_wr(dev, beacon_addr + i, 0);
|
||||
}
|
||||
|
||||
mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xff00 | ~dev->beacon_data_mask);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mt76_mac_set_beacon(struct mt76_dev *dev, u8 vif_idx, struct sk_buff *skb)
|
||||
{
|
||||
bool force_update = false;
|
||||
int bcn_idx = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->beacons); i++) {
|
||||
if (vif_idx == i) {
|
||||
force_update = !!dev->beacons[i] ^ !!skb;
|
||||
|
||||
if (dev->beacons[i])
|
||||
dev_kfree_skb(dev->beacons[i]);
|
||||
|
||||
dev->beacons[i] = skb;
|
||||
__mt76_mac_set_beacon(dev, bcn_idx, skb);
|
||||
} else if (force_update && dev->beacons[i]) {
|
||||
__mt76_mac_set_beacon(dev, bcn_idx, dev->beacons[i]);
|
||||
}
|
||||
|
||||
bcn_idx += !!dev->beacons[i];
|
||||
}
|
||||
|
||||
for (i = bcn_idx; i < ARRAY_SIZE(dev->beacons); i++) {
|
||||
if (!(dev->beacon_data_mask & BIT(i)))
|
||||
break;
|
||||
|
||||
__mt76_mac_set_beacon(dev, i, NULL);
|
||||
}
|
||||
|
||||
mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N, bcn_idx - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76_mac_set_beacon_enable(struct mt76_dev *dev, u8 vif_idx, bool val)
|
||||
{
|
||||
u8 old_mask = dev->beacon_mask;
|
||||
bool en;
|
||||
u32 reg;
|
||||
|
||||
if (val) {
|
||||
dev->beacon_mask |= BIT(vif_idx);
|
||||
} else {
|
||||
dev->beacon_mask &= ~BIT(vif_idx);
|
||||
mt76_mac_set_beacon(dev, vif_idx, NULL);
|
||||
}
|
||||
|
||||
if (!!old_mask == !!dev->beacon_mask)
|
||||
return;
|
||||
|
||||
en = dev->beacon_mask;
|
||||
|
||||
mt76_rmw_field(dev, MT_INT_TIMER_EN, MT_INT_TIMER_EN_PRE_TBTT_EN, en);
|
||||
reg = MT_BEACON_TIME_CFG_BEACON_TX |
|
||||
MT_BEACON_TIME_CFG_TBTT_EN |
|
||||
MT_BEACON_TIME_CFG_TIMER_EN;
|
||||
mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en);
|
||||
|
||||
if (en)
|
||||
mt76_irq_enable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
|
||||
else
|
||||
mt76_irq_disable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT);
|
||||
}
|
||||
|
||||
void mt76_mac_work(struct work_struct *work)
|
||||
{
|
||||
struct mt76_dev *dev = container_of(work, struct mt76_dev,
|
||||
mac_work.work);
|
||||
int i, idx;
|
||||
|
||||
for (i = 0, idx = 0; i < 16; i++) {
|
||||
u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
|
||||
dev->aggr_stats[idx++] += val & 0xffff;
|
||||
dev->aggr_stats[idx++] += val >> 16;
|
||||
}
|
||||
|
||||
ieee80211_queue_delayed_work(dev->hw, &dev->mac_work,
|
||||
MT_CALIBRATE_INTERVAL);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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_dev;
|
||||
struct mt76_sta;
|
||||
struct mt76_vif;
|
||||
struct mt76_wcid;
|
||||
|
||||
struct mt76_txwi;
|
||||
struct mt76_queue;
|
||||
struct mt76_txwi_cache;
|
||||
|
||||
struct mt76_tx_status {
|
||||
u8 valid:1;
|
||||
u8 success:1;
|
||||
u8 aggr:1;
|
||||
u8 ack_req:1;
|
||||
u8 wcid;
|
||||
u8 pktid;
|
||||
u8 retry;
|
||||
u16 rate;
|
||||
} __packed __aligned(2);
|
||||
|
||||
struct mt76_tx_info {
|
||||
unsigned long jiffies;
|
||||
u8 tries;
|
||||
|
||||
u8 wcid;
|
||||
u8 pktid;
|
||||
u8 retry;
|
||||
};
|
||||
|
||||
struct mt76_rxwi {
|
||||
__le32 rxinfo;
|
||||
|
||||
__le32 ctl;
|
||||
|
||||
__le16 tid_sn;
|
||||
__le16 rate;
|
||||
|
||||
u8 rssi[4];
|
||||
|
||||
__le32 bbp_rxinfo[4];
|
||||
};
|
||||
|
||||
#define MT_RXINFO_BA BIT(0)
|
||||
#define MT_RXINFO_DATA BIT(1)
|
||||
#define MT_RXINFO_NULL BIT(2)
|
||||
#define MT_RXINFO_FRAG BIT(3)
|
||||
#define MT_RXINFO_UNICAST BIT(4)
|
||||
#define MT_RXINFO_MULTICAST BIT(5)
|
||||
#define MT_RXINFO_BROADCAST BIT(6)
|
||||
#define MT_RXINFO_MYBSS BIT(7)
|
||||
#define MT_RXINFO_CRCERR BIT(8)
|
||||
#define MT_RXINFO_ICVERR BIT(9)
|
||||
#define MT_RXINFO_MICERR BIT(10)
|
||||
#define MT_RXINFO_AMSDU BIT(11)
|
||||
#define MT_RXINFO_HTC BIT(12)
|
||||
#define MT_RXINFO_RSSI BIT(13)
|
||||
#define MT_RXINFO_L2PAD BIT(14)
|
||||
#define MT_RXINFO_AMPDU BIT(15)
|
||||
#define MT_RXINFO_DECRYPT BIT(16)
|
||||
#define MT_RXINFO_BSSIDX3 BIT(17)
|
||||
#define MT_RXINFO_WAPI_KEY BIT(18)
|
||||
#define MT_RXINFO_PN_LEN GENMASK(21, 19)
|
||||
#define MT_RXINFO_SW_FTYPE0 BIT(22)
|
||||
#define MT_RXINFO_SW_FTYPE1 BIT(23)
|
||||
#define MT_RXINFO_PROBE_RESP BIT(24)
|
||||
#define MT_RXINFO_BEACON BIT(25)
|
||||
#define MT_RXINFO_DISASSOC BIT(26)
|
||||
#define MT_RXINFO_DEAUTH BIT(27)
|
||||
#define MT_RXINFO_ACTION BIT(28)
|
||||
#define MT_RXINFO_TCP_SUM_ERR BIT(30)
|
||||
#define MT_RXINFO_IP_SUM_ERR BIT(31)
|
||||
|
||||
#define MT_RXWI_CTL_WCID GENMASK(7, 0)
|
||||
#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8)
|
||||
#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10)
|
||||
#define MT_RXWI_CTL_UDF GENMASK(15, 13)
|
||||
#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16)
|
||||
#define MT_RXWI_CTL_EOF BIT(31)
|
||||
|
||||
#define MT_RXWI_TID GENMASK(3, 0)
|
||||
#define MT_RXWI_SN GENMASK(15, 4)
|
||||
|
||||
#define MT_RXWI_RATE_INDEX GENMASK(5, 0)
|
||||
#define MT_RXWI_RATE_LDPC BIT(6)
|
||||
#define MT_RXWI_RATE_BW GENMASK(8, 7)
|
||||
#define MT_RXWI_RATE_SGI BIT(9)
|
||||
#define MT_RXWI_RATE_STBC BIT(10)
|
||||
#define MT_RXWI_RATE_LDPC_EXSYM BIT(11)
|
||||
#define MT_RXWI_RATE_PHY GENMASK(15, 13)
|
||||
|
||||
#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0)
|
||||
#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4)
|
||||
|
||||
enum mt76_phy_type {
|
||||
MT_PHY_TYPE_CCK,
|
||||
MT_PHY_TYPE_OFDM,
|
||||
MT_PHY_TYPE_HT,
|
||||
MT_PHY_TYPE_HT_GF,
|
||||
MT_PHY_TYPE_VHT,
|
||||
};
|
||||
|
||||
enum mt76_phy_bandwith {
|
||||
MT_PHY_BW_20,
|
||||
MT_PHY_BW_40,
|
||||
MT_PHY_BW_80,
|
||||
};
|
||||
|
||||
#define MT_TXWI_FLAGS_FRAG BIT(0)
|
||||
#define MT_TXWI_FLAGS_MMPS BIT(1)
|
||||
#define MT_TXWI_FLAGS_CFACK BIT(2)
|
||||
#define MT_TXWI_FLAGS_TS BIT(3)
|
||||
#define MT_TXWI_FLAGS_AMPDU BIT(4)
|
||||
#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5)
|
||||
#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8)
|
||||
#define MT_TXWI_FLAGS_NDPS BIT(10)
|
||||
#define MT_TXWI_FLAGS_RTSBWSIG BIT(11)
|
||||
#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12)
|
||||
#define MT_TXWI_FLAGS_SOUND BIT(14)
|
||||
#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15)
|
||||
|
||||
#define MT_TXWI_ACK_CTL_REQ BIT(0)
|
||||
#define MT_TXWI_ACK_CTL_NSEQ BIT(1)
|
||||
#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2)
|
||||
|
||||
#define MT_TXWI_PKTID_PROBE BIT(7)
|
||||
|
||||
struct mt76_txwi {
|
||||
__le16 flags;
|
||||
__le16 rate;
|
||||
u8 ack_ctl;
|
||||
u8 wcid;
|
||||
__le16 len_ctl;
|
||||
__le32 iv;
|
||||
__le32 eiv;
|
||||
u8 aid;
|
||||
u8 txstream;
|
||||
u8 ctl2;
|
||||
u8 pktid;
|
||||
} __packed __aligned(4);
|
||||
|
||||
|
||||
static inline struct mt76_tx_info *
|
||||
mt76_skb_tx_info(struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
return (void *) info->status.status_driver_data;
|
||||
}
|
||||
|
||||
int mt76_mac_reset(struct mt76_dev *dev, bool hard);
|
||||
int mt76_mac_start(struct mt76_dev *dev);
|
||||
void mt76_mac_stop(struct mt76_dev *dev, bool force);
|
||||
void mt76_mac_resume(struct mt76_dev *dev);
|
||||
void mt76_mac_set_bssid(struct mt76_dev *dev, u8 idx, const u8 *addr);
|
||||
|
||||
int mt76_mac_process_rx(struct mt76_dev *dev, struct sk_buff *skb, void *rxwi);
|
||||
void mt76_mac_write_txwi(struct mt76_dev *dev, struct mt76_txwi *txwi,
|
||||
struct sk_buff *skb, struct mt76_wcid *wcid,
|
||||
struct ieee80211_sta *sta);
|
||||
void mt76_mac_wcid_setup(struct mt76_dev *dev, u8 idx, u8 vif_idx, u8 *mac);
|
||||
int mt76_mac_wcid_set_key(struct mt76_dev *dev, u8 idx,
|
||||
struct ieee80211_key_conf *key);
|
||||
void mt76_mac_wcid_set_rate(struct mt76_dev *dev, struct mt76_wcid *wcid,
|
||||
const struct ieee80211_tx_rate *rate);
|
||||
|
||||
int mt76_mac_shared_key_setup(struct mt76_dev *dev, u8 vif_idx, u8 key_idx,
|
||||
struct ieee80211_key_conf *key);
|
||||
|
||||
int mt76_insert_hdr_pad(struct sk_buff *skb);
|
||||
void mt76_remove_hdr_pad(struct sk_buff *skb);
|
||||
int mt76_mac_skb_tx_overhead(struct mt76_dev *dev, struct sk_buff *skb);
|
||||
|
||||
int mt76_mac_set_beacon(struct mt76_dev *dev, u8 vif_idx, struct sk_buff *skb);
|
||||
void mt76_mac_set_beacon_enable(struct mt76_dev *dev, u8 vif_idx, bool val);
|
||||
|
||||
void mt76_mac_queue_txdone(struct mt76_dev *dev, struct sk_buff *skb,
|
||||
struct mt76_txwi *txwi);
|
||||
|
||||
void mt76_mac_poll_tx_status(struct mt76_dev *dev, bool irq);
|
||||
void mt76_mac_process_tx_status_fifo(struct mt76_dev *dev);
|
||||
|
||||
void mt76_mac_work(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
|
||||
static int
|
||||
mt76_start(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
ret = mt76_mac_start(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = mt76_phy_start(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ieee80211_queue_delayed_work(dev->hw, &dev->mac_work,
|
||||
MT_CALIBRATE_INTERVAL);
|
||||
napi_enable(&dev->napi);
|
||||
|
||||
set_bit(MT76_STATE_RUNNING, &dev->state);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_stop(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
napi_disable(&dev->napi);
|
||||
clear_bit(MT76_STATE_RUNNING, &dev->state);
|
||||
mt76_stop_hardware(dev);
|
||||
mutex_unlock(&dev->mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
unsigned int idx = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (vif->addr[0] & BIT(1))
|
||||
idx = 1 + (((dev->macaddr[0] ^ vif->addr[0]) >> 2) & 7);
|
||||
|
||||
/*
|
||||
* Client mode typically only has one configurable BSSID register,
|
||||
* which is used for bssidx=0. This is linked to the MAC address.
|
||||
* Since mac80211 allows changing interface types, and we cannot
|
||||
* force the use of the primary MAC address for a station mode
|
||||
* interface, we need some other way of configuring a per-interface
|
||||
* remote BSSID.
|
||||
* The hardware provides an AP-Client feature, where bssidx 0-7 are
|
||||
* used for AP mode and bssidx 8-15 for client mode.
|
||||
* We shift the station interface bss index by 8 to force the
|
||||
* hardware to recognize the BSSID.
|
||||
* The resulting bssidx mismatch for unicast frames is ignored by hw.
|
||||
*/
|
||||
if (vif->type == NL80211_IFTYPE_STATION)
|
||||
idx += 8;
|
||||
|
||||
mvif->idx = idx;
|
||||
mvif->group_wcid.idx = 254 - idx;
|
||||
mvif->group_wcid.hw_key_idx = -1;
|
||||
mt76_txq_init(dev, vif->txq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
|
||||
mt76_txq_remove(dev, vif->txq);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_config(struct ieee80211_hw *hw, u32 changed)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
if (changed & IEEE80211_CONF_CHANGE_POWER) {
|
||||
dev->txpower_conf = hw->conf.power_level;
|
||||
|
||||
if (test_bit(MT76_STATE_RUNNING, &dev->state))
|
||||
mt76_phy_set_txpower(dev);
|
||||
}
|
||||
|
||||
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
|
||||
ieee80211_stop_queues(hw);
|
||||
ret = mt76_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 mt76_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(PROMISC_IN_BSS, MT_RX_FILTR_CFG_PROMISC);
|
||||
MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR);
|
||||
MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR);
|
||||
MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK |
|
||||
MT_RX_FILTR_CFG_CTS |
|
||||
MT_RX_FILTR_CFG_CFEND |
|
||||
MT_RX_FILTR_CFG_CFACK |
|
||||
MT_RX_FILTR_CFG_BA |
|
||||
MT_RX_FILTR_CFG_CTRL_RSV);
|
||||
MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL);
|
||||
|
||||
*total_flags = flags;
|
||||
mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
|
||||
|
||||
mutex_unlock(&dev->mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *info, u32 changed)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
if (changed & BSS_CHANGED_BSSID)
|
||||
mt76_mac_set_bssid(dev, mvif->idx, info->bssid);
|
||||
|
||||
if (changed & BSS_CHANGED_BEACON_INT)
|
||||
mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
|
||||
MT_BEACON_TIME_CFG_INTVAL,
|
||||
info->beacon_int << 4);
|
||||
|
||||
if (changed & BSS_CHANGED_BEACON_ENABLED) {
|
||||
tasklet_disable(&dev->pre_tbtt_tasklet);
|
||||
mt76_mac_set_beacon_enable(dev, mvif->idx, info->enable_beacon);
|
||||
tasklet_enable(&dev->pre_tbtt_tasklet);
|
||||
}
|
||||
|
||||
if (changed & BSS_CHANGED_ERP_SLOT) {
|
||||
int slottime = info->use_short_slot ? 9 : 20;
|
||||
|
||||
mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
|
||||
MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_wcid_alloc(struct mt76_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;
|
||||
|
||||
if (idx > 247)
|
||||
return -1;
|
||||
|
||||
idx--;
|
||||
dev->wcid_mask[i] |= BIT(idx);
|
||||
return i * BITS_PER_LONG + idx;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct mt76_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;
|
||||
int i;
|
||||
|
||||
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;
|
||||
mt76_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
|
||||
mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
|
||||
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
|
||||
mt76_txq_init(dev, sta->txq[i]);
|
||||
|
||||
rcu_assign_pointer(dev->wcid[idx], &msta->wcid);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
|
||||
int idx = msta->wcid.idx;
|
||||
int i;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
rcu_assign_pointer(dev->wcid[idx], NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
|
||||
mt76_txq_remove(dev, sta->txq[i]);
|
||||
mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx));
|
||||
dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG);
|
||||
mt76_mac_wcid_setup(dev, idx, 0, NULL);
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_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 mt76_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
|
||||
mt76_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
|
||||
const struct ieee80211_tx_queue_params *params)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
u32 val;
|
||||
|
||||
val = MT76_SET(MT_EDCA_CFG_TXOP, params->txop) |
|
||||
MT76_SET(MT_EDCA_CFG_AIFSN, params->aifs) |
|
||||
MT76_SET(MT_EDCA_CFG_CWMIN, params->cw_min) |
|
||||
MT76_SET(MT_EDCA_CFG_CWMAX, params->cw_max);
|
||||
mt76_wr(dev, MT_EDCA_CFG_AC(queue), val);
|
||||
|
||||
val = mt76_rr(dev, MT_WMM_TXOP(queue));
|
||||
val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue));
|
||||
val |= params->txop << MT_WMM_TXOP_SHIFT(queue);
|
||||
mt76_wr(dev, MT_WMM_TXOP(queue), val);
|
||||
|
||||
val = mt76_rr(dev, MT_WMM_AIFSN);
|
||||
val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue));
|
||||
val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue);
|
||||
mt76_wr(dev, MT_WMM_AIFSN, val);
|
||||
|
||||
val = mt76_rr(dev, MT_WMM_CWMIN);
|
||||
val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue));
|
||||
val |= params->cw_min << MT_WMM_CWMIN_SHIFT(queue);
|
||||
mt76_wr(dev, MT_WMM_CWMIN, val);
|
||||
|
||||
val = mt76_rr(dev, MT_WMM_CWMAX);
|
||||
val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue));
|
||||
val |= params->cw_max << MT_WMM_CWMAX_SHIFT(queue);
|
||||
mt76_wr(dev, MT_WMM_CWMAX, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_sw_scan(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
|
||||
tasklet_disable(&dev->pre_tbtt_tasklet);
|
||||
set_bit(MT76_SCANNING, &dev->state);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
|
||||
clear_bit(MT76_SCANNING, &dev->state);
|
||||
tasklet_enable(&dev->pre_tbtt_tasklet);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
u32 queues, bool drop)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
|
||||
*dbm = dev->txpower_cur;
|
||||
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 mt76_dev *dev = hw->priv;
|
||||
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
|
||||
|
||||
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 mt76_dev *dev = hw->priv;
|
||||
struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
|
||||
struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates);
|
||||
struct ieee80211_tx_rate rate = {};
|
||||
|
||||
if (!rates)
|
||||
return;
|
||||
|
||||
rate.idx = rates->rate[0].idx;
|
||||
rate.flags = rates->rate[0].flags;
|
||||
mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate);
|
||||
}
|
||||
|
||||
const struct ieee80211_ops mt76_ops = {
|
||||
.tx = mt76_tx,
|
||||
.start = mt76_start,
|
||||
.stop = mt76_stop,
|
||||
.add_interface = mt76_add_interface,
|
||||
.remove_interface = mt76_remove_interface,
|
||||
.config = mt76_config,
|
||||
.configure_filter = mt76_configure_filter,
|
||||
.bss_info_changed = mt76_bss_info_changed,
|
||||
.sta_add = mt76_sta_add,
|
||||
.sta_remove = mt76_sta_remove,
|
||||
.sta_notify = mt76_sta_notify,
|
||||
.set_key = mt76_set_key,
|
||||
.conf_tx = mt76_conf_tx,
|
||||
.sw_scan_start = mt76_sw_scan,
|
||||
.sw_scan_complete = mt76_sw_scan_complete,
|
||||
.flush = mt76_flush,
|
||||
.ampdu_action = mt76_ampdu_action,
|
||||
.get_txpower = mt76_get_txpower,
|
||||
.wake_tx_queue = mt76_wake_tx_queue,
|
||||
.sta_rate_tbl_update = mt76_sta_rate_tbl_update,
|
||||
};
|
||||
|
||||
void mt76_rx(struct mt76_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
if (!test_bit(MT76_STATE_RUNNING, &dev->state)) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
ieee80211_rx(dev->hw, skb);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
#include "mcu.h"
|
||||
#include "dma.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
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_patch_header {
|
||||
char build_time[16];
|
||||
char platform[4];
|
||||
char hw_version[4];
|
||||
char patch_version[4];
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
static struct sk_buff *
|
||||
mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(len, GFP_KERNEL);
|
||||
memcpy(skb_put(skb, len), data, len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
mt76_mcu_get_response(struct mt76_dev *dev, unsigned long expires)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (!time_is_after_jiffies(expires))
|
||||
return NULL;
|
||||
|
||||
timeout = expires - jiffies;
|
||||
wait_event_timeout(dev->mcu.wait, !skb_queue_empty(&dev->mcu.res_q),
|
||||
timeout);
|
||||
return skb_dequeue(&dev->mcu.res_q);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_mcu_msg_send(struct mt76_dev *dev, struct sk_buff *skb, enum mcu_cmd cmd)
|
||||
{
|
||||
unsigned long expires = jiffies + HZ;
|
||||
u32 info;
|
||||
int ret;
|
||||
u8 seq;
|
||||
|
||||
if (!skb)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mcu.mutex);
|
||||
|
||||
seq = ++dev->mcu.msg_seq & 0xf;
|
||||
if (!seq)
|
||||
seq = ++dev->mcu.msg_seq & 0xf;
|
||||
|
||||
info = MT_MCU_MSG_TYPE_CMD |
|
||||
MT76_SET(MT_MCU_MSG_CMD_TYPE, cmd) |
|
||||
MT76_SET(MT_MCU_MSG_CMD_SEQ, seq) |
|
||||
MT76_SET(MT_MCU_MSG_PORT, CPU_TX_PORT) |
|
||||
MT76_SET(MT_MCU_MSG_LEN, skb->len);
|
||||
|
||||
ret = __mt76_tx_queue_skb(dev, MT_TXQ_MCU, skb, info);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
while (1) {
|
||||
u32 *rxfce;
|
||||
bool check_seq = false;
|
||||
|
||||
skb = mt76_mcu_get_response(dev, expires);
|
||||
if (!skb) {
|
||||
printk("MCU message %d (seq %d) timed out\n", cmd,
|
||||
MT76_GET(MT_MCU_MSG_CMD_SEQ, info));
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
rxfce = (u32 *) skb->cb;
|
||||
|
||||
if (seq == MT76_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce))
|
||||
check_seq = true;
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
if (check_seq)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mcu.mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_string(const char *data, int len)
|
||||
{
|
||||
static char buf[17];
|
||||
|
||||
BUG_ON(len >= sizeof(buf));
|
||||
memcpy(buf, data, len);
|
||||
buf[len] = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
write_data(struct mt76_dev *dev, u32 offset, __le32 *data, int len)
|
||||
{
|
||||
__iowrite32_copy(dev->regs + offset, data, len / 4);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76pci_load_rom_patch(struct mt76_dev *dev)
|
||||
{
|
||||
const struct firmware *fw = NULL;
|
||||
struct mt76_patch_header *hdr;
|
||||
int len, ret = 0;
|
||||
__le32 *cur;
|
||||
u32 patch_mask, patch_reg;
|
||||
|
||||
if (!mt76_poll(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) {
|
||||
printk("Could not get hardware semaphore for ROM PATCH\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E3) {
|
||||
patch_mask = BIT(0);
|
||||
patch_reg = MT_MCU_CLOCK_CTL;
|
||||
} else {
|
||||
patch_mask = BIT(1);
|
||||
patch_reg = MT_MCU_COM_REG0;
|
||||
}
|
||||
|
||||
if (mt76_rr(dev, patch_reg) & patch_mask) {
|
||||
printk("ROM patch already applied\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = request_firmware(&fw, MT7662_ROM_PATCH, dev->dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!fw || !fw->data || fw->size <= sizeof(*hdr)) {
|
||||
ret = -EIO;
|
||||
printk("Failed to load firmware\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
hdr = (struct mt76_patch_header *) fw->data;
|
||||
printk("ROM patch build: %s\n", get_string(hdr->build_time, 15));
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET);
|
||||
|
||||
cur = (__le32 *) (fw->data + sizeof(*hdr));
|
||||
len = fw->size - sizeof(*hdr);
|
||||
write_data(dev, MT_MCU_ROM_PATCH_ADDR, cur, len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
|
||||
|
||||
/* Trigger ROM */
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 4);
|
||||
|
||||
if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 2000)) {
|
||||
printk("Failed to load ROM patch\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
/* release semaphore */
|
||||
mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1);
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76pci_load_firmware(struct mt76_dev *dev)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const struct mt76_fw_header *hdr;
|
||||
int i, len, ret;
|
||||
__le32 *cur;
|
||||
u32 offset, val;
|
||||
|
||||
ret = request_firmware(&fw, MT7662_FIRMWARE, dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!fw || !fw->data || fw->size < sizeof(*hdr))
|
||||
goto error;
|
||||
|
||||
hdr = (const struct mt76_fw_header *) fw->data;
|
||||
|
||||
len = sizeof(*hdr);
|
||||
len += le32_to_cpu(hdr->ilm_len);
|
||||
len += le32_to_cpu(hdr->dlm_len);
|
||||
|
||||
if (fw->size != len)
|
||||
goto error;
|
||||
|
||||
val = le16_to_cpu(hdr->fw_ver);
|
||||
printk("Firmware Version: %d.%d.%02d\n",
|
||||
(val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf);
|
||||
|
||||
val = le16_to_cpu(hdr->build_ver);
|
||||
printk("Build: %x\n", val);
|
||||
printk("Build Time: %16s\n", get_string(hdr->build_time, 16));
|
||||
|
||||
cur = (__le32 *) (fw->data + sizeof(*hdr));
|
||||
len = le32_to_cpu(hdr->ilm_len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ILM_OFFSET);
|
||||
write_data(dev, MT_MCU_ILM_ADDR, cur, len);
|
||||
|
||||
cur += len / sizeof(*cur);
|
||||
len = le32_to_cpu(hdr->dlm_len);
|
||||
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
|
||||
offset = MT_MCU_DLM_ADDR_E3;
|
||||
else
|
||||
offset = MT_MCU_DLM_ADDR;
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET);
|
||||
write_data(dev, offset, cur, len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
|
||||
|
||||
/* trigger firmware */
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 2);
|
||||
for (i = 200; i > 0; i--) {
|
||||
val = mt76_rr(dev, MT_MCU_COM_REG0);
|
||||
|
||||
if (val & 1)
|
||||
break;
|
||||
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if (!i) {
|
||||
printk("Firmware failed to start\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
printk("Firmware running!\n");
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
printk("Invalid firmware\n");
|
||||
release_firmware(fw);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int mt76_mcu_function_select(struct mt76_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 = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_FUN_SET_OP);
|
||||
}
|
||||
|
||||
int mt76_mcu_load_cr(struct mt76_dev *dev, u8 type, u8 temp_level, u8 channel)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
u8 cr_mode;
|
||||
u8 temp;
|
||||
u8 ch;
|
||||
u8 _pad0;
|
||||
|
||||
__le32 cfg;
|
||||
} __packed __aligned(4) msg = {
|
||||
.cr_mode = type,
|
||||
.temp = temp_level,
|
||||
.ch = channel,
|
||||
};
|
||||
u32 val;
|
||||
|
||||
val = BIT(31);
|
||||
val |= (mt76_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff;
|
||||
val |= (mt76_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00;
|
||||
msg.cfg = cpu_to_le32(val);
|
||||
|
||||
/* first set the channel without the extension channel info */
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_LOAD_CR);
|
||||
}
|
||||
|
||||
int mt76_mcu_set_channel(struct mt76_dev *dev, u8 channel, u8 bw, u8 bw_index,
|
||||
bool scan)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
u8 idx;
|
||||
u8 scan;
|
||||
u8 bw;
|
||||
u8 _pad0;
|
||||
|
||||
__le16 chainmask;
|
||||
u8 ext_chan;
|
||||
u8 _pad1;
|
||||
|
||||
} __packed __aligned(4) msg = {
|
||||
.idx = channel,
|
||||
.scan = scan,
|
||||
.bw = bw,
|
||||
.chainmask = cpu_to_le16(dev->chainmask),
|
||||
};
|
||||
|
||||
/* first set the channel without the extension channel info */
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
mt76_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP);
|
||||
|
||||
msleep(5);
|
||||
|
||||
msg.ext_chan = 0xe0 + bw_index;
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP);
|
||||
}
|
||||
|
||||
int mt76_mcu_set_radio_state(struct mt76_dev *dev, bool on)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 mode;
|
||||
__le32 level;
|
||||
} __packed __aligned(4) msg = {
|
||||
.mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF),
|
||||
.level = cpu_to_le32(0),
|
||||
};
|
||||
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_POWER_SAVING_OP);
|
||||
}
|
||||
|
||||
int mt76_mcu_calibrate(struct mt76_dev *dev, enum mcu_calibration type,
|
||||
u32 param)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 id;
|
||||
__le32 value;
|
||||
} __packed __aligned(4) msg = {
|
||||
.id = cpu_to_le32(type),
|
||||
.value = cpu_to_le32(param),
|
||||
};
|
||||
int ret;
|
||||
|
||||
mt76_clear(dev, MT_MCU_COM_REG0, BIT(31));
|
||||
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
ret = mt76_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0,
|
||||
BIT(31), BIT(31), 100)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_mcu_tssi_comp(struct mt76_dev *dev, struct mt76_tssi_comp *tssi_data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 id;
|
||||
struct mt76_tssi_comp data;
|
||||
} __packed __aligned(4) msg = {
|
||||
.id = cpu_to_le32(MCU_CAL_TSSI_COMP),
|
||||
.data = *tssi_data,
|
||||
};
|
||||
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP);
|
||||
}
|
||||
|
||||
int mt76_mcu_init_gain(struct mt76_dev *dev, u8 channel, u32 gain, bool force)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 channel;
|
||||
__le32 gain_val;
|
||||
} __packed __aligned(4) msg = {
|
||||
.channel = cpu_to_le32(channel),
|
||||
.gain_val = cpu_to_le32(gain),
|
||||
};
|
||||
|
||||
if (force)
|
||||
msg.channel |= cpu_to_le32(BIT(31));
|
||||
|
||||
skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg));
|
||||
return mt76_mcu_msg_send(dev, skb, CMD_INIT_GAIN_OP);
|
||||
}
|
||||
|
||||
int mt76_mcu_init(struct mt76_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_init(&dev->mcu.mutex);
|
||||
|
||||
ret = mt76pci_load_rom_patch(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76pci_load_firmware(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_mcu_function_select(dev, Q_SELECT, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76_mcu_cleanup(struct mt76_dev *dev)
|
||||
{
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 1);
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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_MCU_H
|
||||
#define __MT76_MCU_H
|
||||
|
||||
/* Register definitions */
|
||||
#define MT_MCU_CPU_CTL 0x0704
|
||||
#define MT_MCU_CLOCK_CTL 0x0708
|
||||
#define MT_MCU_RESET_CTL 0x070C
|
||||
#define MT_MCU_INT_LEVEL 0x0718
|
||||
#define MT_MCU_COM_REG0 0x0730
|
||||
#define MT_MCU_COM_REG1 0x0734
|
||||
#define MT_MCU_COM_REG2 0x0738
|
||||
#define MT_MCU_COM_REG3 0x073C
|
||||
#define MT_MCU_PCIE_REMAP_BASE1 0x0740
|
||||
#define MT_MCU_PCIE_REMAP_BASE2 0x0744
|
||||
#define MT_MCU_PCIE_REMAP_BASE3 0x0748
|
||||
#define MT_MCU_PCIE_REMAP_BASE4 0x074C
|
||||
#define MT_MCU_LED_CTRL 0x0770
|
||||
#define MT_MCU_LED_TX_BLINK_0 0x0774
|
||||
#define MT_MCU_LED_TX_BLINK_1 0x0778
|
||||
#define MT_MCU_LED0_S0 0x077C
|
||||
#define MT_MCU_LED0_S1 0x0780
|
||||
#define MT_MCU_SEMAPHORE_00 0x07B0
|
||||
#define MT_MCU_SEMAPHORE_01 0x07B4
|
||||
#define MT_MCU_SEMAPHORE_02 0x07B8
|
||||
#define MT_MCU_SEMAPHORE_03 0x07BC
|
||||
|
||||
#define MT_MCU_ROM_PATCH_OFFSET 0x80000
|
||||
#define MT_MCU_ROM_PATCH_ADDR 0x90000
|
||||
|
||||
#define MT_MCU_ILM_OFFSET 0x80000
|
||||
#define MT_MCU_ILM_ADDR 0x80000
|
||||
|
||||
#define MT_MCU_DLM_OFFSET 0x100000
|
||||
#define MT_MCU_DLM_ADDR 0x90000
|
||||
#define MT_MCU_DLM_ADDR_E3 0x90800
|
||||
|
||||
/* MCU request message header */
|
||||
#define MT_MCU_MSG_LEN GENMASK(15, 0)
|
||||
#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16)
|
||||
#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20)
|
||||
#define MT_MCU_MSG_PORT GENMASK(29, 27)
|
||||
#define MT_MCU_MSG_TYPE GENMASK(31, 30)
|
||||
#define MT_MCU_MSG_TYPE_CMD BIT(30)
|
||||
|
||||
enum mcu_cmd {
|
||||
CMD_FUN_SET_OP = 1,
|
||||
CMD_LOAD_CR = 2,
|
||||
CMD_INIT_GAIN_OP = 3,
|
||||
CMD_DYNC_VGA_OP = 6,
|
||||
CMD_TDLS_CH_SW = 7,
|
||||
CMD_BURST_WRITE = 8,
|
||||
CMD_READ_MODIFY_WRITE = 9,
|
||||
CMD_RANDOM_READ = 10,
|
||||
CMD_BURST_READ = 11,
|
||||
CMD_RANDOM_WRITE = 12,
|
||||
CMD_LED_MODE_OP = 16,
|
||||
CMD_POWER_SAVING_OP = 20,
|
||||
CMD_WOW_CONFIG = 21,
|
||||
CMD_WOW_QUERY = 22,
|
||||
CMD_WOW_FEATURE = 24,
|
||||
CMD_CARRIER_DETECT_OP = 28,
|
||||
CMD_RADOR_DETECT_OP = 29,
|
||||
CMD_SWITCH_CHANNEL_OP = 30,
|
||||
CMD_CALIBRATION_OP = 31,
|
||||
CMD_BEACON_OP = 32,
|
||||
CMD_ANTENNA_OP = 33,
|
||||
};
|
||||
|
||||
enum mcu_function {
|
||||
Q_SELECT = 1,
|
||||
BW_SETTING = 2,
|
||||
USB2_SW_DISCONNECT = 2,
|
||||
USB3_SW_DISCONNECT = 3,
|
||||
LOG_FW_DEBUG_MSG = 4,
|
||||
GET_FW_VERSION = 5,
|
||||
};
|
||||
|
||||
enum mcu_msg_port {
|
||||
WLAN_PORT,
|
||||
CPU_RX_PORT,
|
||||
CPU_TX_PORT,
|
||||
HOST_PORT,
|
||||
VIRTUAL_CPU_RX_PORT,
|
||||
VIRTUAL_CPU_TX_PORT,
|
||||
DISCARD,
|
||||
};
|
||||
|
||||
enum mcu_power_mode {
|
||||
RADIO_OFF = 0x30,
|
||||
RADIO_ON = 0x31,
|
||||
RADIO_OFF_AUTO_WAKEUP = 0x32,
|
||||
RADIO_OFF_ADVANCE = 0x33,
|
||||
RADIO_ON_ADVANCE = 0x34,
|
||||
};
|
||||
|
||||
enum mcu_calibration {
|
||||
MCU_CAL_R = 1,
|
||||
MCU_CAL_TEMP_SENSOR,
|
||||
MCU_CAL_RXDCOC,
|
||||
MCU_CAL_RC,
|
||||
MCU_CAL_SX_LOGEN,
|
||||
MCU_CAL_LC,
|
||||
MCU_CAL_TX_LOFT,
|
||||
MCU_CAL_TXIQ,
|
||||
MCU_CAL_TSSI,
|
||||
MCU_CAL_TSSI_COMP,
|
||||
MCU_CAL_DPD,
|
||||
MCU_CAL_RXIQC_FI,
|
||||
MCU_CAL_RXIQC_FD,
|
||||
};
|
||||
|
||||
enum mt76_mcu_cr_mode {
|
||||
MT_RF_CR,
|
||||
MT_BBP_CR,
|
||||
MT_RF_BBP_CR,
|
||||
MT_HL_TEMP_CR_UPDATE,
|
||||
};
|
||||
|
||||
struct mt76_tssi_comp {
|
||||
u8 pa_mode;
|
||||
u8 cal_mode;
|
||||
u16 pad;
|
||||
|
||||
u8 slope0;
|
||||
u8 slope1;
|
||||
u8 offset0;
|
||||
u8 offset1;
|
||||
} __packed __aligned(4);
|
||||
|
||||
int mt76_mcu_calibrate(struct mt76_dev *dev, enum mcu_calibration type,
|
||||
u32 param);
|
||||
int mt76_mcu_tssi_comp(struct mt76_dev *dev, struct mt76_tssi_comp *data);
|
||||
int mt76_mcu_init_gain(struct mt76_dev *dev, u8 channel, u32 gain, bool force);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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_H
|
||||
#define __MT76_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <net/mac80211.h>
|
||||
|
||||
#define MT7662_FIRMWARE "mt7662.bin"
|
||||
#define MT7662_ROM_PATCH "mt7662_rom_patch.bin"
|
||||
#define MT7662_EEPROM_SIZE 512
|
||||
|
||||
#define MT_RX_BUF_SIZE 2048
|
||||
#define MT_RX_HEADROOM 32
|
||||
#define MT_RX_RING_SIZE 128
|
||||
|
||||
#define MT_TX_RING_SIZE 128
|
||||
|
||||
#define MT_MCU_RING_SIZE 32
|
||||
|
||||
#define MT_MAX_CHAINS 2
|
||||
|
||||
#define MT_CALIBRATE_INTERVAL HZ
|
||||
|
||||
#include "regs.h"
|
||||
#include "util.h"
|
||||
#include "mac.h"
|
||||
|
||||
struct mt76_queue_entry {
|
||||
struct sk_buff *skb;
|
||||
union {
|
||||
void *buf;
|
||||
struct mt76_txwi_cache *txwi;
|
||||
};
|
||||
bool schedule;
|
||||
};
|
||||
|
||||
enum {
|
||||
MT76_STATE_INITAILIZED,
|
||||
MT76_STATE_RUNNING,
|
||||
MT76_SCANNING,
|
||||
};
|
||||
|
||||
enum mt76_txq_id {
|
||||
MT_TXQ_VO = IEEE80211_AC_VO,
|
||||
MT_TXQ_VI = IEEE80211_AC_VI,
|
||||
MT_TXQ_BE = IEEE80211_AC_BE,
|
||||
MT_TXQ_BK = IEEE80211_AC_BK,
|
||||
MT_TXQ_PSD,
|
||||
MT_TXQ_MCU,
|
||||
__MT_TXQ_MAX
|
||||
};
|
||||
|
||||
struct mt76_queue {
|
||||
struct mt76_queue_regs *regs;
|
||||
|
||||
spinlock_t lock;
|
||||
struct mt76_queue_entry *entry;
|
||||
struct mt76_desc *desc;
|
||||
|
||||
struct list_head swq;
|
||||
int swq_queued;
|
||||
|
||||
u16 head;
|
||||
u16 tail;
|
||||
int ndesc;
|
||||
int queued;
|
||||
int buf_size;
|
||||
|
||||
dma_addr_t desc_dma;
|
||||
};
|
||||
|
||||
struct mt76_mcu {
|
||||
struct mutex mutex;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
struct sk_buff_head res_q;
|
||||
|
||||
struct mt76_queue q_rx;
|
||||
u32 msg_seq;
|
||||
};
|
||||
|
||||
struct mt76_rx_freq_cal {
|
||||
s8 high_gain[MT_MAX_CHAINS];
|
||||
s8 rssi_offset[MT_MAX_CHAINS];
|
||||
s8 lna_gain;
|
||||
u32 mcu_gain;
|
||||
};
|
||||
|
||||
struct mt76_calibration {
|
||||
struct mt76_rx_freq_cal rx;
|
||||
|
||||
u8 agc_gain_init[MT_MAX_CHAINS];
|
||||
int avg_rssi[MT_MAX_CHAINS];
|
||||
int avg_rssi_all;
|
||||
|
||||
s8 low_gain;
|
||||
|
||||
u8 temp;
|
||||
|
||||
bool init_cal_done;
|
||||
bool tssi_cal_done;
|
||||
bool tssi_comp_done;
|
||||
bool dpd_cal_done;
|
||||
bool channel_cal_done;
|
||||
};
|
||||
|
||||
struct mt76_wcid {
|
||||
u8 idx;
|
||||
u8 hw_key_idx;
|
||||
|
||||
__le16 tx_rate;
|
||||
bool tx_rate_set;
|
||||
u8 tx_rate_nss;
|
||||
};
|
||||
|
||||
struct mt76_hw_cap {
|
||||
bool has_2ghz;
|
||||
bool has_5ghz;
|
||||
};
|
||||
|
||||
struct mt76_dev {
|
||||
struct ieee80211_hw *hw;
|
||||
struct device *dev;
|
||||
u8 macaddr[ETH_ALEN];
|
||||
struct mac_address macaddr_list[8];
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
const u16 *beacon_offsets;
|
||||
unsigned long wcid_mask[256 / BITS_PER_LONG];
|
||||
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_supported_band sband_2g;
|
||||
struct ieee80211_supported_band sband_5g;
|
||||
int txpower_conf;
|
||||
int txpower_cur;
|
||||
|
||||
u8 txdone_seq;
|
||||
DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76_tx_status);
|
||||
|
||||
struct list_head txwi_cache;
|
||||
struct mt76_mcu mcu;
|
||||
struct mt76_queue q_rx;
|
||||
struct mt76_queue q_tx[__MT_TXQ_MAX];
|
||||
|
||||
struct net_device napi_dev;
|
||||
struct napi_struct napi;
|
||||
|
||||
struct tasklet_struct tx_tasklet;
|
||||
struct tasklet_struct rx_tasklet;
|
||||
struct tasklet_struct pre_tbtt_tasklet;
|
||||
struct delayed_work cal_work;
|
||||
struct delayed_work mac_work;
|
||||
|
||||
u32 aggr_stats[32];
|
||||
|
||||
struct mt76_wcid __rcu *wcid[254 - 8];
|
||||
|
||||
spinlock_t lock;
|
||||
spinlock_t irq_lock;
|
||||
u32 irqmask;
|
||||
unsigned long state;
|
||||
|
||||
struct sk_buff *beacons[8];
|
||||
u8 beacon_mask;
|
||||
u8 beacon_data_mask;
|
||||
|
||||
u32 rev;
|
||||
u32 rxfilter;
|
||||
|
||||
u16 chainmask;
|
||||
|
||||
struct mt76_calibration cal;
|
||||
struct debugfs_blob_wrapper eeprom;
|
||||
struct mt76_hw_cap cap;
|
||||
|
||||
u32 debugfs_reg;
|
||||
};
|
||||
|
||||
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_txq {
|
||||
struct list_head list;
|
||||
struct mt76_queue *hwq;
|
||||
|
||||
struct sk_buff_head retry_q;
|
||||
};
|
||||
|
||||
struct mt76_reg_pair {
|
||||
u32 reg;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
u32 mt76_rr(struct mt76_dev *dev, u32 offset);
|
||||
void mt76_wr(struct mt76_dev *dev, u32 offset, u32 val);
|
||||
u32 mt76_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val);
|
||||
void mt76_wr_copy(struct mt76_dev *dev, u32 offset, const void *data, int len);
|
||||
|
||||
bool mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
|
||||
int timeout);
|
||||
bool mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
|
||||
int timeout);
|
||||
void mt76_write_reg_pairs(struct mt76_dev *dev,
|
||||
const struct mt76_reg_pair *data, int len);
|
||||
|
||||
#define mt76_get_field(_dev, _reg, _field) \
|
||||
MT76_GET(_field, mt76_rr(dev, _reg))
|
||||
|
||||
#define mt76_rmw_field(_dev, _reg, _field, _val) \
|
||||
mt76_rmw(_dev, _reg, _field, MT76_SET(_field, _val))
|
||||
|
||||
static inline u32 mt76_set(struct mt76_dev *dev, u32 offset, u32 val)
|
||||
{
|
||||
return mt76_rmw(dev, offset, 0, val);
|
||||
}
|
||||
|
||||
static inline u32 mt76_clear(struct mt76_dev *dev, u32 offset, u32 val)
|
||||
{
|
||||
return mt76_rmw(dev, offset, val, 0);
|
||||
}
|
||||
|
||||
static inline bool is_mt7612(struct mt76_dev *dev)
|
||||
{
|
||||
return (dev->rev >> 16) == 0x7612;
|
||||
}
|
||||
|
||||
static inline u16 mt76xx_rev(struct mt76_dev *dev)
|
||||
{
|
||||
return dev->rev & 0xffff;
|
||||
}
|
||||
|
||||
void mt76_set_irq_mask(struct mt76_dev *dev, u32 clear, u32 set);
|
||||
|
||||
static inline void mt76_irq_enable(struct mt76_dev *dev, u32 mask)
|
||||
{
|
||||
mt76_set_irq_mask(dev, 0, mask);
|
||||
}
|
||||
|
||||
static inline void mt76_irq_disable(struct mt76_dev *dev, u32 mask)
|
||||
{
|
||||
mt76_set_irq_mask(dev, mask, 0);
|
||||
}
|
||||
|
||||
static inline struct ieee80211_txq *
|
||||
mtxq_to_txq(struct mt76_txq *mtxq)
|
||||
{
|
||||
void *ptr = mtxq;
|
||||
return container_of(ptr, struct ieee80211_txq, drv_priv);
|
||||
}
|
||||
|
||||
extern const struct ieee80211_ops mt76_ops;
|
||||
|
||||
struct mt76_dev *mt76_alloc_device(struct device *pdev);
|
||||
int mt76_register_device(struct mt76_dev *dev);
|
||||
void mt76_init_debugfs(struct mt76_dev *dev);
|
||||
|
||||
irqreturn_t mt76_irq_handler(int irq, void *dev_instance);
|
||||
void mt76_phy_power_on(struct mt76_dev *dev);
|
||||
int mt76_init_hardware(struct mt76_dev *dev);
|
||||
void mt76_stop_hardware(struct mt76_dev *dev);
|
||||
int mt76_eeprom_init(struct mt76_dev *dev);
|
||||
int mt76_apply_calibration_data(struct mt76_dev *dev, int channel);
|
||||
|
||||
int mt76_phy_start(struct mt76_dev *dev);
|
||||
int mt76_set_channel(struct mt76_dev *dev, struct cfg80211_chan_def *chandef);
|
||||
int mt76_phy_set_channel(struct mt76_dev *dev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
int mt76_phy_get_rssi(struct mt76_dev *dev, s8 rssi, int chain);
|
||||
void mt76_phy_calibrate(struct work_struct *work);
|
||||
void mt76_phy_set_txpower(struct mt76_dev *dev);
|
||||
|
||||
int mt76_mcu_init(struct mt76_dev *dev);
|
||||
int mt76_mcu_set_channel(struct mt76_dev *dev, u8 channel, u8 bw, u8 bw_index,
|
||||
bool scan);
|
||||
int mt76_mcu_set_radio_state(struct mt76_dev *dev, bool on);
|
||||
int mt76_mcu_load_cr(struct mt76_dev *dev, u8 type, u8 temp_level, u8 channel);
|
||||
int mt76_mcu_cleanup(struct mt76_dev *dev);
|
||||
|
||||
int mt76_dma_init(struct mt76_dev *dev);
|
||||
void mt76_dma_cleanup(struct mt76_dev *dev);
|
||||
|
||||
void mt76_cleanup(struct mt76_dev *dev);
|
||||
void mt76_rx(struct mt76_dev *dev, struct sk_buff *skb);
|
||||
|
||||
int __mt76_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
|
||||
struct sk_buff *skb, u32 tx_info);
|
||||
|
||||
int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
|
||||
struct sk_buff *skb, struct mt76_wcid *wcid,
|
||||
struct ieee80211_sta *sta);
|
||||
|
||||
void mt76_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
|
||||
struct sk_buff *skb);
|
||||
void mt76_tx_complete(struct mt76_dev *dev, struct sk_buff *skb);
|
||||
|
||||
void mt76_kick_queue(struct mt76_dev *dev, struct mt76_queue *q);
|
||||
|
||||
void mt76_pre_tbtt_tasklet(unsigned long data);
|
||||
|
||||
void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq);
|
||||
void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||
void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq);
|
||||
void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "mt76.h"
|
||||
#include "trace.h"
|
||||
|
||||
static const struct pci_device_id mt76pci_device_table[] = {
|
||||
{ PCI_DEVICE(0x14c3, 0x7662) },
|
||||
};
|
||||
|
||||
u32 mt76_rr(struct mt76_dev *dev, u32 offset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = ioread32(dev->regs + offset);
|
||||
trace_reg_read(dev, offset, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void mt76_wr(struct mt76_dev *dev, u32 offset, u32 val)
|
||||
{
|
||||
trace_reg_write(dev, offset, val);
|
||||
iowrite32(val, dev->regs + offset);
|
||||
}
|
||||
|
||||
u32 mt76_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val)
|
||||
{
|
||||
val |= mt76_rr(dev, offset) & ~mask;
|
||||
mt76_wr(dev, offset, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void mt76_wr_copy(struct mt76_dev *dev, u32 offset, const void *data, int len)
|
||||
{
|
||||
__iowrite32_copy(dev->regs + offset, data, len >> 2);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct mt76_dev *dev;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev = mt76_alloc_device(&pdev->dev);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->regs = pcim_iomap_table(pdev)[0];
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
dev->rev = mt76_rr(dev, MT_ASIC_VERSION);
|
||||
printk("ASIC revision: %08x\n", dev->rev);
|
||||
|
||||
ret = devm_request_irq(dev->dev, pdev->irq, mt76_irq_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = mt76_register_device(dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* Fix up ASPM configuration */
|
||||
|
||||
/* RG_SSUSB_G1_CDR_BIR_LTR = 0x9 */
|
||||
mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9);
|
||||
|
||||
/* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */
|
||||
mt76_rmw_field(dev, 0x15a0c, 0xf << 28, 0xf);
|
||||
|
||||
/* RG_SSUSB_CDR_BR_PE1D = 0x3 */
|
||||
mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3);
|
||||
|
||||
printk("pci device driver attached\n");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
ieee80211_free_hw(dev->hw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct mt76_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
ieee80211_unregister_hw(dev->hw);
|
||||
mt76_cleanup(dev);
|
||||
ieee80211_free_hw(dev->hw);
|
||||
printk("pci device driver detached\n");
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, mt76pci_device_table);
|
||||
MODULE_FIRMWARE(MT7662_FIRMWARE);
|
||||
MODULE_FIRMWARE(MT7662_ROM_PATCH);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static struct pci_driver mt76pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = mt76pci_device_table,
|
||||
.probe = mt76pci_probe,
|
||||
.remove = mt76pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(mt76pci_driver);
|
||||
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include "mt76.h"
|
||||
#include "mcu.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
static bool
|
||||
mt76_phy_rf_op(struct mt76_dev *dev, bool idx, u16 offset, bool write)
|
||||
{
|
||||
u32 val = MT76_SET(MT_RF_CTRL_ADDR, offset);
|
||||
|
||||
if (idx)
|
||||
val |= MT_RF_CTRL_IDX;
|
||||
|
||||
if (write)
|
||||
val |= MT_RF_CTRL_WRITE;
|
||||
|
||||
mt76_wr(dev, MT_RF_CTRL, val);
|
||||
|
||||
return mt76_poll(dev, MT_RF_CTRL, MT_RF_CTRL_BUSY, 0, 2000);
|
||||
}
|
||||
|
||||
static int __maybe_unused
|
||||
mt76_phy_rf_read(struct mt76_dev *dev, bool idx, u16 offset, u32 *val)
|
||||
{
|
||||
if (!mt76_phy_rf_op(dev, idx, offset, false))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
*val = mt76_rr(dev, MT_RF_DATA_READ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused
|
||||
mt76_phy_rf_write(struct mt76_dev *dev, bool idx, u16 offset, u32 val)
|
||||
{
|
||||
mt76_wr(dev, MT_RF_DATA_WRITE, val);
|
||||
|
||||
if (!mt76_phy_rf_op(dev, idx, offset, true))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_adjust_lna_gain(struct mt76_dev *dev, int reg, s8 offset)
|
||||
{
|
||||
s8 gain;
|
||||
|
||||
gain = MT76_GET(MT_BBP_AGC_LNA_GAIN, mt76_rr(dev, MT_BBP(AGC, reg)));
|
||||
gain -= offset / 2;
|
||||
mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_LNA_GAIN, gain);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_adjust_agc_gain(struct mt76_dev *dev, int reg, s8 offset)
|
||||
{
|
||||
s8 gain;
|
||||
|
||||
gain = MT76_GET(MT_BBP_AGC_GAIN, mt76_rr(dev, MT_BBP(AGC, reg)));
|
||||
gain += offset;
|
||||
mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_GAIN, gain);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_apply_gain_adj(struct mt76_dev *dev)
|
||||
{
|
||||
s8 *gain_adj = dev->cal.rx.high_gain;
|
||||
|
||||
mt76_adjust_lna_gain(dev, 4, gain_adj[0]);
|
||||
mt76_adjust_lna_gain(dev, 5, gain_adj[1]);
|
||||
|
||||
mt76_adjust_agc_gain(dev, 8, gain_adj[0]);
|
||||
mt76_adjust_agc_gain(dev, 9, gain_adj[1]);
|
||||
}
|
||||
|
||||
static u32
|
||||
mt76_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
val |= (v1 & (BIT(6) - 1)) << 0;
|
||||
val |= (v2 & (BIT(6) - 1)) << 8;
|
||||
val |= (v3 & (BIT(6) - 1)) << 16;
|
||||
val |= (v4 & (BIT(6) - 1)) << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void mt76_apply_rate_power_table(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_rate_power t;
|
||||
|
||||
mt76_get_rate_power(dev, &t);
|
||||
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_0,
|
||||
mt76_tx_power_mask(t.cck[0], t.cck[1], t.ofdm[0], t.ofdm[1]));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_1,
|
||||
mt76_tx_power_mask(t.ofdm[2], t.ofdm[3], t.ht[0], t.ht[1]));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_2,
|
||||
mt76_tx_power_mask(t.ht[2], t.ht[3], t.ht[4], t.ht[5]));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_3,
|
||||
mt76_tx_power_mask(t.ht[6], t.ht[7], t.ht[0], t.ht[1]));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_4,
|
||||
mt76_tx_power_mask(t.ht[2], t.ht[3], 0, 0));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_7,
|
||||
mt76_tx_power_mask(t.ofdm[2], t.vht[4], t.ht[3], t.vht[4]));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_8,
|
||||
mt76_tx_power_mask(t.ht[7], t.vht[4], t.vht[4], 0));
|
||||
mt76_wr(dev, MT_TX_PWR_CFG_9,
|
||||
mt76_tx_power_mask(t.ht[3], t.vht[4], t.vht[4], 0));
|
||||
}
|
||||
|
||||
int mt76_phy_get_rssi(struct mt76_dev *dev, s8 rssi, int chain)
|
||||
{
|
||||
struct mt76_rx_freq_cal *cal = &dev->cal.rx;
|
||||
|
||||
rssi += cal->rssi_offset[chain];
|
||||
rssi -= cal->lna_gain;
|
||||
|
||||
return rssi;
|
||||
}
|
||||
|
||||
static u8
|
||||
mt76_txpower_check(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return 0;
|
||||
if (value > 0x2f)
|
||||
return 0x2f;
|
||||
return value;
|
||||
}
|
||||
|
||||
void mt76_phy_set_txpower(struct mt76_dev *dev)
|
||||
{
|
||||
enum nl80211_chan_width width = dev->chandef.width;
|
||||
struct mt76_tx_power_info txp;
|
||||
int txp_0, txp_1, delta = 0;
|
||||
|
||||
mt76_get_power_info(dev, &txp);
|
||||
|
||||
if (width == NL80211_CHAN_WIDTH_40)
|
||||
delta = txp.delta_bw40;
|
||||
else if (width == NL80211_CHAN_WIDTH_80)
|
||||
delta = txp.delta_bw80;
|
||||
|
||||
if (txp.target_power > dev->txpower_conf) {
|
||||
dev->txpower_cur = dev->txpower_conf;
|
||||
delta -= txp.target_power - dev->txpower_conf;
|
||||
} else {
|
||||
dev->txpower_cur = txp.target_power;
|
||||
}
|
||||
|
||||
txp_0 = mt76_txpower_check(txp.chain[0].target_power +
|
||||
txp.chain[0].delta + delta);
|
||||
|
||||
txp_1 = mt76_txpower_check(txp.chain[1].target_power +
|
||||
txp.chain[1].delta + delta);
|
||||
|
||||
mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0);
|
||||
mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1);
|
||||
}
|
||||
|
||||
static bool
|
||||
mt76_channel_silent(struct mt76_dev *dev)
|
||||
{
|
||||
struct ieee80211_channel *chan = dev->chandef.chan;
|
||||
|
||||
return ((chan->flags & IEEE80211_CHAN_RADAR) &&
|
||||
chan->dfs_state != NL80211_DFS_AVAILABLE);
|
||||
}
|
||||
|
||||
static bool
|
||||
mt76_phy_tssi_init_cal(struct mt76_dev *dev)
|
||||
{
|
||||
struct ieee80211_channel *chan = dev->chandef.chan;
|
||||
u16 flag = 0;
|
||||
|
||||
if (!mt76_tssi_enabled(dev))
|
||||
return false;
|
||||
|
||||
if (mt76_channel_silent(dev))
|
||||
return false;
|
||||
|
||||
if (chan->band == IEEE80211_BAND_2GHZ)
|
||||
flag |= BIT(0);
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, chan->band))
|
||||
flag |= BIT(16);
|
||||
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_TSSI, flag);
|
||||
dev->cal.tssi_cal_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_channel_calibrate(struct mt76_dev *dev, bool mac_stopped)
|
||||
{
|
||||
struct ieee80211_channel *chan = dev->chandef.chan;
|
||||
bool is_5ghz = chan->band == IEEE80211_BAND_5GHZ;
|
||||
|
||||
if (dev->cal.channel_cal_done)
|
||||
return;
|
||||
|
||||
if (mt76_channel_silent(dev))
|
||||
return;
|
||||
|
||||
if (!dev->cal.tssi_cal_done)
|
||||
mt76_phy_tssi_init_cal(dev);
|
||||
|
||||
if (!mac_stopped)
|
||||
mt76_mac_stop(dev, false);
|
||||
|
||||
if (is_5ghz)
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_LC, 0);
|
||||
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz);
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz);
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz);
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0);
|
||||
|
||||
if (!mac_stopped)
|
||||
mt76_mac_resume(dev);
|
||||
|
||||
mt76_apply_gain_adj(dev);
|
||||
|
||||
dev->cal.channel_cal_done = true;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_set_txpower_regs(struct mt76_dev *dev, enum ieee80211_band band)
|
||||
{
|
||||
u32 pa_mode[2];
|
||||
u32 pa_mode_adj;
|
||||
u32 tx_cfg = 0;
|
||||
|
||||
if (band == IEEE80211_BAND_2GHZ) {
|
||||
pa_mode[0] = 0x010055ff;
|
||||
pa_mode[1] = 0x00550055;
|
||||
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_2, 0x35160a00);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_3, 0x35160a06);
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, band)) {
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0x0000ec00);
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0x0000ec00);
|
||||
} else {
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0xf4000200);
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0xfa000200);
|
||||
}
|
||||
} else {
|
||||
pa_mode[0] = 0x0000ffff;
|
||||
pa_mode[1] = 0x00ff00ff;
|
||||
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_2, 0x1b0f0400);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_3, 0x1b0f0476);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_4, 0);
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, band)) {
|
||||
tx_cfg = 0x00830083;
|
||||
pa_mode_adj = 0x04000000;
|
||||
} else {
|
||||
pa_mode_adj = 0;
|
||||
}
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, pa_mode_adj);
|
||||
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, pa_mode_adj);
|
||||
}
|
||||
|
||||
mt76_wr(dev, MT_BB_PA_MODE_CFG0, pa_mode[0]);
|
||||
mt76_wr(dev, MT_BB_PA_MODE_CFG1, pa_mode[1]);
|
||||
mt76_wr(dev, MT_RF_PA_MODE_CFG0, pa_mode[0]);
|
||||
mt76_wr(dev, MT_RF_PA_MODE_CFG1, pa_mode[1]);
|
||||
mt76_wr(dev, MT_PROT_AUTO_TX_CFG, tx_cfg);
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, band)) {
|
||||
u32 val = 0x3c3c023c;
|
||||
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val);
|
||||
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00001818);
|
||||
} else {
|
||||
if (band == IEEE80211_BAND_2GHZ) {
|
||||
u32 val = 0x0f3c3c3c;
|
||||
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val);
|
||||
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00000606);
|
||||
} else {
|
||||
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x383c023c);
|
||||
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, 0x24282e28);
|
||||
mt76_wr(dev, MT_TX_ALC_CFG_4, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_configure_tx_delay(struct mt76_dev *dev, enum ieee80211_band band, u8 bw)
|
||||
{
|
||||
u32 cfg0, cfg1;
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, band)) {
|
||||
cfg0 = bw ? 0x000b0c01 : 0x00101101;
|
||||
cfg1 = 0x00010200;
|
||||
} else {
|
||||
cfg0 = bw ? 0x000b0b01 : 0x00101001;
|
||||
cfg1 = 0x00020000;
|
||||
}
|
||||
mt76_wr(dev, MT_TX_SW_CFG0, cfg0);
|
||||
mt76_wr(dev, MT_TX_SW_CFG1, cfg1);
|
||||
|
||||
mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_CCK_SIFS,
|
||||
13 + (bw ? 1 : 0));
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_set_bw(struct mt76_dev *dev, int width, u8 ctrl)
|
||||
{
|
||||
int core_val, agc_val;
|
||||
|
||||
switch (width) {
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
core_val = 3;
|
||||
agc_val = 7;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
core_val = 2;
|
||||
agc_val = 3;
|
||||
break;
|
||||
default:
|
||||
core_val = 0;
|
||||
agc_val = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
mt76_rmw_field(dev, MT_BBP(CORE, 1), MT_BBP_CORE_R1_BW, core_val);
|
||||
mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_BW, core_val);
|
||||
mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_CTRL_CHAN, ctrl);
|
||||
mt76_rmw_field(dev, MT_BBP(TXBE, 0), MT_BBP_TXBE_R0_CTRL_CHAN, ctrl);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_set_band(struct mt76_dev *dev, int band, bool primary_upper)
|
||||
{
|
||||
switch (band) {
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G);
|
||||
mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G);
|
||||
break;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G);
|
||||
mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G);
|
||||
break;
|
||||
}
|
||||
|
||||
mt76_rmw_field(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_UPPER_40M,
|
||||
primary_upper);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_set_rx_chains(struct mt76_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = mt76_rr(dev, MT_BBP(AGC, 0));
|
||||
val &= ~(BIT(3) | BIT(4));
|
||||
|
||||
if (dev->chainmask & BIT(1))
|
||||
val |= BIT(3);
|
||||
|
||||
mt76_wr(dev, MT_BBP(AGC, 0), val);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_set_tx_dac(struct mt76_dev *dev)
|
||||
{
|
||||
if (dev->chainmask & BIT(1))
|
||||
mt76_set(dev, MT_BBP(TXBE, 5), 3);
|
||||
else
|
||||
mt76_clear(dev, MT_BBP(TXBE, 5), 3);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_get_agc_gain(struct mt76_dev *dev, u8 *dest)
|
||||
{
|
||||
dest[0] = mt76_get_field(dev, MT_BBP(AGC, 8), MT_BBP_AGC_GAIN);
|
||||
dest[1] = mt76_get_field(dev, MT_BBP(AGC, 9), MT_BBP_AGC_GAIN);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_get_rssi_gain_thresh(struct mt76_dev *dev)
|
||||
{
|
||||
switch (dev->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return -62;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return -65;
|
||||
default:
|
||||
return -68;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_update_channel_gain(struct mt76_dev *dev)
|
||||
{
|
||||
u32 val = mt76_rr(dev, MT_BBP(AGC, 20));
|
||||
int rssi0 = (s8) MT76_GET(MT_BBP_AGC20_RSSI0, val);
|
||||
int rssi1 = (s8) MT76_GET(MT_BBP_AGC20_RSSI1, val);
|
||||
bool low_gain;
|
||||
u8 gain[2], gain_delta;
|
||||
|
||||
dev->cal.avg_rssi[0] = (dev->cal.avg_rssi[0] * 15) / 16 + (rssi0 << 8);
|
||||
dev->cal.avg_rssi[1] = (dev->cal.avg_rssi[0] * 15) / 16 + (rssi1 << 8);
|
||||
dev->cal.avg_rssi_all = (dev->cal.avg_rssi[0] + dev->cal.avg_rssi[1]) / 512;
|
||||
|
||||
low_gain = dev->cal.avg_rssi_all > mt76_get_rssi_gain_thresh(dev);
|
||||
if (dev->cal.low_gain == low_gain)
|
||||
return;
|
||||
|
||||
dev->cal.low_gain = low_gain;
|
||||
|
||||
if (dev->chandef.width >= NL80211_CHAN_WIDTH_40)
|
||||
val = 0x1e42 << 16;
|
||||
else
|
||||
val = 0x1836 << 16;
|
||||
|
||||
mt76_get_agc_gain(dev, gain);
|
||||
val |= 0xf8;
|
||||
|
||||
if (dev->chandef.width == NL80211_CHAN_WIDTH_80)
|
||||
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560411);
|
||||
else
|
||||
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
|
||||
|
||||
if (low_gain) {
|
||||
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
|
||||
mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
|
||||
if (mt76_has_ext_lna(dev))
|
||||
gain_delta = 10;
|
||||
else
|
||||
gain_delta = 14;
|
||||
} else {
|
||||
mt76_wr(dev, MT_BBP(AGC, 35), 0x11111116);
|
||||
mt76_wr(dev, MT_BBP(AGC, 37), 0x1010161C);
|
||||
gain_delta = 0;
|
||||
}
|
||||
|
||||
mt76_wr(dev, MT_BBP(AGC, 8),
|
||||
val | MT76_SET(MT_BBP_AGC_GAIN, gain[0] - gain_delta));
|
||||
mt76_wr(dev, MT_BBP(AGC, 9),
|
||||
val | MT76_SET(MT_BBP_AGC_GAIN, gain[1] - gain_delta));
|
||||
}
|
||||
|
||||
int mt76_phy_set_channel(struct mt76_dev *dev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_channel *chan = chandef->chan;
|
||||
bool scan = test_bit(MT76_SCANNING, &dev->state);
|
||||
enum ieee80211_band band = chan->band;
|
||||
u8 channel;
|
||||
|
||||
u32 ext_cca_chan[4] = {
|
||||
[0] = MT76_SET(MT_EXT_CCA_CFG_CCA0, 0) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA1, 1) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA2, 2) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA3, 3) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)),
|
||||
[1] = MT76_SET(MT_EXT_CCA_CFG_CCA0, 1) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA1, 0) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA2, 2) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA3, 3) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)),
|
||||
[2] = MT76_SET(MT_EXT_CCA_CFG_CCA0, 2) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA1, 3) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA2, 1) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA3, 0) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)),
|
||||
[3] = MT76_SET(MT_EXT_CCA_CFG_CCA0, 3) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA1, 2) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA2, 1) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA3, 0) |
|
||||
MT76_SET(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)),
|
||||
};
|
||||
int ch_group_index;
|
||||
u8 bw, bw_index;
|
||||
int freq, freq1;
|
||||
int ret;
|
||||
u8 sifs = 13;
|
||||
|
||||
dev->chandef = *chandef;
|
||||
dev->cal.channel_cal_done = false;
|
||||
freq = chandef->chan->center_freq;
|
||||
freq1 = chandef->center_freq1;
|
||||
channel = chan->hw_value;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
bw = 1;
|
||||
if (freq1 > freq) {
|
||||
bw_index = 1;
|
||||
ch_group_index = 0;
|
||||
} else {
|
||||
bw_index = 3;
|
||||
ch_group_index = 1;
|
||||
}
|
||||
channel += 2 - ch_group_index * 4;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
ch_group_index = (freq - freq1 + 30) / 20;
|
||||
if (WARN_ON(ch_group_index < 0 || ch_group_index > 3))
|
||||
ch_group_index = 0;
|
||||
bw = 2;
|
||||
bw_index = ch_group_index;
|
||||
channel += 6 - ch_group_index * 4;
|
||||
break;
|
||||
default:
|
||||
bw = 0;
|
||||
bw_index = 0;
|
||||
ch_group_index = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mt76_read_rx_gain(dev);
|
||||
mt76_phy_set_txpower_regs(dev, band);
|
||||
mt76_configure_tx_delay(dev, band, bw);
|
||||
mt76_phy_set_txpower(dev);
|
||||
mt76_apply_rate_power_table(dev);
|
||||
|
||||
mt76_set_rx_chains(dev);
|
||||
mt76_phy_set_band(dev, chan->band, ch_group_index & 1);
|
||||
mt76_phy_set_bw(dev, chandef->width, ch_group_index);
|
||||
mt76_set_tx_dac(dev);
|
||||
|
||||
mt76_rmw(dev, MT_EXT_CCA_CFG,
|
||||
(MT_EXT_CCA_CFG_CCA0 |
|
||||
MT_EXT_CCA_CFG_CCA1 |
|
||||
MT_EXT_CCA_CFG_CCA2 |
|
||||
MT_EXT_CCA_CFG_CCA3 |
|
||||
MT_EXT_CCA_CFG_CCA_MASK),
|
||||
ext_cca_chan[ch_group_index]);
|
||||
|
||||
if (chandef->width >= NL80211_CHAN_WIDTH_40)
|
||||
sifs++;
|
||||
|
||||
mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, sifs);
|
||||
|
||||
ret = mt76_mcu_set_channel(dev, channel, bw, bw_index, scan);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true);
|
||||
|
||||
/* Enable LDPC Rx */
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
|
||||
mt76_set(dev, MT_BBP(RXO, 13), BIT(10));
|
||||
|
||||
if (!dev->cal.init_cal_done) {
|
||||
u8 val = mt76_eeprom_get(dev, MT_EE_BT_RCAL_RESULT);
|
||||
|
||||
if (val != 0xff)
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_R, 0);
|
||||
}
|
||||
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel);
|
||||
|
||||
/* Rx LPF calibration */
|
||||
if (!dev->cal.init_cal_done)
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_RC, 0);
|
||||
|
||||
dev->cal.init_cal_done = true;
|
||||
|
||||
mt76_wr(dev, MT_BBP(AGC, 61), 0xFF64A4E2);
|
||||
mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010);
|
||||
mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404);
|
||||
mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070);
|
||||
mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x04101B3F);
|
||||
|
||||
if (scan)
|
||||
return 0;
|
||||
|
||||
dev->cal.low_gain = -1;
|
||||
mt76_phy_channel_calibrate(dev, true);
|
||||
mt76_get_agc_gain(dev, dev->cal.agc_gain_init);
|
||||
|
||||
ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
|
||||
MT_CALIBRATE_INTERVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_tssi_compensate(struct mt76_dev *dev)
|
||||
{
|
||||
struct ieee80211_channel *chan = dev->chandef.chan;
|
||||
struct mt76_tx_power_info txp;
|
||||
struct mt76_tssi_comp t = {};
|
||||
|
||||
if (!dev->cal.tssi_cal_done)
|
||||
return;
|
||||
|
||||
if (dev->cal.tssi_comp_done) {
|
||||
/* TSSI trigger */
|
||||
t.cal_mode = BIT(0);
|
||||
mt76_mcu_tssi_comp(dev, &t);
|
||||
} else {
|
||||
if (!(mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)))
|
||||
return;
|
||||
|
||||
mt76_get_power_info(dev, &txp);
|
||||
|
||||
if (mt76_ext_pa_enabled(dev, chan->band))
|
||||
t.pa_mode = 1;
|
||||
|
||||
t.cal_mode = BIT(1);
|
||||
t.slope0 = txp.chain[0].tssi_slope;
|
||||
t.offset0 = txp.chain[0].tssi_offset;
|
||||
t.slope1 = txp.chain[1].tssi_slope;
|
||||
t.offset1 = txp.chain[1].tssi_offset;
|
||||
dev->cal.tssi_comp_done = true;
|
||||
mt76_mcu_tssi_comp(dev, &t);
|
||||
|
||||
if (t.pa_mode || dev->cal.dpd_cal_done)
|
||||
return;
|
||||
|
||||
msleep(10);
|
||||
mt76_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value);
|
||||
dev->cal.dpd_cal_done = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_phy_temp_compensate(struct mt76_dev *dev)
|
||||
{
|
||||
struct mt76_temp_comp t;
|
||||
int temp, db_diff;
|
||||
|
||||
if (mt76_get_temp_comp(dev, &t))
|
||||
return;
|
||||
|
||||
temp = mt76_get_field(dev, MT_TEMP_SENSOR, MT_TEMP_SENSOR_VAL);
|
||||
temp -= t.temp_25_ref;
|
||||
temp = (temp * 1789) / 1000 + 25;
|
||||
dev->cal.temp = temp;
|
||||
|
||||
if (temp > 25)
|
||||
db_diff = (temp - 25) / t.high_slope;
|
||||
else
|
||||
db_diff = (25 - temp) / t.low_slope;
|
||||
|
||||
db_diff = min(db_diff, t.upper_bound);
|
||||
db_diff = max(db_diff, t.lower_bound);
|
||||
|
||||
mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
|
||||
db_diff * 2);
|
||||
mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP,
|
||||
db_diff * 2);
|
||||
}
|
||||
|
||||
void mt76_phy_calibrate(struct work_struct *work)
|
||||
{
|
||||
struct mt76_dev *dev;
|
||||
|
||||
dev = container_of(work, struct mt76_dev, cal_work.work);
|
||||
mt76_phy_channel_calibrate(dev, false);
|
||||
mt76_phy_tssi_compensate(dev);
|
||||
mt76_phy_temp_compensate(dev);
|
||||
mt76_phy_update_channel_gain(dev);
|
||||
ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
|
||||
MT_CALIBRATE_INTERVAL);
|
||||
}
|
||||
|
||||
int mt76_phy_start(struct mt76_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mt76_mcu_set_radio_state(dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __MT76_REGS_H
|
||||
#define __MT76_REGS_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#ifndef GENMASK
|
||||
#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
|
||||
#endif
|
||||
|
||||
#define MT_ASIC_VERSION 0x0000
|
||||
|
||||
#define MT76XX_REV_E3 0x22
|
||||
#define MT76XX_REV_E4 0x33
|
||||
|
||||
#define MT_CMB_CTRL 0x0020
|
||||
#define MT_CMB_CTRL_XTAL_RDY BIT(22)
|
||||
#define MT_CMB_CTRL_PLL_LD BIT(23)
|
||||
|
||||
#define MT_EFUSE_CTRL 0x0024
|
||||
#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0)
|
||||
#define MT_EFUSE_CTRL_MODE GENMASK(7, 6)
|
||||
#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8)
|
||||
#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14)
|
||||
#define MT_EFUSE_CTRL_AIN GENMASK(25, 16)
|
||||
#define MT_EFUSE_CTRL_KICK BIT(30)
|
||||
#define MT_EFUSE_CTRL_SEL BIT(31)
|
||||
|
||||
#define MT_EFUSE_DATA_BASE 0x0028
|
||||
#define MT_EFUSE_DATA(_n) (MT_EFUSE_DATA_BASE + ((_n) << 2))
|
||||
|
||||
#define MT_COEXCFG0 0x0040
|
||||
#define MT_COEXCFG0_COEX_EN BIT(0)
|
||||
|
||||
#define MT_WLAN_FUN_CTRL 0x0080
|
||||
#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0)
|
||||
#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1)
|
||||
#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2)
|
||||
|
||||
#define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */
|
||||
#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */
|
||||
|
||||
#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4)
|
||||
#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5)
|
||||
#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6)
|
||||
#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7)
|
||||
|
||||
#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) /* MT76x2 */
|
||||
#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) /* MT76x2 */
|
||||
|
||||
#define MT_WLAN_FUN_CTRL_GPIO_IN GENMASK(15, 8) /* MT76x0 */
|
||||
#define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */
|
||||
#define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */
|
||||
|
||||
#define MT_XO_CTRL0 0x0100
|
||||
#define MT_XO_CTRL1 0x0104
|
||||
#define MT_XO_CTRL2 0x0108
|
||||
#define MT_XO_CTRL3 0x010c
|
||||
#define MT_XO_CTRL4 0x0110
|
||||
|
||||
#define MT_XO_CTRL5 0x0114
|
||||
#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8)
|
||||
|
||||
#define MT_XO_CTRL6 0x0118
|
||||
#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8)
|
||||
|
||||
#define MT_XO_CTRL7 0x011c
|
||||
|
||||
#define MT_WLAN_MTC_CTRL 0x10148
|
||||
#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0)
|
||||
#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12)
|
||||
#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13)
|
||||
#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16)
|
||||
#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20)
|
||||
#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21)
|
||||
#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22)
|
||||
#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24)
|
||||
#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25)
|
||||
#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26)
|
||||
#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27)
|
||||
#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28)
|
||||
|
||||
#define MT_INT_SOURCE_CSR 0x0200
|
||||
#define MT_INT_MASK_CSR 0x0204
|
||||
|
||||
#define MT_INT_RX_DONE(_n) BIT(_n)
|
||||
#define MT_INT_RX_DONE_ALL GENMASK(1, 0)
|
||||
#define MT_INT_TX_DONE_ALL GENMASK(13, 4)
|
||||
#define MT_INT_TX_DONE(_n) BIT(_n + 4)
|
||||
#define MT_INT_RX_COHERENT BIT(16)
|
||||
#define MT_INT_TX_COHERENT BIT(17)
|
||||
#define MT_INT_ANY_COHERENT BIT(18)
|
||||
#define MT_INT_MCU_CMD BIT(19)
|
||||
#define MT_INT_TBTT BIT(20)
|
||||
#define MT_INT_PRE_TBTT BIT(21)
|
||||
#define MT_INT_TX_STAT BIT(22)
|
||||
#define MT_INT_AUTO_WAKEUP BIT(23)
|
||||
#define MT_INT_GPTIMER BIT(24)
|
||||
#define MT_INT_RXDELAYINT BIT(26)
|
||||
#define MT_INT_TXDELAYINT BIT(27)
|
||||
|
||||
#define MT_WPDMA_GLO_CFG 0x0208
|
||||
#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0)
|
||||
#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1)
|
||||
#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2)
|
||||
#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3)
|
||||
#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4)
|
||||
#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6)
|
||||
#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7)
|
||||
#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8)
|
||||
#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30)
|
||||
#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31)
|
||||
|
||||
#define MT_WPDMA_RST_IDX 0x020c
|
||||
|
||||
#define MT_WPDMA_DELAY_INT_CFG 0x0210
|
||||
|
||||
#define MT_WMM_AIFSN 0x0214
|
||||
#define MT_WMM_AIFSN_MASK GENMASK(3, 0)
|
||||
#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4)
|
||||
|
||||
#define MT_WMM_CWMIN 0x0218
|
||||
#define MT_WMM_CWMIN_MASK GENMASK(3, 0)
|
||||
#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 4)
|
||||
|
||||
#define MT_WMM_CWMAX 0x021c
|
||||
#define MT_WMM_CWMAX_MASK GENMASK(3, 0)
|
||||
#define MT_WMM_CWMAX_SHIFT(_n) ((_n) * 4)
|
||||
|
||||
#define MT_WMM_TXOP_BASE 0x0220
|
||||
#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + (((_n) / 2) << 2))
|
||||
#define MT_WMM_TXOP_SHIFT(_n) ((_n & 1) * 16)
|
||||
#define MT_WMM_TXOP_MASK GENMASK(15, 0)
|
||||
|
||||
#define MT_TSO_CTRL 0x0250
|
||||
#define MT_HEADER_TRANS_CTRL_REG 0x0260
|
||||
|
||||
#define MT_TX_RING_BASE 0x0300
|
||||
#define MT_RX_RING_BASE 0x03c0
|
||||
#define MT_RING_SIZE 0x10
|
||||
|
||||
#define MT_TX_HW_QUEUE_MCU 8
|
||||
#define MT_TX_HW_QUEUE_MGMT 9
|
||||
|
||||
#define MT_PBF_SYS_CTRL 0x0400
|
||||
#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0)
|
||||
#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1)
|
||||
#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2)
|
||||
#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3)
|
||||
#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4)
|
||||
|
||||
#define MT_PBF_CFG 0x0404
|
||||
#define MT_PBF_CFG_TX0Q_EN BIT(0)
|
||||
#define MT_PBF_CFG_TX1Q_EN BIT(1)
|
||||
#define MT_PBF_CFG_TX2Q_EN BIT(2)
|
||||
#define MT_PBF_CFG_TX3Q_EN BIT(3)
|
||||
#define MT_PBF_CFG_RX0Q_EN BIT(4)
|
||||
#define MT_PBF_CFG_RX_DROP_EN BIT(8)
|
||||
|
||||
#define MT_PBF_TX_MAX_PCNT 0x0408
|
||||
#define MT_PBF_RX_MAX_PCNT 0x040c
|
||||
|
||||
#define MT_BCN_OFFSET_BASE 0x041c
|
||||
#define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2))
|
||||
|
||||
#define MT_RF_BYPASS_0 0x0504
|
||||
#define MT_RF_BYPASS_1 0x0508
|
||||
#define MT_RF_SETTING_0 0x050c
|
||||
|
||||
#define MT_RF_DATA_WRITE 0x0524
|
||||
|
||||
#define MT_RF_CTRL 0x0528
|
||||
#define MT_RF_CTRL_ADDR GENMASK(11, 0)
|
||||
#define MT_RF_CTRL_WRITE BIT(12)
|
||||
#define MT_RF_CTRL_BUSY BIT(13)
|
||||
#define MT_RF_CTRL_IDX BIT(16)
|
||||
|
||||
#define MT_RF_DATA_READ 0x052c
|
||||
|
||||
#define MT_FCE_PSE_CTRL 0x0800
|
||||
#define MT_FCE_PARAMETERS 0x0804
|
||||
#define MT_FCE_CSO 0x0808
|
||||
|
||||
#define MT_FCE_L2_STUFF 0x080c
|
||||
#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0)
|
||||
#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1)
|
||||
#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2)
|
||||
#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3)
|
||||
#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4)
|
||||
#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5)
|
||||
#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8)
|
||||
#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16)
|
||||
#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24)
|
||||
|
||||
#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824
|
||||
|
||||
#define MT_PAUSE_ENABLE_CONTROL1 0x0a38
|
||||
|
||||
#define MT_MAC_CSR0 0x1000
|
||||
|
||||
#define MT_MAC_SYS_CTRL 0x1004
|
||||
#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0)
|
||||
#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1)
|
||||
#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2)
|
||||
#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3)
|
||||
|
||||
#define MT_MAC_ADDR_DW0 0x1008
|
||||
#define MT_MAC_ADDR_DW1 0x100c
|
||||
|
||||
#define MT_MAC_BSSID_DW0 0x1010
|
||||
#define MT_MAC_BSSID_DW1 0x1014
|
||||
#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0)
|
||||
#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16)
|
||||
#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18)
|
||||
#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21)
|
||||
#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22)
|
||||
#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23)
|
||||
#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24)
|
||||
|
||||
#define MT_MAX_LEN_CFG 0x1018
|
||||
|
||||
#define MT_AMPDU_MAX_LEN_20M1S 0x1030
|
||||
#define MT_AMPDU_MAX_LEN_20M2S 0x1034
|
||||
#define MT_AMPDU_MAX_LEN_40M1S 0x1038
|
||||
#define MT_AMPDU_MAX_LEN_40M2S 0x103c
|
||||
#define MT_AMPDU_MAX_LEN 0x1040
|
||||
|
||||
#define MT_WCID_DROP_BASE 0x106c
|
||||
#define MT_WCID_DROP(_n) (MT_WCID_DROP_BASE + ((_n) >> 5) * 4)
|
||||
#define MT_WCID_DROP_MASK(_n) BIT((_n) % 32)
|
||||
|
||||
#define MT_BCN_BYPASS_MASK 0x108c
|
||||
|
||||
#define MT_MAC_APC_BSSID_BASE 0x1090
|
||||
#define MT_MAC_APC_BSSID_L(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8))
|
||||
#define MT_MAC_APC_BSSID_H(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8 + 4))
|
||||
#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0)
|
||||
#define MT_MAC_APC_BSSID0_H_EN BIT(16)
|
||||
|
||||
#define MT_XIFS_TIME_CFG 0x1100
|
||||
#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0)
|
||||
#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8)
|
||||
#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16)
|
||||
#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20)
|
||||
#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29)
|
||||
|
||||
#define MT_BKOFF_SLOT_CFG 0x1104
|
||||
#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0)
|
||||
#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8)
|
||||
|
||||
#define MT_BEACON_TIME_CFG 0x1114
|
||||
#define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0)
|
||||
#define MT_BEACON_TIME_CFG_TIMER_EN BIT(16)
|
||||
#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17)
|
||||
#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19)
|
||||
#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20)
|
||||
#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24)
|
||||
|
||||
#define MT_TBTT_SYNC_CFG 0x1118
|
||||
#define MT_TBTT_TIMER_CFG 0x1124
|
||||
|
||||
#define MT_INT_TIMER_CFG 0x1128
|
||||
#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0)
|
||||
#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16)
|
||||
|
||||
#define MT_INT_TIMER_EN 0x112c
|
||||
#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0)
|
||||
#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1)
|
||||
|
||||
#define MT_MAC_STATUS 0x1200
|
||||
#define MT_MAC_STATUS_TX BIT(0)
|
||||
#define MT_MAC_STATUS_RX BIT(1)
|
||||
|
||||
#define MT_PWR_PIN_CFG 0x1204
|
||||
#define MT_AUX_CLK_CFG 0x120c
|
||||
|
||||
#define MT_BB_PA_MODE_CFG0 0x1214
|
||||
#define MT_BB_PA_MODE_CFG1 0x1218
|
||||
#define MT_RF_PA_MODE_CFG0 0x121c
|
||||
#define MT_RF_PA_MODE_CFG1 0x1220
|
||||
|
||||
#define MT_RF_PA_MODE_ADJ0 0x1228
|
||||
#define MT_RF_PA_MODE_ADJ1 0x122c
|
||||
|
||||
#define MT_DACCLK_EN_DLY_CFG 0x1264
|
||||
|
||||
#define MT_EDCA_CFG_BASE 0x1300
|
||||
#define MT_EDCA_CFG_AC(_n) (MT_EDCA_CFG_BASE + ((_n) << 2))
|
||||
#define MT_EDCA_CFG_TXOP GENMASK(7, 0)
|
||||
#define MT_EDCA_CFG_AIFSN GENMASK(11, 8)
|
||||
#define MT_EDCA_CFG_CWMIN GENMASK(15, 12)
|
||||
#define MT_EDCA_CFG_CWMAX GENMASK(19, 16)
|
||||
|
||||
#define MT_TX_PWR_CFG_0 0x1314
|
||||
#define MT_TX_PWR_CFG_1 0x1318
|
||||
#define MT_TX_PWR_CFG_2 0x131c
|
||||
#define MT_TX_PWR_CFG_3 0x1320
|
||||
#define MT_TX_PWR_CFG_4 0x1324
|
||||
|
||||
#define MT_TX_BAND_CFG 0x132c
|
||||
#define MT_TX_BAND_CFG_UPPER_40M BIT(0)
|
||||
#define MT_TX_BAND_CFG_5G BIT(1)
|
||||
#define MT_TX_BAND_CFG_2G BIT(2)
|
||||
|
||||
#define MT_HT_FBK_TO_LEGACY 0x1384
|
||||
#define MT_TX_MPDU_ADJ_INT 0x1388
|
||||
|
||||
#define MT_TX_PWR_CFG_7 0x13d4
|
||||
#define MT_TX_PWR_CFG_8 0x13d8
|
||||
#define MT_TX_PWR_CFG_9 0x13dc
|
||||
|
||||
#define MT_TX_SW_CFG0 0x1330
|
||||
#define MT_TX_SW_CFG1 0x1334
|
||||
#define MT_TX_SW_CFG2 0x1338
|
||||
|
||||
#define MT_TXOP_CTRL_CFG 0x1340
|
||||
|
||||
#define MT_TX_RTS_CFG 0x1344
|
||||
#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0)
|
||||
#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8)
|
||||
#define MT_TX_RTS_FALLBACK BIT(24)
|
||||
|
||||
#define MT_TX_TIMEOUT_CFG 0x1348
|
||||
#define MT_TX_RETRY_CFG 0x134c
|
||||
#define MT_VHT_HT_FBK_CFG1 0x1358
|
||||
|
||||
#define MT_EXP_ACK_TIME 0x1380
|
||||
|
||||
#define MT_TX_PWR_CFG_0_EXT 0x1390
|
||||
#define MT_TX_PWR_CFG_1_EXT 0x1394
|
||||
|
||||
#define MT_TX_FBK_LIMIT 0x1398
|
||||
#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0)
|
||||
#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8)
|
||||
#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16)
|
||||
#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17)
|
||||
#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18)
|
||||
|
||||
#define MT_TX0_RF_GAIN_CORR 0x13a0
|
||||
#define MT_TX1_RF_GAIN_CORR 0x13a4
|
||||
|
||||
#define MT_TX_ALC_CFG_0 0x13b0
|
||||
#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0)
|
||||
#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8)
|
||||
#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16)
|
||||
#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24)
|
||||
|
||||
#define MT_TX_ALC_CFG_1 0x13b4
|
||||
#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0)
|
||||
|
||||
#define MT_TX_ALC_CFG_2 0x13a8
|
||||
#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0)
|
||||
|
||||
#define MT_TX_ALC_CFG_3 0x13ac
|
||||
#define MT_TX_ALC_CFG_4 0x13c0
|
||||
#define MT_TX_ALC_CFG_4_LOWGAIN_CH_EN BIT(31)
|
||||
|
||||
#define MT_TX_ALC_VGA3 0x13c8
|
||||
|
||||
#define MT_TX_PROT_CFG6 0x13e0
|
||||
#define MT_TX_PROT_CFG7 0x13e4
|
||||
#define MT_TX_PROT_CFG8 0x13e8
|
||||
|
||||
#define MT_PIFS_TX_CFG 0x13ec
|
||||
|
||||
#define MT_RX_FILTR_CFG 0x1400
|
||||
|
||||
#define MT_RX_FILTR_CFG_CRC_ERR BIT(0)
|
||||
#define MT_RX_FILTR_CFG_PHY_ERR BIT(1)
|
||||
#define MT_RX_FILTR_CFG_PROMISC BIT(2)
|
||||
#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3)
|
||||
#define MT_RX_FILTR_CFG_VER_ERR BIT(4)
|
||||
#define MT_RX_FILTR_CFG_MCAST BIT(5)
|
||||
#define MT_RX_FILTR_CFG_BCAST BIT(6)
|
||||
#define MT_RX_FILTR_CFG_DUP BIT(7)
|
||||
#define MT_RX_FILTR_CFG_CFACK BIT(8)
|
||||
#define MT_RX_FILTR_CFG_CFEND BIT(9)
|
||||
#define MT_RX_FILTR_CFG_ACK BIT(10)
|
||||
#define MT_RX_FILTR_CFG_CTS BIT(11)
|
||||
#define MT_RX_FILTR_CFG_RTS BIT(12)
|
||||
#define MT_RX_FILTR_CFG_PSPOLL BIT(13)
|
||||
#define MT_RX_FILTR_CFG_BA BIT(14)
|
||||
#define MT_RX_FILTR_CFG_BAR BIT(15)
|
||||
#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16)
|
||||
|
||||
#define MT_LEGACY_BASIC_RATE 0x1408
|
||||
#define MT_HT_BASIC_RATE 0x140c
|
||||
|
||||
#define MT_EXT_CCA_CFG 0x141c
|
||||
#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0)
|
||||
#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2)
|
||||
#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4)
|
||||
#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6)
|
||||
#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8)
|
||||
#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12)
|
||||
|
||||
#define MT_TX_SW_CFG3 0x1478
|
||||
|
||||
#define MT_PN_PAD_MODE 0x150c
|
||||
|
||||
#define MT_TXOP_HLDR_ET 0x1608
|
||||
|
||||
#define MT_PROT_AUTO_TX_CFG 0x1648
|
||||
|
||||
#define MT_TX_STAT_FIFO 0x1718
|
||||
#define MT_TX_STAT_FIFO_VALID BIT(0)
|
||||
#define MT_TX_STAT_FIFO_SUCCESS BIT(5)
|
||||
#define MT_TX_STAT_FIFO_AGGR BIT(6)
|
||||
#define MT_TX_STAT_FIFO_ACKREQ BIT(7)
|
||||
#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8)
|
||||
#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16)
|
||||
|
||||
#define MT_TX_AGG_CNT_BASE0 0x1720
|
||||
#define MT_TX_AGG_CNT_BASE1 0x174c
|
||||
|
||||
#define MT_TX_AGG_CNT(_id) (((_id) > 7 ? MT_TX_AGG_CNT_BASE1 : \
|
||||
MT_TX_AGG_CNT_BASE0) + ((_id) << 2))
|
||||
|
||||
#define MT_TX_STAT_FIFO_EXT 0x1798
|
||||
#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0)
|
||||
#define MT_TX_STAT_FIFO_EXT_PKTID GENMASK(15, 8)
|
||||
|
||||
#define MT_BBP_CORE_BASE 0x2000
|
||||
#define MT_BBP_IBI_BASE 0x2100
|
||||
#define MT_BBP_AGC_BASE 0x2300
|
||||
#define MT_BBP_TXC_BASE 0x2400
|
||||
#define MT_BBP_RXC_BASE 0x2500
|
||||
#define MT_BBP_TXO_BASE 0x2600
|
||||
#define MT_BBP_TXBE_BASE 0x2700
|
||||
#define MT_BBP_RXFE_BASE 0x2800
|
||||
#define MT_BBP_RXO_BASE 0x2900
|
||||
#define MT_BBP_DFS_BASE 0x2a00
|
||||
#define MT_BBP_TR_BASE 0x2b00
|
||||
#define MT_BBP_CAL_BASE 0x2c00
|
||||
#define MT_BBP_DSC_BASE 0x2e00
|
||||
#define MT_BBP_PFMU_BASE 0x2f00
|
||||
|
||||
#define MT_BBP(_type, _n) (MT_BBP_##_type##_BASE + ((_n) << 2))
|
||||
|
||||
#define MT_BBP_CORE_R1_BW GENMASK(4, 3)
|
||||
|
||||
#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8)
|
||||
#define MT_BBP_AGC_R0_BW GENMASK(14, 12)
|
||||
|
||||
/* AGC, R4/R5 */
|
||||
#define MT_BBP_AGC_LNA_GAIN GENMASK(21, 16)
|
||||
|
||||
/* AGC, R8/R9 */
|
||||
#define MT_BBP_AGC_GAIN GENMASK(14, 8)
|
||||
|
||||
#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0)
|
||||
#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8)
|
||||
|
||||
#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0)
|
||||
|
||||
#define MT_WCID_ADDR_BASE 0x1800
|
||||
#define MT_WCID_ADDR(_n) (MT_WCID_ADDR_BASE + (_n) * 8)
|
||||
|
||||
#define MT_SRAM_BASE 0x4000
|
||||
|
||||
#define MT_WCID_KEY_BASE 0x8000
|
||||
#define MT_WCID_KEY(_n) (MT_WCID_KEY_BASE + (_n) * 32)
|
||||
|
||||
#define MT_WCID_IV_BASE 0xa000
|
||||
#define MT_WCID_IV(_n) (MT_WCID_IV_BASE + (_n) * 8)
|
||||
|
||||
#define MT_WCID_ATTR_BASE 0xa800
|
||||
#define MT_WCID_ATTR(_n) (MT_WCID_ATTR_BASE + (_n) * 4)
|
||||
|
||||
#define MT_WCID_ATTR_PAIRWISE BIT(0)
|
||||
#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1)
|
||||
#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4)
|
||||
#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7)
|
||||
#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10)
|
||||
#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11)
|
||||
#define MT_WCID_ATTR_WAPI_MCBC BIT(15)
|
||||
#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24)
|
||||
|
||||
#define MT_SKEY_BASE_0 0xac00
|
||||
#define MT_SKEY_BASE_1 0xb400
|
||||
#define MT_SKEY_0(_bss, _idx) (MT_SKEY_BASE_0 + (4 * (_bss) + _idx) * 32)
|
||||
#define MT_SKEY_1(_bss, _idx) (MT_SKEY_BASE_1 + (4 * ((_bss) & 7) + _idx) * 32)
|
||||
#define MT_SKEY(_bss, _idx) ((_bss & 8) ? MT_SKEY_1(_bss, _idx) : MT_SKEY_0(_bss, _idx))
|
||||
|
||||
#define MT_SKEY_MODE_BASE_0 0xb000
|
||||
#define MT_SKEY_MODE_BASE_1 0xb3f0
|
||||
#define MT_SKEY_MODE_0(_bss) (MT_SKEY_MODE_BASE_0 + ((_bss / 2) << 2))
|
||||
#define MT_SKEY_MODE_1(_bss) (MT_SKEY_MODE_BASE_1 + ((((_bss) & 7) / 2) << 2))
|
||||
#define MT_SKEY_MODE(_bss) ((_bss & 8) ? MT_SKEY_MODE_1(_bss) : MT_SKEY_MODE_0(_bss))
|
||||
#define MT_SKEY_MODE_MASK GENMASK(3, 0)
|
||||
#define MT_SKEY_MODE_SHIFT(_bss, _idx) (4 * ((_idx) + 4 * (_bss & 1)))
|
||||
|
||||
#define MT_BEACON_BASE 0xc000
|
||||
|
||||
#define MT_TEMP_SENSOR 0x1d000
|
||||
#define MT_TEMP_SENSOR_VAL GENMASK(6, 0)
|
||||
|
||||
struct mt76_wcid_addr {
|
||||
u8 macaddr[6];
|
||||
__le16 ba_mask;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct mt76_wcid_key {
|
||||
u8 key[16];
|
||||
u8 tx_mic[8];
|
||||
u8 rx_mic[8];
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct mt76_queue_regs {
|
||||
u32 desc_base;
|
||||
u32 ring_size;
|
||||
u32 cpu_idx;
|
||||
u32 dma_idx;
|
||||
} __packed __aligned(4);
|
||||
|
||||
enum mt76_cipher_type {
|
||||
MT_CIPHER_NONE,
|
||||
MT_CIPHER_WEP40,
|
||||
MT_CIPHER_WEP104,
|
||||
MT_CIPHER_TKIP,
|
||||
MT_CIPHER_AES_CCMP,
|
||||
MT_CIPHER_CKIP40,
|
||||
MT_CIPHER_CKIP104,
|
||||
MT_CIPHER_CKIP128,
|
||||
MT_CIPHER_WAPI,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#if !defined(__MT76_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __MT76_TRACE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include "mt76.h"
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM mt76
|
||||
|
||||
#define MAXNAME 32
|
||||
#define DEV_ENTRY __array(char, wiphy_name, 32)
|
||||
#define DEV_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(dev->hw->wiphy), MAXNAME)
|
||||
#define DEV_PR_FMT "%s"
|
||||
#define DEV_PR_ARG __entry->wiphy_name
|
||||
|
||||
#define TXID_ENTRY __field(u8, wcid) __field(u8, pktid)
|
||||
#define TXID_ASSIGN __entry->wcid = wcid; __entry->pktid = pktid
|
||||
#define TXID_PR_FMT " [%d:%d]"
|
||||
#define TXID_PR_ARG __entry->wcid, __entry->pktid
|
||||
|
||||
#define REG_ENTRY __field(u32, reg) __field(u32, val)
|
||||
#define REG_ASSIGN __entry->reg = reg; __entry->val = val
|
||||
#define REG_PR_FMT " %04x=%08x"
|
||||
#define REG_PR_ARG __entry->reg, __entry->val
|
||||
|
||||
DECLARE_EVENT_CLASS(dev_evt,
|
||||
TP_PROTO(struct mt76_dev *dev),
|
||||
TP_ARGS(dev),
|
||||
TP_STRUCT__entry(
|
||||
DEV_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
DEV_ASSIGN;
|
||||
),
|
||||
TP_printk(DEV_PR_FMT, DEV_PR_ARG)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dev_txid_evt,
|
||||
TP_PROTO(struct mt76_dev *dev, u8 wcid, u8 pktid),
|
||||
TP_ARGS(dev, wcid, pktid),
|
||||
TP_STRUCT__entry(
|
||||
DEV_ENTRY
|
||||
TXID_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
DEV_ASSIGN;
|
||||
TXID_ASSIGN;
|
||||
),
|
||||
TP_printk(
|
||||
DEV_PR_FMT TXID_PR_FMT,
|
||||
DEV_PR_ARG, TXID_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dev_reg_evt,
|
||||
TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
|
||||
TP_ARGS(dev, reg, val),
|
||||
TP_STRUCT__entry(
|
||||
DEV_ENTRY
|
||||
REG_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
DEV_ASSIGN;
|
||||
REG_ASSIGN;
|
||||
),
|
||||
TP_printk(
|
||||
DEV_PR_FMT REG_PR_FMT,
|
||||
DEV_PR_ARG, REG_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dev_reg_evt, reg_read,
|
||||
TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
|
||||
TP_ARGS(dev, reg, val)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dev_reg_evt, reg_write,
|
||||
TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val),
|
||||
TP_ARGS(dev, reg, val)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dev_evt, mac_txstat_poll,
|
||||
TP_PROTO(struct mt76_dev *dev),
|
||||
TP_ARGS(dev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dev_txid_evt, mac_txdone_add,
|
||||
TP_PROTO(struct mt76_dev *dev, u8 wcid, u8 pktid),
|
||||
TP_ARGS(dev, wcid, pktid)
|
||||
);
|
||||
|
||||
TRACE_EVENT(mac_txstat_fetch,
|
||||
TP_PROTO(struct mt76_dev *dev,
|
||||
struct mt76_tx_status *stat),
|
||||
|
||||
TP_ARGS(dev, stat),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
DEV_ENTRY
|
||||
TXID_ENTRY
|
||||
__field(bool, success)
|
||||
__field(bool, aggr)
|
||||
__field(bool, ack_req)
|
||||
__field(u16, rate)
|
||||
__field(u8, retry)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
DEV_ASSIGN;
|
||||
__entry->success = stat->success;
|
||||
__entry->aggr = stat->aggr;
|
||||
__entry->ack_req = stat->ack_req;
|
||||
__entry->wcid = stat->wcid;
|
||||
__entry->pktid = stat->pktid;
|
||||
__entry->rate = stat->rate;
|
||||
__entry->retry = stat->retry;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
DEV_PR_FMT TXID_PR_FMT
|
||||
" success:%d aggr:%d ack_req:%d"
|
||||
" rate:%04x retry:%d",
|
||||
DEV_PR_ARG, TXID_PR_ARG,
|
||||
__entry->success, __entry->aggr, __entry->ack_req,
|
||||
__entry->rate, __entry->retry
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
TRACE_EVENT(dev_irq,
|
||||
TP_PROTO(struct mt76_dev *dev, u32 val, u32 mask),
|
||||
|
||||
TP_ARGS(dev, val, mask),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
DEV_ENTRY
|
||||
__field(u32, val)
|
||||
__field(u32, mask)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
DEV_ASSIGN;
|
||||
__entry->val = val;
|
||||
__entry->mask = mask;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
DEV_PR_FMT " %08x & %08x",
|
||||
DEV_PR_ARG, __entry->val, __entry->mask
|
||||
)
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
#include <trace/define_trace.h>
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.h"
|
||||
|
||||
struct beacon_bc_data {
|
||||
struct mt76_dev *dev;
|
||||
struct sk_buff_head q;
|
||||
struct sk_buff *tail[8];
|
||||
};
|
||||
|
||||
void mt76_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
struct ieee80211_vif *vif = info->control.vif;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
struct mt76_sta *msta = NULL;
|
||||
struct mt76_wcid *wcid = &mvif->group_wcid;
|
||||
struct mt76_queue *q;
|
||||
int qid = skb_get_queue_mapping(skb);
|
||||
|
||||
if (WARN_ON(qid >= MT_TXQ_PSD)) {
|
||||
qid = MT_TXQ_BE;
|
||||
skb_set_queue_mapping(skb, qid);
|
||||
}
|
||||
|
||||
if (control->sta) {
|
||||
msta = (struct mt76_sta *) control->sta->drv_priv;
|
||||
wcid = &msta->wcid;
|
||||
}
|
||||
|
||||
if (!wcid->tx_rate_set)
|
||||
ieee80211_get_tx_rates(info->control.vif, control->sta, skb,
|
||||
info->control.rates, 1);
|
||||
|
||||
q = &dev->q_tx[qid];
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
mt76_tx_queue_skb(dev, q, skb, wcid, control->sta);
|
||||
mt76_kick_queue(dev, q);
|
||||
|
||||
if (q->queued > q->ndesc - 8)
|
||||
ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
|
||||
spin_unlock_bh(&q->lock);
|
||||
}
|
||||
|
||||
void mt76_tx_complete(struct mt76_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct mt76_queue *q;
|
||||
int qid = skb_get_queue_mapping(skb);
|
||||
|
||||
ieee80211_tx_info_clear_status(info);
|
||||
info->status.rates[0].idx = -1;
|
||||
info->flags |= IEEE80211_TX_STAT_ACK;
|
||||
ieee80211_tx_status(dev->hw, skb);
|
||||
|
||||
q = &dev->q_tx[qid];
|
||||
if (q->queued < q->ndesc - 8)
|
||||
ieee80211_wake_queue(dev->hw, qid);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mt76_dev *dev = (struct mt76_dev *) priv;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb = NULL;
|
||||
|
||||
if (!(dev->beacon_mask & BIT(mvif->idx)))
|
||||
return;
|
||||
|
||||
skb = ieee80211_beacon_get(dev->hw, vif);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
|
||||
mt76_mac_set_beacon(dev, mvif->idx, skb);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_skb_set_moredata(struct sk_buff *skb, bool enable)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
|
||||
if (enable)
|
||||
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
||||
else
|
||||
hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
||||
}
|
||||
|
||||
static void
|
||||
mt76_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct beacon_bc_data *data = priv;
|
||||
struct mt76_dev *dev = data->dev;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!(dev->beacon_mask & BIT(mvif->idx)))
|
||||
return;
|
||||
|
||||
skb = ieee80211_get_buffered_bc(dev->hw, vif);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->control.vif = vif;
|
||||
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
|
||||
mt76_skb_set_moredata(skb, true);
|
||||
__skb_queue_tail(&data->q, skb);
|
||||
data->tail[mvif->idx] = skb;
|
||||
}
|
||||
|
||||
void mt76_pre_tbtt_tasklet(unsigned long arg)
|
||||
{
|
||||
struct mt76_dev *dev = (struct mt76_dev *) arg;
|
||||
struct mt76_queue *q = &dev->q_tx[MT_TXQ_PSD];
|
||||
struct beacon_bc_data data = {};
|
||||
struct sk_buff *skb;
|
||||
int i, nframes;
|
||||
|
||||
data.dev = dev;
|
||||
__skb_queue_head_init(&data.q);
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(dev->hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
mt76_update_beacon_iter, dev);
|
||||
|
||||
do {
|
||||
nframes = skb_queue_len(&data.q);
|
||||
ieee80211_iterate_active_interfaces_atomic(dev->hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
mt76_add_buffered_bc, &data);
|
||||
} while (nframes != skb_queue_len(&data.q));
|
||||
|
||||
if (!nframes)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
|
||||
if (!data.tail[i])
|
||||
continue;
|
||||
|
||||
mt76_skb_set_moredata(data.tail[i], false);
|
||||
}
|
||||
|
||||
spin_lock_bh(&q->lock);
|
||||
while ((skb = __skb_dequeue(&data.q)) != NULL) {
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_vif *vif = info->control.vif;
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv;
|
||||
|
||||
mt76_tx_queue_skb(dev, q, skb, &mvif->group_wcid, NULL);
|
||||
}
|
||||
spin_unlock_bh(&q->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_txq_get_qid(struct ieee80211_txq *txq)
|
||||
{
|
||||
if (!txq->sta)
|
||||
return MT_TXQ_BE;
|
||||
|
||||
switch (txq->tid) {
|
||||
case 0:
|
||||
case 3:
|
||||
return MT_TXQ_BE;
|
||||
case 1:
|
||||
case 2:
|
||||
return MT_TXQ_BK;
|
||||
case 4:
|
||||
case 5:
|
||||
return MT_TXQ_VI;
|
||||
default:
|
||||
return MT_TXQ_VO;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
|
||||
struct mt76_txq *mtxq, bool *empty)
|
||||
{
|
||||
struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
|
||||
struct ieee80211_tx_info *info;
|
||||
struct mt76_wcid *wcid;
|
||||
struct sk_buff *skb = NULL;
|
||||
int n_frames = 1, limit;
|
||||
struct ieee80211_tx_rate tx_rate;
|
||||
bool ampdu;
|
||||
bool probe;
|
||||
int idx;
|
||||
|
||||
if (txq->sta) {
|
||||
struct mt76_sta *sta = (struct mt76_sta *) txq->sta->drv_priv;
|
||||
wcid = &sta->wcid;
|
||||
} else {
|
||||
struct mt76_vif *mvif = (struct mt76_vif *) txq->vif->drv_priv;
|
||||
wcid = &mvif->group_wcid;
|
||||
}
|
||||
|
||||
if (ieee80211_tx_dequeue(dev->hw, txq, &skb)) {
|
||||
*empty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
if (!wcid->tx_rate_set)
|
||||
ieee80211_get_tx_rates(txq->vif, txq->sta, skb,
|
||||
info->control.rates, 1);
|
||||
tx_rate = info->control.rates[0];
|
||||
|
||||
probe = (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
|
||||
ampdu = IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU;
|
||||
limit = ampdu ? 16 : 3;
|
||||
idx = mt76_tx_queue_skb(dev, hwq, skb, wcid, txq->sta);
|
||||
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
do {
|
||||
bool cur_ampdu;
|
||||
|
||||
if (probe)
|
||||
break;
|
||||
|
||||
if (ieee80211_tx_dequeue(dev->hw, txq, &skb)) {
|
||||
*empty = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cur_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
|
||||
|
||||
if (ampdu != cur_ampdu ||
|
||||
(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) {
|
||||
skb_queue_tail(&mtxq->retry_q, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->control.rates[0] = tx_rate;
|
||||
|
||||
idx = mt76_tx_queue_skb(dev, hwq, skb, wcid, txq->sta);
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
n_frames++;
|
||||
} while (n_frames < limit);
|
||||
|
||||
if (!probe) {
|
||||
hwq->swq_queued++;
|
||||
hwq->entry[idx].schedule = true;
|
||||
}
|
||||
|
||||
mt76_kick_queue(dev, hwq);
|
||||
|
||||
return n_frames;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
|
||||
{
|
||||
struct mt76_txq *mtxq, *mtxq_last;
|
||||
int len = 0;
|
||||
|
||||
mtxq_last = list_last_entry(&hwq->swq, struct mt76_txq, list);
|
||||
while (1) {
|
||||
bool empty = false;
|
||||
int cur, len;
|
||||
|
||||
if (hwq->swq_queued >= 4)
|
||||
break;
|
||||
|
||||
if (list_empty(&hwq->swq))
|
||||
break;
|
||||
|
||||
mtxq = list_first_entry(&hwq->swq, struct mt76_txq, list);
|
||||
list_del_init(&mtxq->list);
|
||||
|
||||
cur = mt76_txq_send_burst(dev, hwq, mtxq, &empty);
|
||||
if (!empty)
|
||||
list_add_tail(&mtxq->list, &hwq->swq);
|
||||
|
||||
if (cur < 0)
|
||||
return cur;
|
||||
|
||||
len += cur;
|
||||
|
||||
if (mtxq == mtxq_last)
|
||||
break;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq)
|
||||
{
|
||||
int len;
|
||||
|
||||
do {
|
||||
len = mt76_txq_schedule_list(dev, hwq);
|
||||
} while (len > 0);
|
||||
}
|
||||
|
||||
void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq)
|
||||
{
|
||||
struct mt76_txq *mtxq;
|
||||
|
||||
if (!txq)
|
||||
return;
|
||||
|
||||
mtxq = (struct mt76_txq *) txq->drv_priv;
|
||||
INIT_LIST_HEAD(&mtxq->list);
|
||||
skb_queue_head_init(&mtxq->retry_q);
|
||||
|
||||
mtxq->hwq = &dev->q_tx[mt76_txq_get_qid(txq)];
|
||||
}
|
||||
|
||||
void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
{
|
||||
struct mt76_dev *dev = hw->priv;
|
||||
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
|
||||
struct mt76_queue *hwq = mtxq->hwq;
|
||||
|
||||
spin_lock_bh(&hwq->lock);
|
||||
if (list_empty(&mtxq->list))
|
||||
list_add_tail(&mtxq->list, &hwq->swq);
|
||||
mt76_txq_schedule(dev, hwq);
|
||||
spin_unlock_bh(&hwq->lock);
|
||||
}
|
||||
|
||||
void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
|
||||
{
|
||||
struct mt76_txq *mtxq;
|
||||
struct mt76_queue *hwq;
|
||||
|
||||
if (!txq)
|
||||
return;
|
||||
|
||||
mtxq = (struct mt76_txq *) txq->drv_priv;
|
||||
hwq = mtxq->hwq;
|
||||
|
||||
spin_lock_bh(&hwq->lock);
|
||||
if (!list_empty(&mtxq->list))
|
||||
list_del(&mtxq->list);
|
||||
spin_unlock_bh(&hwq->lock);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
*
|
||||
* 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 "mt76.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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <[email protected]>
|
||||
* Copyright (C) 2004 - 2009 Ivo van Doorn <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __MT76_UTIL_H
|
||||
#define __MT76_UTIL_H
|
||||
|
||||
/*
|
||||
* Power of two check, this will check
|
||||
* if the mask that has been given contains and contiguous set of bits.
|
||||
* Note that we cannot use the is_power_of_2() function since this
|
||||
* check must be done at compile-time.
|
||||
*/
|
||||
#define is_power_of_two(x) ( !((x) & ((x)-1)) )
|
||||
#define low_bit_mask(x) ( ((x)-1) & ~(x) )
|
||||
#define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x))
|
||||
|
||||
/*
|
||||
* Macros to find first set bit in a variable.
|
||||
* These macros behave the same as the __ffs() functions but
|
||||
* the most important difference that this is done during
|
||||
* compile-time rather then run-time.
|
||||
*/
|
||||
#define compile_ffs2(__x) \
|
||||
__builtin_choose_expr(((__x) & 0x1), 0, 1)
|
||||
|
||||
#define compile_ffs4(__x) \
|
||||
__builtin_choose_expr(((__x) & 0x3), \
|
||||
(compile_ffs2((__x))), \
|
||||
(compile_ffs2((__x) >> 2) + 2))
|
||||
|
||||
#define compile_ffs8(__x) \
|
||||
__builtin_choose_expr(((__x) & 0xf), \
|
||||
(compile_ffs4((__x))), \
|
||||
(compile_ffs4((__x) >> 4) + 4))
|
||||
|
||||
#define compile_ffs16(__x) \
|
||||
__builtin_choose_expr(((__x) & 0xff), \
|
||||
(compile_ffs8((__x))), \
|
||||
(compile_ffs8((__x) >> 8) + 8))
|
||||
|
||||
#define compile_ffs32(__x) \
|
||||
__builtin_choose_expr(((__x) & 0xffff), \
|
||||
(compile_ffs16((__x))), \
|
||||
(compile_ffs16((__x) >> 16) + 16))
|
||||
|
||||
|
||||
/*
|
||||
* This macro will check the requirements for the FIELD{8,16,32} macros
|
||||
* The mask should be a constant non-zero contiguous set of bits which
|
||||
* does not exceed the given typelimit.
|
||||
*/
|
||||
#define FIELD_CHECK(__mask) \
|
||||
BUILD_BUG_ON(!(__mask) || !is_valid_mask(__mask))
|
||||
|
||||
#define MT76_SET(_mask, _val) \
|
||||
({ FIELD_CHECK(_mask); ((_val) << compile_ffs32(_mask)) & _mask; })
|
||||
|
||||
#define MT76_GET(_mask, _val) \
|
||||
({ FIELD_CHECK(_mask); ((_val) & _mask) >> compile_ffs32(_mask); })
|
||||
|
||||
#define MT76_INCR(_var, _size) \
|
||||
_var = (((_var) + 1) % _size)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user