Skip to content

Commit

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

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

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

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
kuba-moo authored and Kalle Valo committed May 28, 2015
1 parent 00e27ee commit c869f77
Show file tree
Hide file tree
Showing 31 changed files with 7,950 additions and 0 deletions.
6 changes: 6 additions & 0 deletions MAINTAINERS
Expand Up @@ -6365,6 +6365,12 @@ F: include/uapi/linux/meye.h
F: include/uapi/linux/ivtv*
F: include/uapi/linux/uvcvideo.h

MEDIATEK MT7601U WIRELESS LAN DRIVER
M: Jakub Kicinski <kubakici@wp.pl>
L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/mediatek/mt7601u/

MEGARAID SCSI/SAS DRIVERS
M: Kashyap Desai <kashyap.desai@avagotech.com>
M: Sumit Saxena <sumit.saxena@avagotech.com>
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/Kconfig
Expand Up @@ -277,6 +277,7 @@ source "drivers/net/wireless/libertas/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/Makefile
Expand Up @@ -45,6 +45,8 @@ obj-$(CONFIG_IWLWIFI) += iwlwifi/
obj-$(CONFIG_IWLEGACY) += iwlegacy/
obj-$(CONFIG_RT2X00) += rt2x00/

obj-$(CONFIG_WL_MEDIATEK) += mediatek/

obj-$(CONFIG_P54_COMMON) += p54/

obj-$(CONFIG_ATH_CARDS) += ath/
Expand Down
10 changes: 10 additions & 0 deletions drivers/net/wireless/mediatek/Kconfig
@@ -0,0 +1,10 @@
menuconfig WL_MEDIATEK
bool "Mediatek Wireless LAN support"
---help---
Enable community drivers for MediaTek WiFi devices.
Those drivers make use of the Linux mac80211 stack.


if WL_MEDIATEK
source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
endif # WL_MEDIATEK
1 change: 1 addition & 0 deletions drivers/net/wireless/mediatek/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_MT7601U) += mt7601u/
6 changes: 6 additions & 0 deletions drivers/net/wireless/mediatek/mt7601u/Kconfig
@@ -0,0 +1,6 @@
config MT7601U
tristate "MediaTek MT7601U (USB) support"
depends on MAC80211
depends on USB
---help---
This adds support for MT7601U-based wireless USB dongles.
9 changes: 9 additions & 0 deletions drivers/net/wireless/mediatek/mt7601u/Makefile
@@ -0,0 +1,9 @@
ccflags-y += -D__CHECK_ENDIAN__

obj-$(CONFIG_MT7601U) += mt7601u.o

mt7601u-objs = \
usb.o init.o main.o mcu.o trace.o dma.o core.o eeprom.o phy.o \
mac.o util.o debugfs.o tx.o

CFLAGS_trace.o := -I$(src)
78 changes: 78 additions & 0 deletions drivers/net/wireless/mediatek/mt7601u/core.c
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "mt7601u.h"

int mt7601u_wait_asic_ready(struct mt7601u_dev *dev)
{
int i = 100;
u32 val;

do {
if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
return -EIO;

val = mt7601u_rr(dev, MT_MAC_CSR0);
if (val && ~val)
return 0;

udelay(10);
} while (i--);

return -EIO;
}

bool mt76_poll(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
int timeout)
{
u32 cur;

timeout /= 10;
do {
if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
return false;

cur = mt7601u_rr(dev, offset) & mask;
if (cur == val)
return true;

udelay(10);
} while (timeout-- > 0);

dev_err(dev->dev, "Error: Time out with reg %08x\n", offset);

return false;
}

bool mt76_poll_msec(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val,
int timeout)
{
u32 cur;

timeout /= 10;
do {
if (test_bit(MT7601U_STATE_REMOVED, &dev->state))
return false;

cur = mt7601u_rr(dev, offset) & mask;
if (cur == val)
return true;

msleep(10);
} while (timeout-- > 0);

dev_err(dev->dev, "Error: Time out with reg %08x\n", offset);

return false;
}
172 changes: 172 additions & 0 deletions drivers/net/wireless/mediatek/mt7601u/debugfs.c
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include <linux/debugfs.h>

#include "mt7601u.h"
#include "eeprom.h"

static int
mt76_reg_set(void *data, u64 val)
{
struct mt7601u_dev *dev = data;

mt76_wr(dev, dev->debugfs_reg, val);
return 0;
}

