18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/firmware.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "mt76x02_mcu.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciint mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, 148c2ecf20Sopenharmony_ci int len, bool wait_resp) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); 178c2ecf20Sopenharmony_ci unsigned long expires = jiffies + HZ; 188c2ecf20Sopenharmony_ci struct sk_buff *skb; 198c2ecf20Sopenharmony_ci u32 tx_info; 208c2ecf20Sopenharmony_ci int ret; 218c2ecf20Sopenharmony_ci u8 seq; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (dev->mcu_timeout) 248c2ecf20Sopenharmony_ci return -EIO; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci skb = mt76_mcu_msg_alloc(mdev, data, len); 278c2ecf20Sopenharmony_ci if (!skb) 288c2ecf20Sopenharmony_ci return -ENOMEM; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci mutex_lock(&mdev->mcu.mutex); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci seq = ++mdev->mcu.msg_seq & 0xf; 338c2ecf20Sopenharmony_ci if (!seq) 348c2ecf20Sopenharmony_ci seq = ++mdev->mcu.msg_seq & 0xf; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci tx_info = MT_MCU_MSG_TYPE_CMD | 378c2ecf20Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | 388c2ecf20Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | 398c2ecf20Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | 408c2ecf20Sopenharmony_ci FIELD_PREP(MT_MCU_MSG_LEN, skb->len); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci ret = mt76_tx_queue_skb_raw(dev, MT_TXQ_MCU, skb, tx_info); 438c2ecf20Sopenharmony_ci if (ret) 448c2ecf20Sopenharmony_ci goto out; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci while (wait_resp) { 478c2ecf20Sopenharmony_ci u32 *rxfce; 488c2ecf20Sopenharmony_ci bool check_seq = false; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci skb = mt76_mcu_get_response(&dev->mt76, expires); 518c2ecf20Sopenharmony_ci if (!skb) { 528c2ecf20Sopenharmony_ci dev_err(mdev->dev, 538c2ecf20Sopenharmony_ci "MCU message %d (seq %d) timed out\n", cmd, 548c2ecf20Sopenharmony_ci seq); 558c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 568c2ecf20Sopenharmony_ci dev->mcu_timeout = 1; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci rxfce = (u32 *)skb->cb; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) 638c2ecf20Sopenharmony_ci check_seq = true; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 668c2ecf20Sopenharmony_ci if (check_seq) 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciout: 718c2ecf20Sopenharmony_ci mutex_unlock(&mdev->mcu.mutex); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciint mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func, 788c2ecf20Sopenharmony_ci u32 val) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct { 818c2ecf20Sopenharmony_ci __le32 id; 828c2ecf20Sopenharmony_ci __le32 value; 838c2ecf20Sopenharmony_ci } __packed __aligned(4) msg = { 848c2ecf20Sopenharmony_ci .id = cpu_to_le32(func), 858c2ecf20Sopenharmony_ci .value = cpu_to_le32(val), 868c2ecf20Sopenharmony_ci }; 878c2ecf20Sopenharmony_ci bool wait = false; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (func != Q_SELECT) 908c2ecf20Sopenharmony_ci wait = true; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return mt76_mcu_send_msg(dev, CMD_FUN_SET_OP, &msg, sizeof(msg), wait); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciint mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct { 998c2ecf20Sopenharmony_ci __le32 mode; 1008c2ecf20Sopenharmony_ci __le32 level; 1018c2ecf20Sopenharmony_ci } __packed __aligned(4) msg = { 1028c2ecf20Sopenharmony_ci .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), 1038c2ecf20Sopenharmony_ci .level = cpu_to_le32(0), 1048c2ecf20Sopenharmony_ci }; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return mt76_mcu_send_msg(dev, CMD_POWER_SAVING_OP, &msg, sizeof(msg), 1078c2ecf20Sopenharmony_ci false); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciint mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct { 1148c2ecf20Sopenharmony_ci __le32 id; 1158c2ecf20Sopenharmony_ci __le32 value; 1168c2ecf20Sopenharmony_ci } __packed __aligned(4) msg = { 1178c2ecf20Sopenharmony_ci .id = cpu_to_le32(type), 1188c2ecf20Sopenharmony_ci .value = cpu_to_le32(param), 1198c2ecf20Sopenharmony_ci }; 1208c2ecf20Sopenharmony_ci bool is_mt76x2e = mt76_is_mmio(&dev->mt76) && is_mt76x2(dev); 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (is_mt76x2e) 1248c2ecf20Sopenharmony_ci mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = mt76_mcu_send_msg(dev, CMD_CALIBRATION_OP, &msg, sizeof(msg), 1278c2ecf20Sopenharmony_ci true); 1288c2ecf20Sopenharmony_ci if (ret) 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (is_mt76x2e && 1328c2ecf20Sopenharmony_ci WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1338c2ecf20Sopenharmony_ci BIT(31), BIT(31), 100))) 1348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciint mt76x02_mcu_cleanup(struct mt76x02_dev *dev) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci mt76_wr(dev, MT_MCU_INT_LEVEL, 1); 1458c2ecf20Sopenharmony_ci usleep_range(20000, 30000); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&dev->mt76.mcu.res_q)) != NULL) 1488c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_civoid mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev, 1558c2ecf20Sopenharmony_ci const struct mt76x02_fw_header *h) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u16 bld = le16_to_cpu(h->build_ver); 1588c2ecf20Sopenharmony_ci u16 ver = le16_to_cpu(h->fw_ver); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci snprintf(dev->mt76.hw->wiphy->fw_version, 1618c2ecf20Sopenharmony_ci sizeof(dev->mt76.hw->wiphy->fw_version), 1628c2ecf20Sopenharmony_ci "%d.%d.%02d-b%x", 1638c2ecf20Sopenharmony_ci (ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver); 166