mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 21:36:52 +00:00
MediaTek has asked to switch from the ISC license to BSD-3-Clause-Clear, in order to improve clarity and the legal integrity of the code. The BSD-3-Clause license includes the "no endorsement" clause, which is important for protecting the reputation of the original authors and contributors by preventing unauthorized use of their names for endorsement purposes. This clause is absent in the BSD-2-Clause license, which is more permissive but lacks this specific protection. This change also cleans up the license of some Kconfig/Makefile files, which were accidentally marked as GPL. The GPL 2.0 remains in use on mt76x0, as well as two source files in mt7615 for which the license situation still needs to be clarified. Signed-off-by: Felix Fietkau <[email protected]>
172 lines
3.9 KiB
C
172 lines
3.9 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (C) 2016 Felix Fietkau <[email protected]>
|
|
* Copyright (C) 2018 Lorenzo Bianconi <[email protected]>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include "mt76x02_mcu.h"
|
|
|
|
int mt76x02_mcu_parse_response(struct mt76_dev *mdev, int cmd,
|
|
struct sk_buff *skb, int seq)
|
|
{
|
|
struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
|
|
u32 *rxfce;
|
|
|
|
if (!skb) {
|
|
dev_err(mdev->dev, "MCU message %02x (seq %d) timed out\n",
|
|
abs(cmd), seq);
|
|
dev->mcu_timeout = 1;
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
rxfce = (u32 *)skb->cb;
|
|
if (seq != FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce))
|
|
return -EAGAIN;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_parse_response);
|
|
|
|
int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data,
|
|
int len, bool wait_resp)
|
|
{
|
|
struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
|
|
unsigned long expires = jiffies + HZ;
|
|
struct sk_buff *skb;
|
|
u32 tx_info;
|
|
int ret;
|
|
u8 seq;
|
|
|
|
if (dev->mcu_timeout)
|
|
return -EIO;
|
|
|
|
skb = mt76_mcu_msg_alloc(mdev, data, len);
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
|
|
mutex_lock(&mdev->mcu.mutex);
|
|
|
|
seq = ++mdev->mcu.msg_seq & 0xf;
|
|
if (!seq)
|
|
seq = ++mdev->mcu.msg_seq & 0xf;
|
|
|
|
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);
|
|
|
|
ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, tx_info);
|
|
if (ret)
|
|
goto out;
|
|
|
|
while (wait_resp) {
|
|
skb = mt76_mcu_get_response(&dev->mt76, expires);
|
|
ret = mt76x02_mcu_parse_response(mdev, cmd, skb, seq);
|
|
dev_kfree_skb(skb);
|
|
if (ret != -EAGAIN)
|
|
break;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&mdev->mcu.mutex);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send);
|
|
|
|
int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func,
|
|
u32 val)
|
|
{
|
|
struct {
|
|
__le32 id;
|
|
__le32 value;
|
|
} __packed __aligned(4) msg = {
|
|
.id = cpu_to_le32(func),
|
|
.value = cpu_to_le32(val),
|
|
};
|
|
bool wait = false;
|
|
|
|
if (func != Q_SELECT)
|
|
wait = true;
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, CMD_FUN_SET_OP, &msg,
|
|
sizeof(msg), wait);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select);
|
|
|
|
int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on)
|
|
{
|
|
struct {
|
|
__le32 mode;
|
|
__le32 level;
|
|
} __packed __aligned(4) msg = {
|
|
.mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF),
|
|
.level = cpu_to_le32(0),
|
|
};
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, CMD_POWER_SAVING_OP, &msg,
|
|
sizeof(msg), false);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state);
|
|
|
|
int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param)
|
|
{
|
|
struct {
|
|
__le32 id;
|
|
__le32 value;
|
|
} __packed __aligned(4) msg = {
|
|
.id = cpu_to_le32(type),
|
|
.value = cpu_to_le32(param),
|
|
};
|
|
bool is_mt76x2e = mt76_is_mmio(&dev->mt76) && is_mt76x2(dev);
|
|
int ret;
|
|
|
|
if (is_mt76x2e)
|
|
mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0);
|
|
|
|
ret = mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg,
|
|
sizeof(msg), true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (is_mt76x2e &&
|
|
WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0,
|
|
BIT(31), BIT(31), 100)))
|
|
return -ETIMEDOUT;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate);
|
|
|
|
int mt76x02_mcu_cleanup(struct mt76x02_dev *dev)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
mt76_wr(dev, MT_MCU_INT_LEVEL, 1);
|
|
usleep_range(20000, 30000);
|
|
|
|
while ((skb = skb_dequeue(&dev->mt76.mcu.res_q)) != NULL)
|
|
dev_kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup);
|
|
|
|
void mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev,
|
|
const struct mt76x02_fw_header *h)
|
|
{
|
|
u16 bld = le16_to_cpu(h->build_ver);
|
|
u16 ver = le16_to_cpu(h->fw_ver);
|
|
|
|
snprintf(dev->mt76.hw->wiphy->fw_version,
|
|
sizeof(dev->mt76.hw->wiphy->fw_version),
|
|
"%d.%d.%02d-b%x",
|
|
(ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver);
|