static int
mt76_reg_get(void *data, u64 *val)
{
struct mt7601u_dev *dev = data;

*val = mt76_rr(dev, dev->debugfs_reg);
return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n");

static int
mt7601u_ampdu_stat_read(struct seq_file *file, void *data)
{
struct mt7601u_dev *dev = file->private;
int i, j;

#define stat_printf(grp, off, name) \
seq_printf(file, #name ":\t%llu\n", dev->stats.grp[off])

stat_printf(rx_stat, 0, rx_crc_err);
stat_printf(rx_stat, 1, rx_phy_err);
stat_printf(rx_stat, 2, rx_false_cca);
stat_printf(rx_stat, 3, rx_plcp_err);
stat_printf(rx_stat, 4, rx_fifo_overflow);
stat_printf(rx_stat, 5, rx_duplicate);

stat_printf(tx_stat, 0, tx_fail_cnt);
stat_printf(tx_stat, 1, tx_bcn_cnt);
stat_printf(tx_stat, 2, tx_success);
stat_printf(tx_stat, 3, tx_retransmit);
stat_printf(tx_stat, 4, tx_zero_len);
stat_printf(tx_stat, 5, tx_underflow);

stat_printf(aggr_stat, 0, non_aggr_tx);
stat_printf(aggr_stat, 1, aggr_tx);

stat_printf(zero_len_del, 0, tx_zero_len_del);
stat_printf(zero_len_del, 1, rx_zero_len_del);
#undef stat_printf

seq_puts(file, "Aggregations stats:\n");
for (i = 0; i < 4; i++) {
for (j = 0; j < 8; j++)
seq_printf(file, "%08llx ",
dev->stats.aggr_n[i * 8 + j]);
seq_putc(file, '\n');
}

seq_printf(file, "recent average AMPDU len: %d\n",
atomic_read(&dev->avg_ampdu_len));

return 0;
}

static int
mt7601u_ampdu_stat_open(struct inode *inode, struct file *f)
{
return single_open(f, mt7601u_ampdu_stat_read, inode->i_private);
}

static const struct file_operations fops_ampdu_stat = {
.open = mt7601u_ampdu_stat_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

static int
mt7601u_eeprom_param_read(struct seq_file *file, void *data)
{
struct mt7601u_dev *dev = file->private;
struct mt7601u_rate_power *rp = &dev->ee->power_rate_table;
struct tssi_data *td = &dev->ee->tssi_data;
int i;

seq_printf(file, "RF freq offset: %hhx\n", dev->ee->rf_freq_off);
seq_printf(file, "RSSI offset: %hhx %hhx\n",
dev->ee->rssi_offset[0], dev->ee->rssi_offset[1]);
seq_printf(file, "Reference temp: %hhx\n", dev->ee->ref_temp);
seq_printf(file, "LNA gain: %hhx\n", dev->ee->lna_gain);
seq_printf(file, "Reg channels: %hhu-%hhu\n", dev->ee->reg.start,
dev->ee->reg.start + dev->ee->reg.num - 1);

seq_puts(file, "Per rate power:\n");
for (i = 0; i < 2; i++)
seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
rp->cck[i].raw, rp->cck[i].bw20, rp->cck[i].bw40);
for (i = 0; i < 4; i++)
seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
rp->ofdm[i].raw, rp->ofdm[i].bw20, rp->ofdm[i].bw40);
for (i = 0; i < 4; i++)
seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n",
rp->ht[i].raw, rp->ht[i].bw20, rp->ht[i].bw40);

seq_puts(file, "Per channel power:\n");
for (i = 0; i < 7; i++)
seq_printf(file, "\t tx_power ch%u:%02hhx ch%u:%02hhx\n",
i * 2 + 1, dev->ee->chan_pwr[i * 2],
i * 2 + 2, dev->ee->chan_pwr[i * 2 + 1]);

if (!dev->ee->tssi_enabled)
return 0;

seq_puts(file, "TSSI:\n");
seq_printf(file, "\t slope:%02hhx\n", td->slope);
seq_printf(file, "\t offset=%02hhx %02hhx %02hhx\n",
td->offset[0], td->offset[1], td->offset[2]);
seq_printf(file, "\t delta_off:%08x\n", td->tx0_delta_offset);

return 0;
}

static int
mt7601u_eeprom_param_open(struct inode *inode, struct file *f)
{
return single_open(f, mt7601u_eeprom_param_read, inode->i_private);
}

static const struct file_operations fops_eeprom_param = {
.open = mt7601u_eeprom_param_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

void mt7601u_init_debugfs(struct mt7601u_dev *dev)
{
struct dentry *dir;

dir = debugfs_create_dir("mt7601u", dev->hw->wiphy->debugfsdir);
if (!dir)
return;

debugfs_create_u8("temperature", S_IRUSR, dir, &dev->raw_temp);
debugfs_create_u32("temp_mode", S_IRUSR, dir, &dev->temp_mode);

debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, dir, dev,
&fops_regval);
debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
debugfs_create_file("eeprom_param", S_IRUSR, dir, dev,
&fops_eeprom_param);
}

0 comments on commit c869f77

Please sign in to comment.