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 | |
13 | int 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 | } |
32 | EXPORT_SYMBOL_GPL(mt76x02_mcu_parse_response); |
33 | |
34 | int 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(dev: mdev, data, data_len: 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: &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 | |
75 | out: |
76 | mutex_unlock(lock: &mdev->mcu.mutex); |
77 | |
78 | return ret; |
79 | } |
80 | EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); |
81 | |
82 | int 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: &dev->mt76, cmd: CMD_FUN_SET_OP, data: &msg, |
98 | len: sizeof(msg), wait_resp: wait); |
99 | } |
100 | EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); |
101 | |
102 | int 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: &dev->mt76, cmd: CMD_POWER_SAVING_OP, data: &msg, |
113 | len: sizeof(msg), wait_resp: false); |
114 | } |
115 | EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); |
116 | |
117 | int 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: &dev->mt76, cmd: CMD_CALIBRATION_OP, data: &msg, |
133 | len: sizeof(msg), wait_resp: 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 | } |
144 | EXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); |
145 | |
146 | int 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(min: 20000, max: 30000); |
152 | |
153 | while ((skb = skb_dequeue(list: &dev->mt76.mcu.res_q)) != NULL) |
154 | dev_kfree_skb(skb); |
155 | |
156 | return 0; |
157 | } |
158 | EXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); |
159 | |
160 | void 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(buf: dev->mt76.hw->wiphy->fw_version, |
167 | size: sizeof(dev->mt76.hw->wiphy->fw_version), |
168 | fmt: "%d.%d.%02d-b%x" , |
169 | (ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld); |
170 | } |
171 | EXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver); |
172 | |