mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
mt76: add driver code for MT76x2e
MT76x2e is a 2x2 PCIe 802.11ac chipset by MediaTek. This driver has full support for AP, station, ad-hoc, mesh and monitor mode. Signed-off-by: Felix Fietkau <nbd@nbd.name> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
committed by
Kalle Valo
parent
17f1de56df
commit
7bc04215a6
@@ -11,4 +11,5 @@ config WLAN_VENDOR_MEDIATEK
|
||||
|
||||
if WLAN_VENDOR_MEDIATEK
|
||||
source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
|
||||
source "drivers/net/wireless/mediatek/mt76/Kconfig"
|
||||
endif # WLAN_VENDOR_MEDIATEK
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
obj-$(CONFIG_MT7601U) += mt7601u/
|
||||
obj-$(CONFIG_MT76_CORE) += mt76/
|
||||
|
||||
10
drivers/net/wireless/mediatek/mt76/Kconfig
Normal file
10
drivers/net/wireless/mediatek/mt76/Kconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
config MT76_CORE
|
||||
tristate
|
||||
|
||||
config MT76x2E
|
||||
tristate "MediaTek MT76x2E (PCIe) support"
|
||||
select MT76_CORE
|
||||
depends on MAC80211
|
||||
depends on PCI
|
||||
---help---
|
||||
This adds support for MT7612/MT7602/MT7662-based wireless PCIe devices.
|
||||
15
drivers/net/wireless/mediatek/mt76/Makefile
Normal file
15
drivers/net/wireless/mediatek/mt76/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
obj-$(CONFIG_MT76_CORE) += mt76.o
|
||||
obj-$(CONFIG_MT76x2E) += mt76x2e.o
|
||||
|
||||
mt76-y := \
|
||||
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o
|
||||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
mt76x2e-y := \
|
||||
mt76x2_pci.o mt76x2_dma.o \
|
||||
mt76x2_main.o mt76x2_init.o mt76x2_debugfs.o mt76x2_tx.o \
|
||||
mt76x2_core.o mt76x2_mac.o mt76x2_eeprom.o mt76x2_mcu.o mt76x2_phy.o \
|
||||
mt76x2_dfs.o mt76x2_trace.o
|
||||
|
||||
CFLAGS_mt76x2_trace.o := -I$(src)
|
||||
227
drivers/net/wireless/mediatek/mt76/mt76x2.h
Normal file
227
drivers/net/wireless/mediatek/mt76/mt76x2.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_H
|
||||
#define __MT76x2_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
#define MT7662_FIRMWARE "mt7662.bin"
|
||||
#define MT7662_ROM_PATCH "mt7662_rom_patch.bin"
|
||||
#define MT7662_EEPROM_SIZE 512
|
||||
|
||||
#define MT76x2_RX_RING_SIZE 256
|
||||
#define MT_RX_HEADROOM 32
|
||||
|
||||
#define MT_MAX_CHAINS 2
|
||||
|
||||
#define MT_CALIBRATE_INTERVAL HZ
|
||||
|
||||
#include "mt76.h"
|
||||
#include "mt76x2_regs.h"
|
||||
#include "mt76x2_mac.h"
|
||||
#include "mt76x2_dfs.h"
|
||||
|
||||
struct mt76x2_mcu {
|
||||
struct mutex mutex;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
struct sk_buff_head res_q;
|
||||
|
||||
u32 msg_seq;
|
||||
};
|
||||
|
||||
struct mt76x2_rx_freq_cal {
|
||||
s8 high_gain[MT_MAX_CHAINS];
|
||||
s8 rssi_offset[MT_MAX_CHAINS];
|
||||
s8 lna_gain;
|
||||
u32 mcu_gain;
|
||||
};
|
||||
|
||||
struct mt76x2_calibration {
|
||||
struct mt76x2_rx_freq_cal rx;
|
||||
|
||||
u8 agc_gain_init[MT_MAX_CHAINS];
|
||||
u8 agc_gain_cur[MT_MAX_CHAINS];
|
||||
|
||||
int avg_rssi[MT_MAX_CHAINS];
|
||||
int avg_rssi_all;
|
||||
|
||||
s8 agc_gain_adjust;
|
||||
s8 low_gain;
|
||||
|
||||
u8 temp;
|
||||
|
||||
bool init_cal_done;
|
||||
bool tssi_cal_done;
|
||||
bool tssi_comp_pending;
|
||||
bool dpd_cal_done;
|
||||
bool channel_cal_done;
|
||||
};
|
||||
|
||||
struct mt76x2_dev {
|
||||
struct mt76_dev mt76; /* must be first */
|
||||
|
||||
struct mac_address macaddr_list[8];
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
const u16 *beacon_offsets;
|
||||
unsigned long wcid_mask[128 / BITS_PER_LONG];
|
||||
|
||||
int txpower_conf;
|
||||
int txpower_cur;
|
||||
|
||||
u8 txdone_seq;
|
||||
DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x2_tx_status);
|
||||
|
||||
struct mt76x2_mcu mcu;
|
||||
struct sk_buff *rx_head;
|
||||
|
||||
struct tasklet_struct tx_tasklet;
|
||||
struct tasklet_struct pre_tbtt_tasklet;
|
||||
struct delayed_work cal_work;
|
||||
struct delayed_work mac_work;
|
||||
|
||||
u32 aggr_stats[32];
|
||||
|
||||
struct mt76_wcid global_wcid;
|
||||
struct mt76_wcid __rcu *wcid[128];
|
||||
|
||||
spinlock_t irq_lock;
|
||||
u32 irqmask;
|
||||
|
||||
struct sk_buff *beacons[8];
|
||||
u8 beacon_mask;
|
||||
u8 beacon_data_mask;
|
||||
|
||||
u32 rev;
|
||||
u32 rxfilter;
|
||||
|
||||
u16 chainmask;
|
||||
|
||||
struct mt76x2_calibration cal;
|
||||
|
||||
s8 target_power;
|
||||
s8 target_power_delta[2];
|
||||
struct mt76_rate_power rate_power;
|
||||
bool enable_tpc;
|
||||
|
||||
u8 coverage_class;
|
||||
u8 slottime;
|
||||
|
||||
struct mt76x2_dfs_pattern_detector dfs_pd;
|
||||
};
|
||||
|
||||
struct mt76x2_vif {
|
||||
u8 idx;
|
||||
|
||||
struct mt76_wcid group_wcid;
|
||||
};
|
||||
|
||||
struct mt76x2_sta {
|
||||
struct mt76_wcid wcid; /* must be first */
|
||||
|
||||
struct mt76x2_tx_status status;
|
||||
int n_frames;
|
||||
};
|
||||
|
||||
static inline bool is_mt7612(struct mt76x2_dev *dev)
|
||||
{
|
||||
return (dev->rev >> 16) == 0x7612;
|
||||
}
|
||||
|
||||
void mt76x2_set_irq_mask(struct mt76x2_dev *dev, u32 clear, u32 set);
|
||||
|
||||
static inline void mt76x2_irq_enable(struct mt76x2_dev *dev, u32 mask)
|
||||
{
|
||||
mt76x2_set_irq_mask(dev, 0, mask);
|
||||
}
|
||||
|
||||
static inline void mt76x2_irq_disable(struct mt76x2_dev *dev, u32 mask)
|
||||
{
|
||||
mt76x2_set_irq_mask(dev, mask, 0);
|
||||
}
|
||||
|
||||
extern const struct ieee80211_ops mt76x2_ops;
|
||||
|
||||
struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev);
|
||||
int mt76x2_register_device(struct mt76x2_dev *dev);
|
||||
void mt76x2_init_debugfs(struct mt76x2_dev *dev);
|
||||
|
||||
irqreturn_t mt76x2_irq_handler(int irq, void *dev_instance);
|
||||
void mt76x2_phy_power_on(struct mt76x2_dev *dev);
|
||||
int mt76x2_init_hardware(struct mt76x2_dev *dev);
|
||||
void mt76x2_stop_hardware(struct mt76x2_dev *dev);
|
||||
int mt76x2_eeprom_init(struct mt76x2_dev *dev);
|
||||
int mt76x2_apply_calibration_data(struct mt76x2_dev *dev, int channel);
|
||||
void mt76x2_set_tx_ackto(struct mt76x2_dev *dev);
|
||||
|
||||
int mt76x2_phy_start(struct mt76x2_dev *dev);
|
||||
int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
int mt76x2_phy_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain);
|
||||
void mt76x2_phy_calibrate(struct work_struct *work);
|
||||
void mt76x2_phy_set_txpower(struct mt76x2_dev *dev);
|
||||
|
||||
int mt76x2_mcu_init(struct mt76x2_dev *dev);
|
||||
int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw,
|
||||
u8 bw_index, bool scan);
|
||||
int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on);
|
||||
int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level,
|
||||
u8 channel);
|
||||
int mt76x2_mcu_cleanup(struct mt76x2_dev *dev);
|
||||
|
||||
int mt76x2_dma_init(struct mt76x2_dev *dev);
|
||||
void mt76x2_dma_cleanup(struct mt76x2_dev *dev);
|
||||
|
||||
void mt76x2_cleanup(struct mt76x2_dev *dev);
|
||||
|
||||
int mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid,
|
||||
struct sk_buff *skb, int cmd, int seq);
|
||||
void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
|
||||
struct sk_buff *skb);
|
||||
void mt76x2_tx_complete(struct mt76x2_dev *dev, struct sk_buff *skb);
|
||||
int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
|
||||
struct sk_buff *skb, struct mt76_queue *q,
|
||||
struct mt76_wcid *wcid, struct ieee80211_sta *sta,
|
||||
u32 *tx_info);
|
||||
void mt76x2_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
|
||||
struct mt76_queue_entry *e, bool flush);
|
||||
|
||||
void mt76x2_pre_tbtt_tasklet(unsigned long arg);
|
||||
|
||||
void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
|
||||
void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
|
||||
struct sk_buff *skb);
|
||||
|
||||
void mt76x2_update_channel(struct mt76_dev *mdev);
|
||||
|
||||
s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev,
|
||||
const struct ieee80211_tx_rate *rate);
|
||||
s8 mt76x2_tx_get_txpwr_adj(struct mt76x2_dev *dev, s8 txpwr, s8 max_txpwr_adj);
|
||||
void mt76x2_tx_set_txpwr_auto(struct mt76x2_dev *dev, s8 txpwr);
|
||||
|
||||
#endif
|
||||
88
drivers/net/wireless/mediatek/mt76/mt76x2_core.c
Normal file
88
drivers/net/wireless/mediatek/mt76/mt76x2_core.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "mt76x2.h"
|
||||
#include "mt76x2_trace.h"
|
||||
|
||||
void mt76x2_set_irq_mask(struct mt76x2_dev *dev, u32 clear, u32 set)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->irq_lock, flags);
|
||||
dev->irqmask &= ~clear;
|
||||
dev->irqmask |= set;
|
||||
mt76_wr(dev, MT_INT_MASK_CSR, dev->irqmask);
|
||||
spin_unlock_irqrestore(&dev->irq_lock, flags);
|
||||
}
|
||||
|
||||
void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q)
|
||||
{
|
||||
struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
|
||||
|
||||
mt76x2_irq_enable(dev, MT_INT_RX_DONE(q));
|
||||
}
|
||||
|
||||
irqreturn_t mt76x2_irq_handler(int irq, void *dev_instance)
|
||||
{
|
||||
struct mt76x2_dev *dev = dev_instance;
|
||||
u32 intr;
|
||||
|
||||
intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
|
||||
mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
|
||||
|
||||
if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.state))
|
||||
return IRQ_NONE;
|
||||
|
||||
trace_dev_irq(dev, intr, dev->irqmask);
|
||||
|
||||
intr &= dev->irqmask;
|
||||
|
||||
if (intr & MT_INT_TX_DONE_ALL) {
|
||||
mt76x2_irq_disable(dev, MT_INT_TX_DONE_ALL);
|
||||
tasklet_schedule(&dev->tx_tasklet);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_RX_DONE(0)) {
|
||||
mt76x2_irq_disable(dev, MT_INT_RX_DONE(0));
|
||||
napi_schedule(&dev->mt76.napi[0]);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_RX_DONE(1)) {
|
||||
mt76x2_irq_disable(dev, MT_INT_RX_DONE(1));
|
||||
napi_schedule(&dev->mt76.napi[1]);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_PRE_TBTT)
|
||||
tasklet_schedule(&dev->pre_tbtt_tasklet);
|
||||
|
||||
/* send buffered multicast frames now */
|
||||
if (intr & MT_INT_TBTT)
|
||||
mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]);
|
||||
|
||||
if (intr & MT_INT_TX_STAT) {
|
||||
mt76x2_mac_poll_tx_status(dev, true);
|
||||
tasklet_schedule(&dev->tx_tasklet);
|
||||
}
|
||||
|
||||
if (intr & MT_INT_GPTIMER) {
|
||||
mt76x2_irq_disable(dev, MT_INT_GPTIMER);
|
||||
tasklet_schedule(&dev->dfs_pd.dfs_tasklet);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
133
drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
Normal file
133
drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include "mt76x2.h"
|
||||
|
||||
static int
|
||||
mt76x2_ampdu_stat_read(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mt76x2_dev *dev = file->private;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
seq_puts(file, "Length: ");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_printf(file, "%8d | ", i * 8 + j + 1);
|
||||
seq_puts(file, "\n");
|
||||
seq_puts(file, "Count: ");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_printf(file, "%8d | ", dev->aggr_stats[i * 8 + j]);
|
||||
seq_puts(file, "\n");
|
||||
seq_puts(file, "--------");
|
||||
for (j = 0; j < 8; j++)
|
||||
seq_puts(file, "-----------");
|
||||
seq_puts(file, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_ampdu_stat_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
return single_open(f, mt76x2_ampdu_stat_read, inode->i_private);
|
||||
}
|
||||
|
||||
static void
|
||||
seq_puts_array(struct seq_file *file, const char *str, s8 *val, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_printf(file, "%10s:", str);
|
||||
for (i = 0; i < len; i++)
|
||||
seq_printf(file, " %2d", val[i]);
|
||||
seq_puts(file, "\n");
|
||||
}
|
||||
|
||||
static int read_txpower(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mt76x2_dev *dev = dev_get_drvdata(file->private);
|
||||
|
||||
seq_printf(file, "Target power: %d\n", dev->target_power);
|
||||
|
||||
seq_puts_array(file, "Delta", dev->target_power_delta,
|
||||
ARRAY_SIZE(dev->target_power_delta));
|
||||
seq_puts_array(file, "CCK", dev->rate_power.cck,
|
||||
ARRAY_SIZE(dev->rate_power.cck));
|
||||
seq_puts_array(file, "OFDM", dev->rate_power.ofdm,
|
||||
ARRAY_SIZE(dev->rate_power.ofdm));
|
||||
seq_puts_array(file, "HT", dev->rate_power.ht,
|
||||
ARRAY_SIZE(dev->rate_power.ht));
|
||||
seq_puts_array(file, "VHT", dev->rate_power.vht,
|
||||
ARRAY_SIZE(dev->rate_power.vht));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_ampdu_stat = {
|
||||
.open = mt76x2_ampdu_stat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int
|
||||
mt76x2_dfs_stat_read(struct seq_file *file, void *data)
|
||||
{
|
||||
int i;
|
||||
struct mt76x2_dev *dev = file->private;
|
||||
struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
|
||||
|
||||
for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
|
||||
seq_printf(file, "engine: %d\n", i);
|
||||
seq_printf(file, " hw pattern detected:\t%d\n",
|
||||
dfs_pd->stats[i].hw_pattern);
|
||||
seq_printf(file, " hw pulse discarded:\t%d\n",
|
||||
dfs_pd->stats[i].hw_pulse_discarded);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_dfs_stat_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
return single_open(f, mt76x2_dfs_stat_read, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_dfs_stat = {
|
||||
.open = mt76x2_dfs_stat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void mt76x2_init_debugfs(struct mt76x2_dev *dev)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = mt76_register_debugfs(&dev->mt76);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
debugfs_create_u8("temperature", S_IRUSR, dir, &dev->cal.temp);
|
||||
debugfs_create_bool("tpc", S_IRUSR | S_IWUSR, dir, &dev->enable_tpc);
|
||||
|
||||
debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
|
||||
debugfs_create_file("dfs_stats", S_IRUSR, dir, dev, &fops_dfs_stat);
|
||||
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
|
||||
read_txpower);
|
||||
}
|
||||
493
drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c
Normal file
493
drivers/net/wireless/mediatek/mt76/mt76x2_dfs.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mt76x2.h"
|
||||
|
||||
#define RADAR_SPEC(m, len, el, eh, wl, wh, \
|
||||
w_tolerance, tl, th, t_tolerance, \
|
||||
bl, bh, event_exp, power_jmp) \
|
||||
{ \
|
||||
.mode = m, \
|
||||
.avg_len = len, \
|
||||
.e_low = el, \
|
||||
.e_high = eh, \
|
||||
.w_low = wl, \
|
||||
.w_high = wh, \
|
||||
.w_margin = w_tolerance, \
|
||||
.t_low = tl, \
|
||||
.t_high = th, \
|
||||
.t_margin = t_tolerance, \
|
||||
.b_low = bl, \
|
||||
.b_high = bh, \
|
||||
.event_expiration = event_exp, \
|
||||
.pwr_jmp = power_jmp \
|
||||
}
|
||||
|
||||
static const struct mt76x2_radar_specs etsi_radar_specs[] = {
|
||||
/* 20MHz */
|
||||
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19dd),
|
||||
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
|
||||
0x7fffffff, 0x2191c0, 0x15cc),
|
||||
/* 40MHz */
|
||||
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19dd),
|
||||
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
|
||||
0x7fffffff, 0x2191c0, 0x15cc),
|
||||
/* 80MHz */
|
||||
RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19cc),
|
||||
RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0,
|
||||
0x7fffffff, 0x155cc0, 0x19dd),
|
||||
RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0,
|
||||
0x7fffffff, 0x2191c0, 0x15cc)
|
||||
};
|
||||
|
||||
static const struct mt76x2_radar_specs fcc_radar_specs[] = {
|
||||
/* 20MHz */
|
||||
RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
|
||||
0x7fffffff, 0xfe808, 0x13dc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0xfe808, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0xfe808, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0x57bcf00, 0x1289),
|
||||
/* 40MHz */
|
||||
RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0,
|
||||
0x7fffffff, 0xfe808, 0x13dc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0xfe808, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0xfe808, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0x57bcf00, 0x1289),
|
||||
/* 80MHz */
|
||||
RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0,
|
||||
0x7fffffff, 0xfe808, 0x16cc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0xfe808, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0xfe808, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0x57bcf00, 0x1289)
|
||||
};
|
||||
|
||||
static const struct mt76x2_radar_specs jp_w56_radar_specs[] = {
|
||||
/* 20MHz */
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
|
||||
0x7fffffff, 0x14c080, 0x13dc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0x14c080, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0x14c080, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0X57bcf00, 0x1289),
|
||||
/* 40MHz */
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0,
|
||||
0x7fffffff, 0x14c080, 0x13dc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0x14c080, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0x14c080, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0X57bcf00, 0x1289),
|
||||
/* 80MHz */
|
||||
RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0,
|
||||
0x7fffffff, 0x14c080, 0x19dd),
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0,
|
||||
0x7fffffff, 0x14c080, 0x12cc),
|
||||
RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0,
|
||||
0x3938700, 0X57bcf00, 0x1289)
|
||||
};
|
||||
|
||||
static const struct mt76x2_radar_specs jp_w53_radar_specs[] = {
|
||||
/* 20MHz */
|
||||
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 },
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 },
|
||||
/* 40MHz */
|
||||
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 },
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 },
|
||||
/* 80MHz */
|
||||
RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 },
|
||||
RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0,
|
||||
0x7fffffff, 0x14c080, 0x16cc),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void mt76x2_dfs_set_capture_mode_ctrl(struct mt76x2_dev *dev,
|
||||
u8 enable)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
data = (1 << 1) | enable;
|
||||
mt76_wr(dev, MT_BBP(DFS, 36), data);
|
||||
}
|
||||
|
||||
static bool mt76x2_dfs_check_chirp(struct mt76x2_dev *dev)
|
||||
{
|
||||
bool ret = false;
|
||||
u32 current_ts, delta_ts;
|
||||
struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
|
||||
|
||||
current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER);
|
||||
delta_ts = current_ts - dfs_pd->chirp_pulse_ts;
|
||||
dfs_pd->chirp_pulse_ts = current_ts;
|
||||
|
||||
/* 12 sec */
|
||||
if (delta_ts <= (12 * (1 << 20))) {
|
||||
if (++dfs_pd->chirp_pulse_cnt > 8)
|
||||
ret = true;
|
||||
} else {
|
||||
dfs_pd->chirp_pulse_cnt = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt76x2_dfs_get_hw_pulse(struct mt76x2_dev *dev,
|
||||
struct mt76x2_dfs_hw_pulse *pulse)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
/* select channel */
|
||||
data = (MT_DFS_CH_EN << 16) | pulse->engine;
|
||||
mt76_wr(dev, MT_BBP(DFS, 0), data);
|
||||
|
||||
/* reported period */
|
||||
pulse->period = mt76_rr(dev, MT_BBP(DFS, 19));
|
||||
|
||||
/* reported width */
|
||||
pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20));
|
||||
pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23));
|
||||
|
||||
/* reported burst number */
|
||||
pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22));
|
||||
}
|
||||
|
||||
static bool mt76x2_dfs_check_hw_pulse(struct mt76x2_dev *dev,
|
||||
struct mt76x2_dfs_hw_pulse *pulse)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (!pulse->period || !pulse->w1)
|
||||
return false;
|
||||
|
||||
switch (dev->dfs_pd.region) {
|
||||
case NL80211_DFS_FCC:
|
||||
if (pulse->engine > 3)
|
||||
break;
|
||||
|
||||
if (pulse->engine == 3) {
|
||||
ret = mt76x2_dfs_check_chirp(dev);
|
||||
break;
|
||||
}
|
||||
|
||||
/* check short pulse*/
|
||||
if (pulse->w1 < 120)
|
||||
ret = (pulse->period >= 2900 &&
|
||||
(pulse->period <= 4700 ||
|
||||
pulse->period >= 6400) &&
|
||||
(pulse->period <= 6800 ||
|
||||
pulse->period >= 10200) &&
|
||||
pulse->period <= 61600);
|
||||
else if (pulse->w1 < 130) /* 120 - 130 */
|
||||
ret = (pulse->period >= 2900 &&
|
||||
pulse->period <= 61600);
|
||||
else
|
||||
ret = (pulse->period >= 3500 &&
|
||||
pulse->period <= 10100);
|
||||
break;
|
||||
case NL80211_DFS_ETSI:
|
||||
if (pulse->engine >= 3)
|
||||
break;
|
||||
|
||||
ret = (pulse->period >= 4900 &&
|
||||
(pulse->period <= 10200 ||
|
||||
pulse->period >= 12400) &&
|
||||
pulse->period <= 100100);
|
||||
break;
|
||||
case NL80211_DFS_JP:
|
||||
if (dev->mt76.chandef.chan->center_freq >= 5250 &&
|
||||
dev->mt76.chandef.chan->center_freq <= 5350) {
|
||||
/* JPW53 */
|
||||
if (pulse->w1 <= 130)
|
||||
ret = (pulse->period >= 28360 &&
|
||||
(pulse->period <= 28700 ||
|
||||
pulse->period >= 76900) &&
|
||||
pulse->period <= 76940);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pulse->engine > 3)
|
||||
break;
|
||||
|
||||
if (pulse->engine == 3) {
|
||||
ret = mt76x2_dfs_check_chirp(dev);
|
||||
break;
|
||||
}
|
||||
|
||||
/* check short pulse*/
|
||||
if (pulse->w1 < 120)
|
||||
ret = (pulse->period >= 2900 &&
|
||||
(pulse->period <= 4700 ||
|
||||
pulse->period >= 6400) &&
|
||||
(pulse->period <= 6800 ||
|
||||
pulse->period >= 27560) &&
|
||||
(pulse->period <= 27960 ||
|
||||
pulse->period >= 28360) &&
|
||||
(pulse->period <= 28700 ||
|
||||
pulse->period >= 79900) &&
|
||||
pulse->period <= 80100);
|
||||
else if (pulse->w1 < 130) /* 120 - 130 */
|
||||
ret = (pulse->period >= 2900 &&
|
||||
(pulse->period <= 10100 ||
|
||||
pulse->period >= 27560) &&
|
||||
(pulse->period <= 27960 ||
|
||||
pulse->period >= 28360) &&
|
||||
(pulse->period <= 28700 ||
|
||||
pulse->period >= 79900) &&
|
||||
pulse->period <= 80100);
|
||||
else
|
||||
ret = (pulse->period >= 3900 &&
|
||||
pulse->period <= 10100);
|
||||
break;
|
||||
case NL80211_DFS_UNSET:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt76x2_dfs_tasklet(unsigned long arg)
|
||||
{
|
||||
struct mt76x2_dev *dev = (struct mt76x2_dev *)arg;
|
||||
struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
|
||||
u32 engine_mask;
|
||||
int i;
|
||||
|
||||
if (test_bit(MT76_SCANNING, &dev->mt76.state))
|
||||
goto out;
|
||||
|
||||
engine_mask = mt76_rr(dev, MT_BBP(DFS, 1));
|
||||
if (!(engine_mask & 0xf))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
|
||||
struct mt76x2_dfs_hw_pulse pulse;
|
||||
|
||||
if (!(engine_mask & (1 << i)))
|
||||
continue;
|
||||
|
||||
pulse.engine = i;
|
||||
mt76x2_dfs_get_hw_pulse(dev, &pulse);
|
||||
|
||||
if (!mt76x2_dfs_check_hw_pulse(dev, &pulse)) {
|
||||
dfs_pd->stats[i].hw_pulse_discarded++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* hw detector rx radar pattern */
|
||||
dfs_pd->stats[i].hw_pattern++;
|
||||
ieee80211_radar_detected(dev->mt76.hw);
|
||||
|
||||
/* reset hw detector */
|
||||
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* reset hw detector */
|
||||
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
|
||||
|
||||
out:
|
||||
mt76x2_irq_enable(dev, MT_INT_GPTIMER);
|
||||
}
|
||||
|
||||
static void mt76x2_dfs_set_bbp_params(struct mt76x2_dev *dev)
|
||||
{
|
||||
u32 data;
|
||||
u8 i, shift;
|
||||
const struct mt76x2_radar_specs *radar_specs;
|
||||
|
||||
switch (dev->mt76.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
shift = MT_DFS_NUM_ENGINES;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
shift = 2 * MT_DFS_NUM_ENGINES;
|
||||
break;
|
||||
default:
|
||||
shift = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (dev->dfs_pd.region) {
|
||||
case NL80211_DFS_FCC:
|
||||
radar_specs = &fcc_radar_specs[shift];
|
||||
break;
|
||||
case NL80211_DFS_ETSI:
|
||||
radar_specs = &etsi_radar_specs[shift];
|
||||
break;
|
||||
case NL80211_DFS_JP:
|
||||
if (dev->mt76.chandef.chan->center_freq >= 5250 &&
|
||||
dev->mt76.chandef.chan->center_freq <= 5350)
|
||||
radar_specs = &jp_w53_radar_specs[shift];
|
||||
else
|
||||
radar_specs = &jp_w56_radar_specs[shift];
|
||||
break;
|
||||
case NL80211_DFS_UNSET:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
data = (MT_DFS_VGA_MASK << 16) |
|
||||
(MT_DFS_PWR_GAIN_OFFSET << 12) |
|
||||
(MT_DFS_PWR_DOWN_TIME << 8) |
|
||||
(MT_DFS_SYM_ROUND << 4) |
|
||||
(MT_DFS_DELTA_DELAY & 0xf);
|
||||
mt76_wr(dev, MT_BBP(DFS, 2), data);
|
||||
|
||||
data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK;
|
||||
mt76_wr(dev, MT_BBP(DFS, 3), data);
|
||||
|
||||
for (i = 0; i < MT_DFS_NUM_ENGINES; i++) {
|
||||
/* configure engine */
|
||||
mt76_wr(dev, MT_BBP(DFS, 0), i);
|
||||
|
||||
/* detection mode + avg_len */
|
||||
data = ((radar_specs[i].avg_len & 0x1ff) << 16) |
|
||||
(radar_specs[i].mode & 0xf);
|
||||
mt76_wr(dev, MT_BBP(DFS, 4), data);
|
||||
|
||||
/* dfs energy */
|
||||
data = ((radar_specs[i].e_high & 0x0fff) << 16) |
|
||||
(radar_specs[i].e_low & 0x0fff);
|
||||
mt76_wr(dev, MT_BBP(DFS, 5), data);
|
||||
|
||||
/* dfs period */
|
||||
mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low);
|
||||
mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high);
|
||||
|
||||
/* dfs burst */
|
||||
mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low);
|
||||
mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high);
|
||||
|
||||
/* dfs width */
|
||||
data = ((radar_specs[i].w_high & 0x0fff) << 16) |
|
||||
(radar_specs[i].w_low & 0x0fff);
|
||||
mt76_wr(dev, MT_BBP(DFS, 14), data);
|
||||
|
||||
/* dfs margins */
|
||||
data = (radar_specs[i].w_margin << 16) |
|
||||
radar_specs[i].t_margin;
|
||||
mt76_wr(dev, MT_BBP(DFS, 15), data);
|
||||
|
||||
/* dfs event expiration */
|
||||
mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration);
|
||||
|
||||
/* dfs pwr adj */
|
||||
mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp);
|
||||
}
|
||||
|
||||
/* reset status */
|
||||
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
|
||||
mt76_wr(dev, MT_BBP(DFS, 36), 0x3);
|
||||
|
||||
/* enable detection*/
|
||||
mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16);
|
||||
mt76_wr(dev, 0x212c, 0x0c350001);
|
||||
}
|
||||
|
||||
void mt76x2_dfs_adjust_agc(struct mt76x2_dev *dev)
|
||||
{
|
||||
u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31;
|
||||
|
||||
agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8));
|
||||
agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4));
|
||||
|
||||
val_r8 = (agc_r8 & 0x00007e00) >> 9;
|
||||
val_r4 = agc_r4 & ~0x1f000000;
|
||||
val_r4 += (((val_r8 + 1) >> 1) << 24);
|
||||
mt76_wr(dev, MT_BBP(AGC, 4), val_r4);
|
||||
|
||||
dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4);
|
||||
dfs_r31 += val_r8;
|
||||
dfs_r31 -= (agc_r8 & 0x00000038) >> 3;
|
||||
dfs_r31 = (dfs_r31 << 16) | 0x00000307;
|
||||
mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31);
|
||||
|
||||
mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071);
|
||||
}
|
||||
|
||||
void mt76x2_dfs_init_params(struct mt76x2_dev *dev)
|
||||
{
|
||||
struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
|
||||
|
||||
tasklet_kill(&dev->dfs_pd.dfs_tasklet);
|
||||
if (chandef->chan->flags & IEEE80211_CHAN_RADAR) {
|
||||
mt76x2_dfs_set_bbp_params(dev);
|
||||
/* enable debug mode */
|
||||
mt76x2_dfs_set_capture_mode_ctrl(dev, true);
|
||||
|
||||
mt76x2_irq_enable(dev, MT_INT_GPTIMER);
|
||||
mt76_rmw_field(dev, MT_INT_TIMER_EN,
|
||||
MT_INT_TIMER_EN_GP_TIMER_EN, 1);
|
||||
} else {
|
||||
/* disable hw detector */
|
||||
mt76_wr(dev, MT_BBP(DFS, 0), 0);
|
||||
/* clear detector status */
|
||||
mt76_wr(dev, MT_BBP(DFS, 1), 0xf);
|
||||
mt76_wr(dev, 0x212c, 0);
|
||||
|
||||
mt76x2_irq_disable(dev, MT_INT_GPTIMER);
|
||||
mt76_rmw_field(dev, MT_INT_TIMER_EN,
|
||||
MT_INT_TIMER_EN_GP_TIMER_EN, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void mt76x2_dfs_init_detector(struct mt76x2_dev *dev)
|
||||
{
|
||||
struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
|
||||
|
||||
dfs_pd->region = NL80211_DFS_UNSET;
|
||||
tasklet_init(&dfs_pd->dfs_tasklet, mt76x2_dfs_tasklet,
|
||||
(unsigned long)dev);
|
||||
}
|
||||
|
||||
80
drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h
Normal file
80
drivers/net/wireless/mediatek/mt76/mt76x2_dfs.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_DFS_H
|
||||
#define __MT76x2_DFS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#define MT_DFS_GP_INTERVAL (10 << 4) /* 64 us unit */
|
||||
#define MT_DFS_NUM_ENGINES 4
|
||||
|
||||
/* bbp params */
|
||||
#define MT_DFS_SYM_ROUND 0
|
||||
#define MT_DFS_DELTA_DELAY 2
|
||||
#define MT_DFS_VGA_MASK 0
|
||||
#define MT_DFS_PWR_GAIN_OFFSET 3
|
||||
#define MT_DFS_PWR_DOWN_TIME 0xf
|
||||
#define MT_DFS_RX_PE_MASK 0xff
|
||||
#define MT_DFS_PKT_END_MASK 0
|
||||
#define MT_DFS_CH_EN 0xf
|
||||
|
||||
struct mt76x2_radar_specs {
|
||||
u8 mode;
|
||||
u16 avg_len;
|
||||
u16 e_low;
|
||||
u16 e_high;
|
||||
u16 w_low;
|
||||
u16 w_high;
|
||||
u16 w_margin;
|
||||
u32 t_low;
|
||||
u32 t_high;
|
||||
u16 t_margin;
|
||||
u32 b_low;
|
||||
u32 b_high;
|
||||
u32 event_expiration;
|
||||
u16 pwr_jmp;
|
||||
};
|
||||
|
||||
struct mt76x2_dfs_hw_pulse {
|
||||
u8 engine;
|
||||
u32 period;
|
||||
u32 w1;
|
||||
u32 w2;
|
||||
u32 burst;
|
||||
};
|
||||
|
||||
struct mt76x2_dfs_engine_stats {
|
||||
u32 hw_pattern;
|
||||
u32 hw_pulse_discarded;
|
||||
};
|
||||
|
||||
struct mt76x2_dfs_pattern_detector {
|
||||
enum nl80211_dfs_regions region;
|
||||
|
||||
u8 chirp_pulse_cnt;
|
||||
u32 chirp_pulse_ts;
|
||||
|
||||
struct mt76x2_dfs_engine_stats stats[MT_DFS_NUM_ENGINES];
|
||||
struct tasklet_struct dfs_tasklet;
|
||||
};
|
||||
|
||||
void mt76x2_dfs_init_params(struct mt76x2_dev *dev);
|
||||
void mt76x2_dfs_init_detector(struct mt76x2_dev *dev);
|
||||
void mt76x2_dfs_adjust_agc(struct mt76x2_dev *dev);
|
||||
|
||||
#endif /* __MT76x2_DFS_H */
|
||||
183
drivers/net/wireless/mediatek/mt76/mt76x2_dma.c
Normal file
183
drivers/net/wireless/mediatek/mt76/mt76x2_dma.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mt76x2.h"
|
||||
#include "mt76x2_dma.h"
|
||||
|
||||
int
|
||||
mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid,
|
||||
struct sk_buff *skb, int cmd, int seq)
|
||||
{
|
||||
struct mt76_queue *q = &dev->mt76.q_tx[qid];
|
||||
struct mt76_queue_buf buf;
|
||||
dma_addr_t addr;
|
||||
u32 tx_info;
|
||||
|
||||
tx_info = MT_MCU_MSG_TYPE_CMD |
|
||||
FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) |
|
||||
FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) |
|
||||
FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) |
|
||||
FIELD_PREP(MT_MCU_MSG_LEN, skb->len);
|
||||
|
||||
addr = dma_map_single(dev->mt76.dev, skb->data, skb->len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev->mt76.dev, addr))
|
||||
return -ENOMEM;
|
||||
|
||||
buf.addr = addr;
|
||||
buf.len = skb->len;
|
||||
spin_lock_bh(&q->lock);
|
||||
mt76_queue_add_buf(dev, q, &buf, 1, tx_info, skb, NULL);
|
||||
mt76_queue_kick(dev, q);
|
||||
spin_unlock_bh(&q->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_init_tx_queue(struct mt76x2_dev *dev, struct mt76_queue *q,
|
||||
int idx, int n_desc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
q->regs = dev->mt76.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE;
|
||||
q->ndesc = n_desc;
|
||||
|
||||
ret = mt76_queue_alloc(dev, q);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76x2_irq_enable(dev, MT_INT_TX_DONE(idx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
|
||||
void *rxwi = skb->data;
|
||||
|
||||
if (q == MT_RXQ_MCU) {
|
||||
skb_queue_tail(&dev->mcu.res_q, skb);
|
||||
wake_up(&dev->mcu.wait);
|
||||
return;
|
||||
}
|
||||
|
||||
skb_pull(skb, sizeof(struct mt76x2_rxwi));
|
||||
if (mt76x2_mac_process_rx(dev, skb, rxwi)) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
mt76_rx(&dev->mt76, q, skb);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_init_rx_queue(struct mt76x2_dev *dev, struct mt76_queue *q,
|
||||
int idx, int n_desc, int bufsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
q->regs = dev->mt76.regs + MT_RX_RING_BASE + idx * MT_RING_SIZE;
|
||||
q->ndesc = n_desc;
|
||||
q->buf_size = bufsize;
|
||||
|
||||
ret = mt76_queue_alloc(dev, q);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76x2_irq_enable(dev, MT_INT_RX_DONE(idx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76x2_tx_tasklet(unsigned long data)
|
||||
{
|
||||
struct mt76x2_dev *dev = (struct mt76x2_dev *) data;
|
||||
int i;
|
||||
|
||||
mt76x2_mac_process_tx_status_fifo(dev);
|
||||
|
||||
for (i = MT_TXQ_MCU; i >= 0; i--)
|
||||
mt76_queue_tx_cleanup(dev, i, false);
|
||||
|
||||
mt76x2_mac_poll_tx_status(dev, false);
|
||||
mt76x2_irq_enable(dev, MT_INT_TX_DONE_ALL);
|
||||
}
|
||||
|
||||
int mt76x2_dma_init(struct mt76x2_dev *dev)
|
||||
{
|
||||
static const u8 wmm_queue_map[] = {
|
||||
[IEEE80211_AC_BE] = 0,
|
||||
[IEEE80211_AC_BK] = 1,
|
||||
[IEEE80211_AC_VI] = 2,
|
||||
[IEEE80211_AC_VO] = 3,
|
||||
};
|
||||
int ret;
|
||||
int i;
|
||||
struct mt76_txwi_cache __maybe_unused *t;
|
||||
struct mt76_queue *q;
|
||||
|
||||
BUILD_BUG_ON(sizeof(t->txwi) < sizeof(struct mt76x2_txwi));
|
||||
BUILD_BUG_ON(sizeof(struct mt76x2_rxwi) > MT_RX_HEADROOM);
|
||||
|
||||
mt76_dma_attach(&dev->mt76);
|
||||
|
||||
init_waitqueue_head(&dev->mcu.wait);
|
||||
skb_queue_head_init(&dev->mcu.res_q);
|
||||
|
||||
tasklet_init(&dev->tx_tasklet, mt76x2_tx_tasklet, (unsigned long) dev);
|
||||
|
||||
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) {
|
||||
ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[i],
|
||||
wmm_queue_map[i], MT_TX_RING_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_PSD],
|
||||
MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_MCU],
|
||||
MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76x2_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1,
|
||||
MT_MCU_RING_SIZE, MT_RX_BUF_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
q = &dev->mt76.q_rx[MT_RXQ_MAIN];
|
||||
q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x2_rxwi);
|
||||
ret = mt76x2_init_rx_queue(dev, q, 0, MT76x2_RX_RING_SIZE, MT_RX_BUF_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mt76_init_queues(dev);
|
||||
}
|
||||
|
||||
void mt76x2_dma_cleanup(struct mt76x2_dev *dev)
|
||||
{
|
||||
tasklet_kill(&dev->tx_tasklet);
|
||||
mt76_dma_cleanup(&dev->mt76);
|
||||
}
|
||||
68
drivers/net/wireless/mediatek/mt76/mt76x2_dma.h
Normal file
68
drivers/net/wireless/mediatek/mt76/mt76x2_dma.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_DMA_H
|
||||
#define __MT76x2_DMA_H
|
||||
|
||||
#include "dma.h"
|
||||
|
||||
#define MT_TXD_INFO_LEN GENMASK(13, 0)
|
||||
#define MT_TXD_INFO_NEXT_VLD BIT(16)
|
||||
#define MT_TXD_INFO_TX_BURST BIT(17)
|
||||
#define MT_TXD_INFO_80211 BIT(19)
|
||||
#define MT_TXD_INFO_TSO BIT(20)
|
||||
#define MT_TXD_INFO_CSO BIT(21)
|
||||
#define MT_TXD_INFO_WIV BIT(24)
|
||||
#define MT_TXD_INFO_QSEL GENMASK(26, 25)
|
||||
#define MT_TXD_INFO_TCO BIT(29)
|
||||
#define MT_TXD_INFO_UCO BIT(30)
|
||||
#define MT_TXD_INFO_ICO BIT(31)
|
||||
|
||||
#define MT_RX_FCE_INFO_LEN GENMASK(13, 0)
|
||||
#define MT_RX_FCE_INFO_SELF_GEN BIT(15)
|
||||
#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16)
|
||||
#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20)
|
||||
#define MT_RX_FCE_INFO_PCIE_INTR BIT(24)
|
||||
#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25)
|
||||
#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27)
|
||||
#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30)
|
||||
|
||||
/* MCU request message header */
|
||||
#define MT_MCU_MSG_LEN GENMASK(15, 0)
|
||||
#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16)
|
||||
#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20)
|
||||
#define MT_MCU_MSG_PORT GENMASK(29, 27)
|
||||
#define MT_MCU_MSG_TYPE GENMASK(31, 30)
|
||||
#define MT_MCU_MSG_TYPE_CMD BIT(30)
|
||||
|
||||
enum mt76x2_qsel {
|
||||
MT_QSEL_MGMT,
|
||||
MT_QSEL_HCCA,
|
||||
MT_QSEL_EDCA,
|
||||
MT_QSEL_EDCA_2,
|
||||
};
|
||||
|
||||
enum dma_msg_port {
|
||||
WLAN_PORT,
|
||||
CPU_RX_PORT,
|
||||
CPU_TX_PORT,
|
||||
HOST_PORT,
|
||||
VIRTUAL_CPU_RX_PORT,
|
||||
VIRTUAL_CPU_TX_PORT,
|
||||
DISCARD,
|
||||
};
|
||||
|
||||
#endif
|
||||
647
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
Normal file
647
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
Normal file
File diff suppressed because it is too large
Load Diff
182
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
Normal file
182
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_EEPROM_H
|
||||
#define __MT76x2_EEPROM_H
|
||||
|
||||
#include "mt76x2.h"
|
||||
|
||||
enum mt76x2_eeprom_field {
|
||||
MT_EE_CHIP_ID = 0x000,
|
||||
MT_EE_VERSION = 0x002,
|
||||
MT_EE_MAC_ADDR = 0x004,
|
||||
MT_EE_PCI_ID = 0x00A,
|
||||
MT_EE_NIC_CONF_0 = 0x034,
|
||||
MT_EE_NIC_CONF_1 = 0x036,
|
||||
MT_EE_NIC_CONF_2 = 0x042,
|
||||
|
||||
MT_EE_XTAL_TRIM_1 = 0x03a,
|
||||
MT_EE_XTAL_TRIM_2 = 0x09e,
|
||||
|
||||
MT_EE_LNA_GAIN = 0x044,
|
||||
MT_EE_RSSI_OFFSET_2G_0 = 0x046,
|
||||
MT_EE_RSSI_OFFSET_2G_1 = 0x048,
|
||||
MT_EE_RSSI_OFFSET_5G_0 = 0x04a,
|
||||
MT_EE_RSSI_OFFSET_5G_1 = 0x04c,
|
||||
|
||||
MT_EE_TX_POWER_DELTA_BW40 = 0x050,
|
||||
MT_EE_TX_POWER_DELTA_BW80 = 0x052,
|
||||
|
||||
MT_EE_TX_POWER_EXT_PA_5G = 0x054,
|
||||
|
||||
MT_EE_TX_POWER_0_START_2G = 0x056,
|
||||
MT_EE_TX_POWER_1_START_2G = 0x05c,
|
||||
|
||||
/* used as byte arrays */
|
||||
#define MT_TX_POWER_GROUP_SIZE_5G 5
|
||||
#define MT_TX_POWER_GROUPS_5G 6
|
||||
MT_EE_TX_POWER_0_START_5G = 0x062,
|
||||
|
||||
MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074,
|
||||
MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076,
|
||||
|
||||
MT_EE_TX_POWER_1_START_5G = 0x080,
|
||||
|
||||
MT_EE_TX_POWER_CCK = 0x0a0,
|
||||
MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2,
|
||||
MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4,
|
||||
MT_EE_TX_POWER_OFDM_5G_6M = 0x0b2,
|
||||
MT_EE_TX_POWER_OFDM_5G_24M = 0x0b4,
|
||||
MT_EE_TX_POWER_HT_MCS0 = 0x0a6,
|
||||
MT_EE_TX_POWER_HT_MCS4 = 0x0a8,
|
||||
MT_EE_TX_POWER_HT_MCS8 = 0x0aa,
|
||||
MT_EE_TX_POWER_HT_MCS12 = 0x0ac,
|
||||
MT_EE_TX_POWER_VHT_MCS0 = 0x0ba,
|
||||
MT_EE_TX_POWER_VHT_MCS4 = 0x0bc,
|
||||
MT_EE_TX_POWER_VHT_MCS8 = 0x0be,
|
||||
|
||||
MT_EE_RF_TEMP_COMP_SLOPE_5G = 0x0f2,
|
||||
MT_EE_RF_TEMP_COMP_SLOPE_2G = 0x0f4,
|
||||
|
||||
MT_EE_RF_2G_TSSI_OFF_TXPOWER = 0x0f6,
|
||||
MT_EE_RF_2G_RX_HIGH_GAIN = 0x0f8,
|
||||
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN = 0x0fa,
|
||||
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN = 0x0fc,
|
||||
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN = 0x0fe,
|
||||
|
||||
MT_EE_BT_RCAL_RESULT = 0x138,
|
||||
MT_EE_BT_VCDL_CALIBRATION = 0x13c,
|
||||
MT_EE_BT_PMUCFG = 0x13e,
|
||||
|
||||
__MT_EE_MAX
|
||||
};
|
||||
|
||||
#define MT_EE_NIC_CONF_0_PA_INT_2G BIT(8)
|
||||
#define MT_EE_NIC_CONF_0_PA_INT_5G BIT(9)
|
||||
#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12)
|
||||
|
||||
#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1)
|
||||
#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2)
|
||||
#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3)
|
||||
#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13)
|
||||
|
||||
#define MT_EE_NIC_CONF_2_RX_STREAM GENMASK(3, 0)
|
||||
#define MT_EE_NIC_CONF_2_TX_STREAM GENMASK(7, 4)
|
||||
#define MT_EE_NIC_CONF_2_HW_ANTDIV BIT(8)
|
||||
#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9)
|
||||
#define MT_EE_NIC_CONF_2_TEMP_DISABLE BIT(11)
|
||||
#define MT_EE_NIC_CONF_2_COEX_METHOD GENMASK(15, 13)
|
||||
|
||||
enum mt76x2_board_type {
|
||||
BOARD_TYPE_2GHZ = 1,
|
||||
BOARD_TYPE_5GHZ = 2,
|
||||
};
|
||||
|
||||
enum mt76x2_cal_channel_group {
|
||||
MT_CH_5G_JAPAN,
|
||||
MT_CH_5G_UNII_1,
|
||||
MT_CH_5G_UNII_2,
|
||||
MT_CH_5G_UNII_2E_1,
|
||||
MT_CH_5G_UNII_2E_2,
|
||||
MT_CH_5G_UNII_3,
|
||||
__MT_CH_MAX
|
||||
};
|
||||
|
||||
struct mt76x2_tx_power_info {
|
||||
u8 target_power;
|
||||
|
||||
s8 delta_bw40;
|
||||
s8 delta_bw80;
|
||||
|
||||
struct {
|
||||
s8 tssi_slope;
|
||||
s8 tssi_offset;
|
||||
s8 target_power;
|
||||
s8 delta;
|
||||
} chain[MT_MAX_CHAINS];
|
||||
};
|
||||
|
||||
struct mt76x2_temp_comp {
|
||||
u8 temp_25_ref;
|
||||
int lower_bound; /* J */
|
||||
int upper_bound; /* J */
|
||||
unsigned int high_slope; /* J / dB */
|
||||
unsigned int low_slope; /* J / dB */
|
||||
};
|
||||
|
||||
static inline int
|
||||
mt76x2_eeprom_get(struct mt76x2_dev *dev, enum mt76x2_eeprom_field field)
|
||||
{
|
||||
if ((field & 1) || field >= __MT_EE_MAX)
|
||||
return -1;
|
||||
|
||||
return get_unaligned_le16(dev->mt76.eeprom.data + field);
|
||||
}
|
||||
|
||||
void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t);
|
||||
void mt76x2_get_power_info(struct mt76x2_dev *dev,
|
||||
struct mt76x2_tx_power_info *t);
|
||||
int mt76x2_get_temp_comp(struct mt76x2_dev *dev, struct mt76x2_temp_comp *t);
|
||||
bool mt76x2_ext_pa_enabled(struct mt76x2_dev *dev, enum nl80211_band band);
|
||||
void mt76x2_read_rx_gain(struct mt76x2_dev *dev);
|
||||
|
||||
static inline bool
|
||||
mt76x2_temp_tx_alc_enabled(struct mt76x2_dev *dev)
|
||||
{
|
||||
return mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) &
|
||||
MT_EE_NIC_CONF_1_TEMP_TX_ALC;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mt76x2_tssi_enabled(struct mt76x2_dev *dev)
|
||||
{
|
||||
return !mt76x2_temp_tx_alc_enabled(dev) &&
|
||||
(mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) &
|
||||
MT_EE_NIC_CONF_1_TX_ALC_EN);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mt76x2_has_ext_lna(struct mt76x2_dev *dev)
|
||||
{
|
||||
u32 val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1);
|
||||
|
||||
if (dev->mt76.chandef.chan->band == NL80211_BAND_2GHZ)
|
||||
return val & MT_EE_NIC_CONF_1_LNA_EXT_2G;
|
||||
else
|
||||
return val & MT_EE_NIC_CONF_1_LNA_EXT_5G;
|
||||
}
|
||||
|
||||
#endif
|
||||
839
drivers/net/wireless/mediatek/mt76/mt76x2_init.c
Normal file
839
drivers/net/wireless/mediatek/mt76/mt76x2_init.c
Normal file
File diff suppressed because it is too large
Load Diff
755
drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
Normal file
755
drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
Normal file
File diff suppressed because it is too large
Load Diff
190
drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
Normal file
190
drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_MAC_H
|
||||
#define __MT76x2_MAC_H
|
||||
|
||||
#include "mt76.h"
|
||||
|
||||
struct mt76x2_dev;
|
||||
struct mt76x2_sta;
|
||||
struct mt76x2_vif;
|
||||
struct mt76x2_txwi;
|
||||
|
||||
struct mt76x2_tx_status {
|
||||
u8 valid:1;
|
||||
u8 success:1;
|
||||
u8 aggr:1;
|
||||
u8 ack_req:1;
|
||||
u8 wcid;
|
||||
u8 pktid;
|
||||
u8 retry;
|
||||
u16 rate;
|
||||
} __packed __aligned(2);
|
||||
|
||||
struct mt76x2_tx_info {
|
||||
unsigned long jiffies;
|
||||
u8 tries;
|
||||
|
||||
u8 wcid;
|
||||
u8 pktid;
|
||||
u8 retry;
|
||||
};
|
||||
|
||||
struct mt76x2_rxwi {
|
||||
__le32 rxinfo;
|
||||
|
||||
__le32 ctl;
|
||||
|
||||
__le16 tid_sn;
|
||||
__le16 rate;
|
||||
|
||||
u8 rssi[4];
|
||||
|
||||
__le32 bbp_rxinfo[4];
|
||||
};
|
||||
|
||||
#define MT_RXINFO_BA BIT(0)
|
||||
#define MT_RXINFO_DATA BIT(1)
|
||||
#define MT_RXINFO_NULL BIT(2)
|
||||
#define MT_RXINFO_FRAG BIT(3)
|
||||
#define MT_RXINFO_UNICAST BIT(4)
|
||||
#define MT_RXINFO_MULTICAST BIT(5)
|
||||
#define MT_RXINFO_BROADCAST BIT(6)
|
||||
#define MT_RXINFO_MYBSS BIT(7)
|
||||
#define MT_RXINFO_CRCERR BIT(8)
|
||||
#define MT_RXINFO_ICVERR BIT(9)
|
||||
#define MT_RXINFO_MICERR BIT(10)
|
||||
#define MT_RXINFO_AMSDU BIT(11)
|
||||
#define MT_RXINFO_HTC BIT(12)
|
||||
#define MT_RXINFO_RSSI BIT(13)
|
||||
#define MT_RXINFO_L2PAD BIT(14)
|
||||
#define MT_RXINFO_AMPDU BIT(15)
|
||||
#define MT_RXINFO_DECRYPT BIT(16)
|
||||
#define MT_RXINFO_BSSIDX3 BIT(17)
|
||||
#define MT_RXINFO_WAPI_KEY BIT(18)
|
||||
#define MT_RXINFO_PN_LEN GENMASK(21, 19)
|
||||
#define MT_RXINFO_SW_FTYPE0 BIT(22)
|
||||
#define MT_RXINFO_SW_FTYPE1 BIT(23)
|
||||
#define MT_RXINFO_PROBE_RESP BIT(24)
|
||||
#define MT_RXINFO_BEACON BIT(25)
|
||||
#define MT_RXINFO_DISASSOC BIT(26)
|
||||
#define MT_RXINFO_DEAUTH BIT(27)
|
||||
#define MT_RXINFO_ACTION BIT(28)
|
||||
#define MT_RXINFO_TCP_SUM_ERR BIT(30)
|
||||
#define MT_RXINFO_IP_SUM_ERR BIT(31)
|
||||
|
||||
#define MT_RXWI_CTL_WCID GENMASK(7, 0)
|
||||
#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8)
|
||||
#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10)
|
||||
#define MT_RXWI_CTL_UDF GENMASK(15, 13)
|
||||
#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16)
|
||||
#define MT_RXWI_CTL_EOF BIT(31)
|
||||
|
||||
#define MT_RXWI_TID GENMASK(3, 0)
|
||||
#define MT_RXWI_SN GENMASK(15, 4)
|
||||
|
||||
#define MT_RXWI_RATE_INDEX GENMASK(5, 0)
|
||||
#define MT_RXWI_RATE_LDPC BIT(6)
|
||||
#define MT_RXWI_RATE_BW GENMASK(8, 7)
|
||||
#define MT_RXWI_RATE_SGI BIT(9)
|
||||
#define MT_RXWI_RATE_STBC BIT(10)
|
||||
#define MT_RXWI_RATE_LDPC_EXSYM BIT(11)
|
||||
#define MT_RXWI_RATE_PHY GENMASK(15, 13)
|
||||
|
||||
#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0)
|
||||
#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4)
|
||||
|
||||
#define MT_TX_PWR_ADJ GENMASK(3, 0)
|
||||
|
||||
enum mt76x2_phy_bandwidth {
|
||||
MT_PHY_BW_20,
|
||||
MT_PHY_BW_40,
|
||||
MT_PHY_BW_80,
|
||||
};
|
||||
|
||||
#define MT_TXWI_FLAGS_FRAG BIT(0)
|
||||
#define MT_TXWI_FLAGS_MMPS BIT(1)
|
||||
#define MT_TXWI_FLAGS_CFACK BIT(2)
|
||||
#define MT_TXWI_FLAGS_TS BIT(3)
|
||||
#define MT_TXWI_FLAGS_AMPDU BIT(4)
|
||||
#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5)
|
||||
#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8)
|
||||
#define MT_TXWI_FLAGS_NDPS BIT(10)
|
||||
#define MT_TXWI_FLAGS_RTSBWSIG BIT(11)
|
||||
#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12)
|
||||
#define MT_TXWI_FLAGS_SOUND BIT(14)
|
||||
#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15)
|
||||
|
||||
#define MT_TXWI_ACK_CTL_REQ BIT(0)
|
||||
#define MT_TXWI_ACK_CTL_NSEQ BIT(1)
|
||||
#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2)
|
||||
|
||||
#define MT_TXWI_PKTID_PROBE BIT(7)
|
||||
|
||||
struct mt76x2_txwi {
|
||||
__le16 flags;
|
||||
__le16 rate;
|
||||
u8 ack_ctl;
|
||||
u8 wcid;
|
||||
__le16 len_ctl;
|
||||
__le32 iv;
|
||||
__le32 eiv;
|
||||
u8 aid;
|
||||
u8 txstream;
|
||||
u8 ctl2;
|
||||
u8 pktid;
|
||||
} __packed __aligned(4);
|
||||
|
||||
static inline struct mt76x2_tx_info *
|
||||
mt76x2_skb_tx_info(struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
return (void *) info->status.status_driver_data;
|
||||
}
|
||||
|
||||
int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard);
|
||||
int mt76x2_mac_start(struct mt76x2_dev *dev);
|
||||
void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force);
|
||||
void mt76x2_mac_resume(struct mt76x2_dev *dev);
|
||||
void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr);
|
||||
|
||||
int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
|
||||
void *rxi);
|
||||
void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
|
||||
struct sk_buff *skb, struct mt76_wcid *wcid,
|
||||
struct ieee80211_sta *sta);
|
||||
void mt76x2_mac_wcid_setup(struct mt76x2_dev *dev, u8 idx, u8 vif_idx, u8 *mac);
|
||||
int mt76x2_mac_wcid_set_key(struct mt76x2_dev *dev, u8 idx,
|
||||
struct ieee80211_key_conf *key);
|
||||
void mt76x2_mac_wcid_set_rate(struct mt76x2_dev *dev, struct mt76_wcid *wcid,
|
||||
const struct ieee80211_tx_rate *rate);
|
||||
void mt76x2_mac_wcid_set_drop(struct mt76x2_dev *dev, u8 idx, bool drop);
|
||||
|
||||
int mt76x2_mac_shared_key_setup(struct mt76x2_dev *dev, u8 vif_idx, u8 key_idx,
|
||||
struct ieee80211_key_conf *key);
|
||||
|
||||
int mt76x2_mac_set_beacon(struct mt76x2_dev *dev, u8 vif_idx,
|
||||
struct sk_buff *skb);
|
||||
void mt76x2_mac_set_beacon_enable(struct mt76x2_dev *dev, u8 vif_idx, bool val);
|
||||
|
||||
void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq);
|
||||
void mt76x2_mac_process_tx_status_fifo(struct mt76x2_dev *dev);
|
||||
|
||||
void mt76x2_mac_work(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
545
drivers/net/wireless/mediatek/mt76/mt76x2_main.c
Normal file
545
drivers/net/wireless/mediatek/mt76/mt76x2_main.c
Normal file
File diff suppressed because it is too large
Load Diff
451
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
Normal file
451
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "mt76x2.h"
|
||||
#include "mt76x2_mcu.h"
|
||||
#include "mt76x2_dma.h"
|
||||
#include "mt76x2_eeprom.h"
|
||||
|
||||
struct mt76x2_fw_header {
|
||||
__le32 ilm_len;
|
||||
__le32 dlm_len;
|
||||
__le16 build_ver;
|
||||
__le16 fw_ver;
|
||||
u8 pad[4];
|
||||
char build_time[16];
|
||||
};
|
||||
|
||||
struct mt76x2_patch_header {
|
||||
char build_time[16];
|
||||
char platform[4];
|
||||
char hw_version[4];
|
||||
char patch_version[4];
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
static struct sk_buff *mt76x2_mcu_msg_alloc(const void *data, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(len, GFP_KERNEL);
|
||||
memcpy(skb_put(skb, len), data, len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
mt76x2_mcu_get_response(struct mt76x2_dev *dev, unsigned long expires)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (!time_is_after_jiffies(expires))
|
||||
return NULL;
|
||||
|
||||
timeout = expires - jiffies;
|
||||
wait_event_timeout(dev->mcu.wait, !skb_queue_empty(&dev->mcu.res_q),
|
||||
timeout);
|
||||
return skb_dequeue(&dev->mcu.res_q);
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_mcu_msg_send(struct mt76x2_dev *dev, struct sk_buff *skb,
|
||||
enum mcu_cmd cmd)
|
||||
{
|
||||
unsigned long expires = jiffies + HZ;
|
||||
int ret;
|
||||
u8 seq;
|
||||
|
||||
if (!skb)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mcu.mutex);
|
||||
|
||||
seq = ++dev->mcu.msg_seq & 0xf;
|
||||
if (!seq)
|
||||
seq = ++dev->mcu.msg_seq & 0xf;
|
||||
|
||||
ret = mt76x2_tx_queue_mcu(dev, MT_TXQ_MCU, skb, cmd, seq);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
while (1) {
|
||||
u32 *rxfce;
|
||||
bool check_seq = false;
|
||||
|
||||
skb = mt76x2_mcu_get_response(dev, expires);
|
||||
if (!skb) {
|
||||
dev_err(dev->mt76.dev,
|
||||
"MCU message %d (seq %d) timed out\n", cmd,
|
||||
seq);
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
rxfce = (u32 *) skb->cb;
|
||||
|
||||
if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce))
|
||||
check_seq = true;
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
if (check_seq)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mcu.mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76pci_load_rom_patch(struct mt76x2_dev *dev)
|
||||
{
|
||||
const struct firmware *fw = NULL;
|
||||
struct mt76x2_patch_header *hdr;
|
||||
bool rom_protect = !is_mt7612(dev);
|
||||
int len, ret = 0;
|
||||
__le32 *cur;
|
||||
u32 patch_mask, patch_reg;
|
||||
|
||||
if (rom_protect && !mt76_poll(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) {
|
||||
dev_err(dev->mt76.dev,
|
||||
"Could not get hardware semaphore for ROM PATCH\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E3) {
|
||||
patch_mask = BIT(0);
|
||||
patch_reg = MT_MCU_CLOCK_CTL;
|
||||
} else {
|
||||
patch_mask = BIT(1);
|
||||
patch_reg = MT_MCU_COM_REG0;
|
||||
}
|
||||
|
||||
if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) {
|
||||
dev_info(dev->mt76.dev, "ROM patch already applied\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!fw || !fw->data || fw->size <= sizeof(*hdr)) {
|
||||
ret = -EIO;
|
||||
dev_err(dev->mt76.dev, "Failed to load firmware\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
hdr = (struct mt76x2_patch_header *) fw->data;
|
||||
dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET);
|
||||
|
||||
cur = (__le32 *) (fw->data + sizeof(*hdr));
|
||||
len = fw->size - sizeof(*hdr);
|
||||
mt76_wr_copy(dev, MT_MCU_ROM_PATCH_ADDR, cur, len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
|
||||
|
||||
/* Trigger ROM */
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 4);
|
||||
|
||||
if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 2000)) {
|
||||
dev_err(dev->mt76.dev, "Failed to load ROM patch\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
/* release semaphore */
|
||||
if (rom_protect)
|
||||
mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1);
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76pci_load_firmware(struct mt76x2_dev *dev)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const struct mt76x2_fw_header *hdr;
|
||||
int i, len, ret;
|
||||
__le32 *cur;
|
||||
u32 offset, val;
|
||||
|
||||
ret = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!fw || !fw->data || fw->size < sizeof(*hdr))
|
||||
goto error;
|
||||
|
||||
hdr = (const struct mt76x2_fw_header *) fw->data;
|
||||
|
||||
len = sizeof(*hdr);
|
||||
len += le32_to_cpu(hdr->ilm_len);
|
||||
len += le32_to_cpu(hdr->dlm_len);
|
||||
|
||||
if (fw->size != len)
|
||||
goto error;
|
||||
|
||||
val = le16_to_cpu(hdr->fw_ver);
|
||||
dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n",
|
||||
(val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf);
|
||||
|
||||
val = le16_to_cpu(hdr->build_ver);
|
||||
dev_info(dev->mt76.dev, "Build: %x\n", val);
|
||||
dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time);
|
||||
|
||||
cur = (__le32 *) (fw->data + sizeof(*hdr));
|
||||
len = le32_to_cpu(hdr->ilm_len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ILM_OFFSET);
|
||||
mt76_wr_copy(dev, MT_MCU_ILM_ADDR, cur, len);
|
||||
|
||||
cur += len / sizeof(*cur);
|
||||
len = le32_to_cpu(hdr->dlm_len);
|
||||
|
||||
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
|
||||
offset = MT_MCU_DLM_ADDR_E3;
|
||||
else
|
||||
offset = MT_MCU_DLM_ADDR;
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET);
|
||||
mt76_wr_copy(dev, offset, cur, len);
|
||||
|
||||
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
|
||||
|
||||
val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_2);
|
||||
if (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, val) == 1)
|
||||
mt76_set(dev, MT_MCU_COM_REG0, BIT(30));
|
||||
|
||||
/* trigger firmware */
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 2);
|
||||
for (i = 200; i > 0; i--) {
|
||||
val = mt76_rr(dev, MT_MCU_COM_REG0);
|
||||
|
||||
if (val & 1)
|
||||
break;
|
||||
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if (!i) {
|
||||
dev_err(dev->mt76.dev, "Firmware failed to start\n");
|
||||
release_firmware(fw);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
dev_info(dev->mt76.dev, "Firmware running!\n");
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
dev_err(dev->mt76.dev, "Invalid firmware\n");
|
||||
release_firmware(fw);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int
|
||||
mt76x2_mcu_function_select(struct mt76x2_dev *dev, enum mcu_function func,
|
||||
u32 val)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 id;
|
||||
__le32 value;
|
||||
} __packed __aligned(4) msg = {
|
||||
.id = cpu_to_le32(func),
|
||||
.value = cpu_to_le32(val),
|
||||
};
|
||||
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_FUN_SET_OP);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level,
|
||||
u8 channel)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
u8 cr_mode;
|
||||
u8 temp;
|
||||
u8 ch;
|
||||
u8 _pad0;
|
||||
|
||||
__le32 cfg;
|
||||
} __packed __aligned(4) msg = {
|
||||
.cr_mode = type,
|
||||
.temp = temp_level,
|
||||
.ch = channel,
|
||||
};
|
||||
u32 val;
|
||||
|
||||
val = BIT(31);
|
||||
val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff;
|
||||
val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00;
|
||||
msg.cfg = cpu_to_le32(val);
|
||||
|
||||
/* first set the channel without the extension channel info */
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_LOAD_CR);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw,
|
||||
u8 bw_index, bool scan)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
u8 idx;
|
||||
u8 scan;
|
||||
u8 bw;
|
||||
u8 _pad0;
|
||||
|
||||
__le16 chainmask;
|
||||
u8 ext_chan;
|
||||
u8 _pad1;
|
||||
|
||||
} __packed __aligned(4) msg = {
|
||||
.idx = channel,
|
||||
.scan = scan,
|
||||
.bw = bw,
|
||||
.chainmask = cpu_to_le16(dev->chainmask),
|
||||
};
|
||||
|
||||
/* first set the channel without the extension channel info */
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP);
|
||||
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
msg.ext_chan = 0xe0 + bw_index;
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 mode;
|
||||
__le32 level;
|
||||
} __packed __aligned(4) msg = {
|
||||
.mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF),
|
||||
.level = cpu_to_le32(0),
|
||||
};
|
||||
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_POWER_SAVING_OP);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type,
|
||||
u32 param)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 id;
|
||||
__le32 value;
|
||||
} __packed __aligned(4) msg = {
|
||||
.id = cpu_to_le32(type),
|
||||
.value = cpu_to_le32(param),
|
||||
};
|
||||
int ret;
|
||||
|
||||
mt76_clear(dev, MT_MCU_COM_REG0, BIT(31));
|
||||
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
ret = mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0,
|
||||
BIT(31), BIT(31), 100)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev,
|
||||
struct mt76x2_tssi_comp *tssi_data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 id;
|
||||
struct mt76x2_tssi_comp data;
|
||||
} __packed __aligned(4) msg = {
|
||||
.id = cpu_to_le32(MCU_CAL_TSSI_COMP),
|
||||
.data = *tssi_data,
|
||||
};
|
||||
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain,
|
||||
bool force)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct {
|
||||
__le32 channel;
|
||||
__le32 gain_val;
|
||||
} __packed __aligned(4) msg = {
|
||||
.channel = cpu_to_le32(channel),
|
||||
.gain_val = cpu_to_le32(gain),
|
||||
};
|
||||
|
||||
if (force)
|
||||
msg.channel |= cpu_to_le32(BIT(31));
|
||||
|
||||
skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg));
|
||||
return mt76x2_mcu_msg_send(dev, skb, CMD_INIT_GAIN_OP);
|
||||
}
|
||||
|
||||
int mt76x2_mcu_init(struct mt76x2_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_init(&dev->mcu.mutex);
|
||||
|
||||
ret = mt76pci_load_rom_patch(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt76pci_load_firmware(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mt76x2_mcu_function_select(dev, Q_SELECT, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt76x2_mcu_cleanup(struct mt76x2_dev *dev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
mt76_wr(dev, MT_MCU_INT_LEVEL, 1);
|
||||
usleep_range(20000, 30000);
|
||||
|
||||
while ((skb = skb_dequeue(&dev->mcu.res_q)) != NULL)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
155
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h
Normal file
155
drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MT76x2_MCU_H
|
||||
#define __MT76x2_MCU_H
|
||||
|
||||
/* Register definitions */
|
||||
#define MT_MCU_CPU_CTL 0x0704
|
||||
#define MT_MCU_CLOCK_CTL 0x0708
|
||||
#define MT_MCU_RESET_CTL 0x070C
|
||||
#define MT_MCU_INT_LEVEL 0x0718
|
||||
#define MT_MCU_COM_REG0 0x0730
|
||||
#define MT_MCU_COM_REG1 0x0734
|
||||
#define MT_MCU_COM_REG2 0x0738
|
||||
#define MT_MCU_COM_REG3 0x073C
|
||||
#define MT_MCU_PCIE_REMAP_BASE1 0x0740
|
||||
#define MT_MCU_PCIE_REMAP_BASE2 0x0744
|
||||
#define MT_MCU_PCIE_REMAP_BASE3 0x0748
|
||||
#define MT_MCU_PCIE_REMAP_BASE4 0x074C
|
||||
|
||||
#define MT_LED_CTRL 0x0770
|
||||
#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n)))
|
||||
#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n)))
|
||||
#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n)))
|
||||
#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n)))
|
||||
|
||||
#define MT_LED_TX_BLINK_0 0x0774
|
||||
#define MT_LED_TX_BLINK_1 0x0778
|
||||
|
||||
#define MT_LED_S0_BASE 0x077C
|
||||
#define MT_LED_S0(_n) (MT_LED_S0_BASE + 8 * (_n))
|
||||
#define MT_LED_S1_BASE 0x0780
|
||||
#define MT_LED_S1(_n) (MT_LED_S1_BASE + 8 * (_n))
|
||||
#define MT_LED_STATUS_OFF_MASK GENMASK(31, 24)
|
||||
#define MT_LED_STATUS_OFF(_v) (((_v) << __ffs(MT_LED_STATUS_OFF_MASK)) & \
|
||||
MT_LED_STATUS_OFF_MASK)
|
||||
#define MT_LED_STATUS_ON_MASK GENMASK(23, 16)
|
||||
#define MT_LED_STATUS_ON(_v) (((_v) << __ffs(MT_LED_STATUS_ON_MASK)) & \
|
||||
MT_LED_STATUS_ON_MASK)
|
||||
#define MT_LED_STATUS_DURATION_MASK GENMASK(15, 8)
|
||||
#define MT_LED_STATUS_DURATION(_v) (((_v) << __ffs(MT_LED_STATUS_DURATION_MASK)) & \
|
||||
MT_LED_STATUS_DURATION_MASK)
|
||||
|
||||
#define MT_MCU_SEMAPHORE_00 0x07B0
|
||||
#define MT_MCU_SEMAPHORE_01 0x07B4
|
||||
#define MT_MCU_SEMAPHORE_02 0x07B8
|
||||
#define MT_MCU_SEMAPHORE_03 0x07BC
|
||||
|
||||
#define MT_MCU_ROM_PATCH_OFFSET 0x80000
|
||||
#define MT_MCU_ROM_PATCH_ADDR 0x90000
|
||||
|
||||
#define MT_MCU_ILM_OFFSET 0x80000
|
||||
#define MT_MCU_ILM_ADDR 0x80000
|
||||
|
||||
#define MT_MCU_DLM_OFFSET 0x100000
|
||||
#define MT_MCU_DLM_ADDR 0x90000
|
||||
#define MT_MCU_DLM_ADDR_E3 0x90800
|
||||
|
||||
enum mcu_cmd {
|
||||
CMD_FUN_SET_OP = 1,
|
||||
CMD_LOAD_CR = 2,
|
||||
CMD_INIT_GAIN_OP = 3,
|
||||
CMD_DYNC_VGA_OP = 6,
|
||||
CMD_TDLS_CH_SW = 7,
|
||||
CMD_BURST_WRITE = 8,
|
||||
CMD_READ_MODIFY_WRITE = 9,
|
||||
CMD_RANDOM_READ = 10,
|
||||
CMD_BURST_READ = 11,
|
||||
CMD_RANDOM_WRITE = 12,
|
||||
CMD_LED_MODE_OP = 16,
|
||||
CMD_POWER_SAVING_OP = 20,
|
||||
CMD_WOW_CONFIG = 21,
|
||||
CMD_WOW_QUERY = 22,
|
||||
CMD_WOW_FEATURE = 24,
|
||||
CMD_CARRIER_DETECT_OP = 28,
|
||||
CMD_RADOR_DETECT_OP = 29,
|
||||
CMD_SWITCH_CHANNEL_OP = 30,
|
||||
CMD_CALIBRATION_OP = 31,
|
||||
CMD_BEACON_OP = 32,
|
||||
CMD_ANTENNA_OP = 33,
|
||||
};
|
||||
|
||||
enum mcu_function {
|
||||
Q_SELECT = 1,
|
||||
BW_SETTING = 2,
|
||||
USB2_SW_DISCONNECT = 2,
|
||||
USB3_SW_DISCONNECT = 3,
|
||||
LOG_FW_DEBUG_MSG = 4,
|
||||
GET_FW_VERSION = 5,
|
||||
};
|
||||
|
||||
enum mcu_power_mode {
|
||||
RADIO_OFF = 0x30,
|
||||
RADIO_ON = 0x31,
|
||||
RADIO_OFF_AUTO_WAKEUP = 0x32,
|
||||
RADIO_OFF_ADVANCE = 0x33,
|
||||
RADIO_ON_ADVANCE = 0x34,
|
||||
};
|
||||
|
||||
enum mcu_calibration {
|
||||
MCU_CAL_R = 1,
|
||||
MCU_CAL_TEMP_SENSOR,
|
||||
MCU_CAL_RXDCOC,
|
||||
MCU_CAL_RC,
|
||||
MCU_CAL_SX_LOGEN,
|
||||
MCU_CAL_LC,
|
||||
MCU_CAL_TX_LOFT,
|
||||
MCU_CAL_TXIQ,
|
||||
MCU_CAL_TSSI,
|
||||
MCU_CAL_TSSI_COMP,
|
||||
MCU_CAL_DPD,
|
||||
MCU_CAL_RXIQC_FI,
|
||||
MCU_CAL_RXIQC_FD,
|
||||
MCU_CAL_PWRON,
|
||||
MCU_CAL_TX_SHAPING,
|
||||
};
|
||||
|
||||
enum mt76x2_mcu_cr_mode {
|
||||
MT_RF_CR,
|
||||
MT_BBP_CR,
|
||||
MT_RF_BBP_CR,
|
||||
MT_HL_TEMP_CR_UPDATE,
|
||||
};
|
||||
|
||||
struct mt76x2_tssi_comp {
|
||||
u8 pa_mode;
|
||||
u8 cal_mode;
|
||||
u16 pad;
|
||||
|
||||
u8 slope0;
|
||||
u8 slope1;
|
||||
u8 offset0;
|
||||
u8 offset1;
|
||||
} __packed __aligned(4);
|
||||
|
||||
int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type,
|
||||
u32 param);
|
||||
int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, struct mt76x2_tssi_comp *tssi_data);
|
||||
int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain,
|
||||
bool force);
|
||||
|
||||
#endif
|
||||
110
drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
Normal file
110
drivers/net/wireless/mediatek/mt76/mt76x2_pci.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "mt76x2.h"
|
||||
#include "mt76x2_trace.h"
|
||||
|
||||
static const struct pci_device_id mt76pci_device_table[] = {
|
||||
{ PCI_DEVICE(0x14c3, 0x7662) },
|
||||
{ PCI_DEVICE(0x14c3, 0x7612) },
|
||||
{ PCI_DEVICE(0x14c3, 0x7602) },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int
|
||||
mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct mt76x2_dev *dev;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev = mt76x2_alloc_device(&pdev->dev);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
|
||||
|
||||
dev->mt76.rev = mt76_rr(dev, MT_ASIC_VERSION);
|
||||
dev_info(dev->mt76.dev, "ASIC revision: %08x\n", dev->mt76.rev);
|
||||
|
||||
ret = devm_request_irq(dev->mt76.dev, pdev->irq, mt76x2_irq_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = mt76x2_register_device(dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* Fix up ASPM configuration */
|
||||
|
||||
/* RG_SSUSB_G1_CDR_BIR_LTR = 0x9 */
|
||||
mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9);
|
||||
|
||||
/* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */
|
||||
mt76_rmw_field(dev, 0x15a0c, 0xf << 28, 0xf);
|
||||
|
||||
/* RG_SSUSB_CDR_BR_PE1D = 0x3 */
|
||||
mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
ieee80211_free_hw(mt76_hw(dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mt76pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct mt76_dev *mdev = pci_get_drvdata(pdev);
|
||||
struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
|
||||
|
||||
mt76_unregister_device(mdev);
|
||||
mt76x2_cleanup(dev);
|
||||
ieee80211_free_hw(mdev->hw);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, mt76pci_device_table);
|
||||
MODULE_FIRMWARE(MT7662_FIRMWARE);
|
||||
MODULE_FIRMWARE(MT7662_ROM_PATCH);
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
static struct pci_driver mt76pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = mt76pci_device_table,
|
||||
.probe = mt76pci_probe,
|
||||
.remove = mt76pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(mt76pci_driver);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user