162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 462306a36Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/firmware.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "mt76x02_mcu.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciint mt76x02_mcu_parse_response(struct mt76_dev *mdev, int cmd, 1462306a36Sopenharmony_ci struct sk_buff *skb, int seq) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); 1762306a36Sopenharmony_ci u32 *rxfce; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci if (!skb) { 2062306a36Sopenharmony_ci dev_err(mdev->dev, "MCU message %02x (seq %d) timed out\n", 2162306a36Sopenharmony_ci abs(cmd), seq); 2262306a36Sopenharmony_ci dev->mcu_timeout = 1; 2362306a36Sopenharmony_ci return -ETIMEDOUT; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci rxfce = (u32 *)skb->cb; 2762306a36Sopenharmony_ci if (seq != FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) 2862306a36Sopenharmony_ci return -EAGAIN; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_parse_response); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciint mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, 3562306a36Sopenharmony_ci int len, bool wait_resp) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); 3862306a36Sopenharmony_ci unsigned long expires = jiffies + HZ; 3962306a36Sopenharmony_ci struct sk_buff *skb; 4062306a36Sopenharmony_ci u32 tx_info; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci u8 seq; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (dev->mcu_timeout) 4562306a36Sopenharmony_ci return -EIO; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci skb = mt76_mcu_msg_alloc(mdev, data, len); 4862306a36Sopenharmony_ci if (!skb) 4962306a36Sopenharmony_ci return -ENOMEM; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci mutex_lock(&mdev->mcu.mutex); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci seq = ++mdev->mcu.msg_seq & 0xf; 5462306a36Sopenharmony_ci if (!seq) 5562306a36Sopenharmony_ci seq = ++mdev->mcu.msg_seq & 0xf; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci tx_info = MT_MCU_MSG_TYPE_CMD | 5862306a36Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | 5962306a36Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | 6062306a36Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | 6162306a36Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_LEN, skb->len); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, tx_info); 6462306a36Sopenharmony_ci if (ret) 6562306a36Sopenharmony_ci goto out; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while (wait_resp) { 6862306a36Sopenharmony_ci skb = mt76_mcu_get_response(&dev->mt76, expires); 6962306a36Sopenharmony_ci ret = mt76x02_mcu_parse_response(mdev, cmd, skb, seq); 7062306a36Sopenharmony_ci dev_kfree_skb(skb); 7162306a36Sopenharmony_ci if (ret != -EAGAIN) 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciout: 7662306a36Sopenharmony_ci mutex_unlock(&mdev->mcu.mutex); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ciint mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func, 8362306a36Sopenharmony_ci u32 val) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct { 8662306a36Sopenharmony_ci __le32 id; 8762306a36Sopenharmony_ci __le32 value; 8862306a36Sopenharmony_ci } __packed __aligned(4) msg = { 8962306a36Sopenharmony_ci .id = cpu_to_le32(func), 9062306a36Sopenharmony_ci .value = cpu_to_le32(val), 9162306a36Sopenharmony_ci }; 9262306a36Sopenharmony_ci bool wait = false; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (func != Q_SELECT) 9562306a36Sopenharmony_ci wait = true; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return mt76_mcu_send_msg(&dev->mt76, CMD_FUN_SET_OP, &msg, 9862306a36Sopenharmony_ci sizeof(msg), wait); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct { 10562306a36Sopenharmony_ci __le32 mode; 10662306a36Sopenharmony_ci __le32 level; 10762306a36Sopenharmony_ci } __packed __aligned(4) msg = { 10862306a36Sopenharmony_ci .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), 10962306a36Sopenharmony_ci .level = cpu_to_le32(0), 11062306a36Sopenharmony_ci }; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return mt76_mcu_send_msg(&dev->mt76, CMD_POWER_SAVING_OP, &msg, 11362306a36Sopenharmony_ci sizeof(msg), false); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct { 12062306a36Sopenharmony_ci __le32 id; 12162306a36Sopenharmony_ci __le32 value; 12262306a36Sopenharmony_ci } __packed __aligned(4) msg = { 12362306a36Sopenharmony_ci .id = cpu_to_le32(type), 12462306a36Sopenharmony_ci .value = cpu_to_le32(param), 12562306a36Sopenharmony_ci }; 12662306a36Sopenharmony_ci bool is_mt76x2e = mt76_is_mmio(&dev->mt76) && is_mt76x2(dev); 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (is_mt76x2e) 13062306a36Sopenharmony_ci mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg, 13362306a36Sopenharmony_ci sizeof(msg), true); 13462306a36Sopenharmony_ci if (ret) 13562306a36Sopenharmony_ci return ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (is_mt76x2e && 13862306a36Sopenharmony_ci WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, 13962306a36Sopenharmony_ci BIT(31), BIT(31), 100))) 14062306a36Sopenharmony_ci return -ETIMEDOUT; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciint mt76x02_mcu_cleanup(struct mt76x02_dev *dev) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct sk_buff *skb; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci mt76_wr(dev, MT_MCU_INT_LEVEL, 1); 15162306a36Sopenharmony_ci usleep_range(20000, 30000); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci while ((skb = skb_dequeue(&dev->mt76.mcu.res_q)) != NULL) 15462306a36Sopenharmony_ci dev_kfree_skb(skb); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev, 16162306a36Sopenharmony_ci const struct mt76x02_fw_header *h) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci u16 bld = le16_to_cpu(h->build_ver); 16462306a36Sopenharmony_ci u16 ver = le16_to_cpu(h->fw_ver); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci snprintf(dev->mt76.hw->wiphy->fw_version, 16762306a36Sopenharmony_ci sizeof(dev->mt76.hw->wiphy->fw_version), 16862306a36Sopenharmony_ci "%d.%d.%02d-b%x", 16962306a36Sopenharmony_ci (ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver); 172