mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 05:16:57 +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]>
256 lines
6.2 KiB
C
256 lines
6.2 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (C) 2018 Lorenzo Bianconi <[email protected]>
|
|
*/
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#include "mt76x2u.h"
|
|
#include "eeprom.h"
|
|
#include "../mt76x02_usb.h"
|
|
|
|
#define MT_CMD_HDR_LEN 4
|
|
|
|
#define MCU_FW_URB_MAX_PAYLOAD 0x3900
|
|
#define MCU_ROM_PATCH_MAX_PAYLOAD 2048
|
|
|
|
#define MT76U_MCU_ILM_OFFSET 0x80000
|
|
#define MT76U_MCU_DLM_OFFSET 0x110000
|
|
#define MT76U_MCU_ROM_PATCH_OFFSET 0x90000
|
|
|
|
static void mt76x2u_mcu_load_ivb(struct mt76x02_dev *dev)
|
|
{
|
|
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x12, 0, NULL, 0);
|
|
}
|
|
|
|
static void mt76x2u_mcu_enable_patch(struct mt76x02_dev *dev)
|
|
{
|
|
struct mt76_usb *usb = &dev->mt76.usb;
|
|
static const u8 data[] = {
|
|
0x6f, 0xfc, 0x08, 0x01,
|
|
0x20, 0x04, 0x00, 0x00,
|
|
0x00, 0x09, 0x00,
|
|
};
|
|
|
|
memcpy(usb->data, data, sizeof(data));
|
|
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
|
|
USB_DIR_OUT | USB_TYPE_CLASS,
|
|
0x12, 0, usb->data, sizeof(data));
|
|
}
|
|
|
|
static void mt76x2u_mcu_reset_wmt(struct mt76x02_dev *dev)
|
|
{
|
|
struct mt76_usb *usb = &dev->mt76.usb;
|
|
u8 data[] = {
|
|
0x6f, 0xfc, 0x05, 0x01,
|
|
0x07, 0x01, 0x00, 0x04
|
|
};
|
|
|
|
memcpy(usb->data, data, sizeof(data));
|
|
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
|
|
USB_DIR_OUT | USB_TYPE_CLASS,
|
|
0x12, 0, usb->data, sizeof(data));
|
|
}
|
|
|
|
static int mt76x2u_mcu_load_rom_patch(struct mt76x02_dev *dev)
|
|
{
|
|
bool rom_protect = !is_mt7612(dev);
|
|
struct mt76x02_patch_header *hdr;
|
|
u32 val, patch_mask, patch_reg;
|
|
const struct firmware *fw;
|
|
int err;
|
|
|
|
if (rom_protect &&
|
|
!mt76_poll_msec(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");
|
|
return 0;
|
|
}
|
|
|
|
err = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (!fw || !fw->data || fw->size <= sizeof(*hdr)) {
|
|
dev_err(dev->mt76.dev, "failed to load firmware\n");
|
|
err = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
hdr = (struct mt76x02_patch_header *)fw->data;
|
|
dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time);
|
|
|
|
/* enable USB_DMA_CFG */
|
|
val = MT_USB_DMA_CFG_RX_BULK_EN |
|
|
MT_USB_DMA_CFG_TX_BULK_EN |
|
|
FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20);
|
|
mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val);
|
|
|
|
/* vendor reset */
|
|
mt76x02u_mcu_fw_reset(dev);
|
|
usleep_range(5000, 10000);
|
|
|
|
/* enable FCE to send in-band cmd */
|
|
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
|
|
/* FCE tx_fs_base_ptr */
|
|
mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
|
|
/* FCE tx_fs_max_cnt */
|
|
mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1);
|
|
/* FCE pdma enable */
|
|
mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
|
|
/* FCE skip_fs_en */
|
|
mt76_wr(dev, MT_FCE_SKIP_FS, 0x3);
|
|
|
|
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr),
|
|
fw->size - sizeof(*hdr),
|
|
MCU_ROM_PATCH_MAX_PAYLOAD,
|
|
MT76U_MCU_ROM_PATCH_OFFSET);
|
|
if (err < 0) {
|
|
err = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
mt76x2u_mcu_enable_patch(dev);
|
|
mt76x2u_mcu_reset_wmt(dev);
|
|
mdelay(20);
|
|
|
|
if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 100)) {
|
|
dev_err(dev->mt76.dev, "failed to load ROM patch\n");
|
|
err = -ETIMEDOUT;
|
|
}
|
|
|
|
out:
|
|
if (rom_protect)
|
|
mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1);
|
|
release_firmware(fw);
|
|
return err;
|
|
}
|
|
|
|
static int mt76x2u_mcu_load_firmware(struct mt76x02_dev *dev)
|
|
{
|
|
u32 val, dlm_offset = MT76U_MCU_DLM_OFFSET;
|
|
const struct mt76x02_fw_header *hdr;
|
|
int err, len, ilm_len, dlm_len;
|
|
const struct firmware *fw;
|
|
|
|
err = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
hdr = (const struct mt76x02_fw_header *)fw->data;
|
|
ilm_len = le32_to_cpu(hdr->ilm_len);
|
|
dlm_len = le32_to_cpu(hdr->dlm_len);
|
|
len = sizeof(*hdr) + ilm_len + dlm_len;
|
|
if (fw->size != len) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
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);
|
|
|
|
/* vendor reset */
|
|
mt76x02u_mcu_fw_reset(dev);
|
|
usleep_range(5000, 10000);
|
|
|
|
/* enable USB_DMA_CFG */
|
|
val = MT_USB_DMA_CFG_RX_BULK_EN |
|
|
MT_USB_DMA_CFG_TX_BULK_EN |
|
|
FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20);
|
|
mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val);
|
|
/* enable FCE to send in-band cmd */
|
|
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
|
|
/* FCE tx_fs_base_ptr */
|
|
mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
|
|
/* FCE tx_fs_max_cnt */
|
|
mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1);
|
|
/* FCE pdma enable */
|
|
mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
|
|
/* FCE skip_fs_en */
|
|
mt76_wr(dev, MT_FCE_SKIP_FS, 0x3);
|
|
|
|
/* load ILM */
|
|
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr),
|
|
ilm_len, MCU_FW_URB_MAX_PAYLOAD,
|
|
MT76U_MCU_ILM_OFFSET);
|
|
if (err < 0) {
|
|
err = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* load DLM */
|
|
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
|
|
dlm_offset += 0x800;
|
|
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr) + ilm_len,
|
|
dlm_len, MCU_FW_URB_MAX_PAYLOAD,
|
|
dlm_offset);
|
|
if (err < 0) {
|
|
err = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
mt76x2u_mcu_load_ivb(dev);
|
|
if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 100)) {
|
|
dev_err(dev->mt76.dev, "firmware failed to start\n");
|
|
err = -ETIMEDOUT;
|
|
goto out;
|
|
}
|
|
|
|
mt76_set(dev, MT_MCU_COM_REG0, BIT(1));
|
|
/* enable FCE to send in-band cmd */
|
|
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
|
|
mt76x02_set_ethtool_fwver(dev, hdr);
|
|
dev_dbg(dev->mt76.dev, "firmware running\n");
|
|
|
|
out:
|
|
release_firmware(fw);
|
|
return err;
|
|
}
|
|
|
|
int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev)
|
|
{
|
|
int err;
|
|
|
|
err = mt76x2u_mcu_load_rom_patch(dev);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return mt76x2u_mcu_load_firmware(dev);
|
|
}
|
|
|
|
int mt76x2u_mcu_init(struct mt76x02_dev *dev)
|
|
{
|
|
int err;
|
|
|
|
err = mt76x02_mcu_function_select(dev, Q_SELECT, 1);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return mt76x02_mcu_set_radio_state(dev, true);
|
|
}
|