mt76x02_mcu.c (3946B)
1// SPDX-License-Identifier: ISC 2/* 3 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 4 * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 5 */ 6 7#include <linux/kernel.h> 8#include <linux/firmware.h> 9#include <linux/delay.h> 10 11#include "mt76x02_mcu.h" 12 13int mt76x02_mcu_parse_response(struct mt76_dev *mdev, int cmd, 14 struct sk_buff *skb, int seq) 15{ 16 struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); 17 u32 *rxfce; 18 19 if (!skb) { 20 dev_err(mdev->dev, "MCU message %02x (seq %d) timed out\n", 21 abs(cmd), seq); 22 dev->mcu_timeout = 1; 23 return -ETIMEDOUT; 24 } 25 26 rxfce = (u32 *)skb->cb; 27 if (seq != FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) 28 return -EAGAIN; 29 30 return 0; 31} 32EXPORT_SYMBOL_GPL(mt76x02_mcu_parse_response); 33 34int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, 35 int len, bool wait_resp) 36{ 37 struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); 38 unsigned long expires = jiffies + HZ; 39 struct sk_buff *skb; 40 u32 tx_info; 41 int ret; 42 u8 seq; 43 44 if (dev->mcu_timeout) 45 return -EIO; 46 47 skb = mt76_mcu_msg_alloc(mdev, data, len); 48 if (!skb) 49 return -ENOMEM; 50 51 mutex_lock(&mdev->mcu.mutex); 52 53 seq = ++mdev->mcu.msg_seq & 0xf; 54 if (!seq) 55 seq = ++mdev->mcu.msg_seq & 0xf; 56 57 tx_info = MT_MCU_MSG_TYPE_CMD | 58 FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | 59 FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | 60 FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | 61 FIELD_PREP(MT_MCU_MSG_LEN, skb->len); 62 63 ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, tx_info); 64 if (ret) 65 goto out; 66 67 while (wait_resp) { 68 skb = mt76_mcu_get_response(&dev->mt76, expires); 69 ret = mt76x02_mcu_parse_response(mdev, cmd, skb, seq); 70 dev_kfree_skb(skb); 71 if (ret != -EAGAIN) 72 break; 73 } 74 75out: 76 mutex_unlock(&mdev->mcu.mutex); 77 78 return ret; 79} 80EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); 81 82int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func, 83 u32 val) 84{ 85 struct { 86 __le32 id; 87 __le32 value; 88 } __packed __aligned(4) msg = { 89 .id = cpu_to_le32(func), 90 .value = cpu_to_le32(val), 91 }; 92 bool wait = false; 93 94 if (func != Q_SELECT) 95 wait = true; 96 97 return mt76_mcu_send_msg(&dev->mt76, CMD_FUN_SET_OP, &msg, 98 sizeof(msg), wait); 99} 100EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); 101 102int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on) 103{ 104 struct { 105 __le32 mode; 106 __le32 level; 107 } __packed __aligned(4) msg = { 108 .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), 109 .level = cpu_to_le32(0), 110 }; 111 112 return mt76_mcu_send_msg(&dev->mt76, CMD_POWER_SAVING_OP, &msg, 113 sizeof(msg), false); 114} 115EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); 116 117int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param) 118{ 119 struct { 120 __le32 id; 121 __le32 value; 122 } __packed __aligned(4) msg = { 123 .id = cpu_to_le32(type), 124 .value = cpu_to_le32(param), 125 }; 126 bool is_mt76x2e = mt76_is_mmio(&dev->mt76) && is_mt76x2(dev); 127 int ret; 128 129 if (is_mt76x2e) 130 mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0); 131 132 ret = mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg, 133 sizeof(msg), true); 134 if (ret) 135 return ret; 136 137 if (is_mt76x2e && 138 WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, 139 BIT(31), BIT(31), 100))) 140 return -ETIMEDOUT; 141 142 return 0; 143} 144EXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); 145 146int mt76x02_mcu_cleanup(struct mt76x02_dev *dev) 147{ 148 struct sk_buff *skb; 149 150 mt76_wr(dev, MT_MCU_INT_LEVEL, 1); 151 usleep_range(20000, 30000); 152 153 while ((skb = skb_dequeue(&dev->mt76.mcu.res_q)) != NULL) 154 dev_kfree_skb(skb); 155 156 return 0; 157} 158EXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); 159 160void mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev, 161 const struct mt76x02_fw_header *h) 162{ 163 u16 bld = le16_to_cpu(h->build_ver); 164 u16 ver = le16_to_cpu(h->fw_ver); 165 166 snprintf(dev->mt76.hw->wiphy->fw_version, 167 sizeof(dev->mt76.hw->wiphy->fw_version), 168 "%d.%d.%02d-b%x", 169 (ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld); 170} 171EXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver);