Commit | Line | Data |
---|---|---|
c948b5da DW |
1 | // SPDX-License-Identifier: ISC |
2 | /* Copyright (C) 2023 MediaTek Inc. */ | |
3 | ||
4 | #include <linux/kernel.h> | |
5 | #include <linux/module.h> | |
6 | #include <linux/usb.h> | |
7 | ||
8 | #include "mt7925.h" | |
9 | #include "mcu.h" | |
10 | #include "mac.h" | |
11 | ||
12 | static const struct usb_device_id mt7925u_device_table[] = { | |
13 | { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff), | |
14 | .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, | |
15 | { }, | |
16 | }; | |
17 | ||
18 | static int | |
19 | mt7925u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, | |
20 | int cmd, int *seq) | |
21 | { | |
22 | struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); | |
23 | u32 pad, ep; | |
24 | int ret; | |
25 | ||
26 | ret = mt7925_mcu_fill_message(mdev, skb, cmd, seq); | |
27 | if (ret) | |
28 | return ret; | |
29 | ||
30 | mdev->mcu.timeout = 3 * HZ; | |
31 | ||
32 | if (cmd != MCU_CMD(FW_SCATTER)) | |
33 | ep = MT_EP_OUT_INBAND_CMD; | |
34 | else | |
35 | ep = MT_EP_OUT_AC_BE; | |
36 | ||
4d2cb56a | 37 | mt792x_skb_add_usb_sdio_hdr(dev, skb, 0); |
c948b5da DW |
38 | pad = round_up(skb->len, 4) + 4 - skb->len; |
39 | __skb_put_zero(skb, pad); | |
40 | ||
41 | ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL, | |
42 | 1000, ep); | |
43 | dev_kfree_skb(skb); | |
44 | ||
45 | return ret; | |
46 | } | |
47 | ||
48 | static int mt7925u_mcu_init(struct mt792x_dev *dev) | |
49 | { | |
50 | static const struct mt76_mcu_ops mcu_ops = { | |
51 | .headroom = MT_SDIO_HDR_SIZE + | |
52 | sizeof(struct mt76_connac2_mcu_txd), | |
53 | .tailroom = MT_USB_TAIL_SIZE, | |
54 | .mcu_skb_send_msg = mt7925u_mcu_send_message, | |
55 | .mcu_parse_response = mt7925_mcu_parse_response, | |
56 | }; | |
57 | int ret; | |
58 | ||
59 | dev->mt76.mcu_ops = &mcu_ops; | |
60 | ||
61 | mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); | |
62 | ret = mt7925_run_firmware(dev); | |
63 | if (ret) | |
64 | return ret; | |
65 | ||
66 | set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); | |
67 | mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
c948b5da DW |
72 | static int mt7925u_mac_reset(struct mt792x_dev *dev) |
73 | { | |
74 | int err; | |
75 | ||
76 | mt76_txq_schedule_all(&dev->mphy); | |
77 | mt76_worker_disable(&dev->mt76.tx_worker); | |
78 | ||
79 | set_bit(MT76_RESET, &dev->mphy.state); | |
80 | set_bit(MT76_MCU_RESET, &dev->mphy.state); | |
81 | ||
82 | wake_up(&dev->mt76.mcu.wait); | |
83 | skb_queue_purge(&dev->mt76.mcu.res_q); | |
84 | ||
85 | mt76u_stop_rx(&dev->mt76); | |
86 | mt76u_stop_tx(&dev->mt76); | |
87 | ||
88 | mt792xu_wfsys_reset(dev); | |
89 | ||
90 | clear_bit(MT76_MCU_RESET, &dev->mphy.state); | |
91 | err = mt76u_resume_rx(&dev->mt76); | |
92 | if (err) | |
93 | goto out; | |
94 | ||
95 | err = mt792xu_mcu_power_on(dev); | |
96 | if (err) | |
97 | goto out; | |
98 | ||
99 | err = mt792xu_dma_init(dev, false); | |
100 | if (err) | |
101 | goto out; | |
102 | ||
103 | mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); | |
104 | mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); | |
105 | ||
106 | err = mt7925_run_firmware(dev); | |
107 | if (err) | |
108 | goto out; | |
109 | ||
110 | mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); | |
111 | ||
112 | err = mt7925_mcu_set_eeprom(dev); | |
113 | if (err) | |
114 | goto out; | |
115 | ||
116 | err = mt7925_mac_init(dev); | |
117 | if (err) | |
118 | goto out; | |
119 | ||
120 | err = __mt7925_start(&dev->phy); | |
121 | out: | |
122 | clear_bit(MT76_RESET, &dev->mphy.state); | |
123 | ||
124 | mt76_worker_enable(&dev->mt76.tx_worker); | |
125 | ||
126 | return err; | |
127 | } | |
128 | ||
129 | static int mt7925u_probe(struct usb_interface *usb_intf, | |
130 | const struct usb_device_id *id) | |
131 | { | |
132 | static const struct mt76_driver_ops drv_ops = { | |
133 | .txwi_size = MT_SDIO_TXD_SIZE, | |
134 | .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ | | |
135 | MT_DRV_AMSDU_OFFLOAD, | |
136 | .survey_flags = SURVEY_INFO_TIME_TX | | |
137 | SURVEY_INFO_TIME_RX | | |
138 | SURVEY_INFO_TIME_BSS_RX, | |
139 | .tx_prepare_skb = mt7925_usb_sdio_tx_prepare_skb, | |
140 | .tx_complete_skb = mt7925_usb_sdio_tx_complete_skb, | |
141 | .tx_status_data = mt7925_usb_sdio_tx_status_data, | |
142 | .rx_skb = mt7925_queue_rx_skb, | |
143 | .rx_check = mt7925_rx_check, | |
144 | .sta_add = mt7925_mac_sta_add, | |
145 | .sta_assoc = mt7925_mac_sta_assoc, | |
146 | .sta_remove = mt7925_mac_sta_remove, | |
147 | .update_survey = mt792x_update_channel, | |
148 | }; | |
149 | static const struct mt792x_hif_ops hif_ops = { | |
150 | .mcu_init = mt7925u_mcu_init, | |
151 | .init_reset = mt792xu_init_reset, | |
152 | .reset = mt7925u_mac_reset, | |
153 | }; | |
154 | static struct mt76_bus_ops bus_ops = { | |
155 | .rr = mt792xu_rr, | |
156 | .wr = mt792xu_wr, | |
157 | .rmw = mt792xu_rmw, | |
158 | .read_copy = mt76u_read_copy, | |
159 | .write_copy = mt792xu_copy, | |
160 | .type = MT76_BUS_USB, | |
161 | }; | |
162 | struct usb_device *udev = interface_to_usbdev(usb_intf); | |
163 | struct ieee80211_ops *ops; | |
164 | struct ieee80211_hw *hw; | |
165 | struct mt792x_dev *dev; | |
166 | struct mt76_dev *mdev; | |
167 | u8 features; | |
168 | int ret; | |
169 | ||
170 | ops = mt792x_get_mac80211_ops(&usb_intf->dev, &mt7925_ops, | |
171 | (void *)id->driver_info, &features); | |
172 | if (!ops) | |
173 | return -ENOMEM; | |
174 | ||
5ab7d466 | 175 | ops->stop = mt792xu_stop; |
c948b5da DW |
176 | |
177 | mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); | |
178 | if (!mdev) | |
179 | return -ENOMEM; | |
180 | ||
181 | dev = container_of(mdev, struct mt792x_dev, mt76); | |
182 | dev->fw_features = features; | |
183 | dev->hif_ops = &hif_ops; | |
184 | ||
185 | udev = usb_get_dev(udev); | |
186 | usb_reset_device(udev); | |
187 | ||
188 | usb_set_intfdata(usb_intf, dev); | |
189 | ||
190 | ret = __mt76u_init(mdev, usb_intf, &bus_ops); | |
191 | if (ret < 0) | |
192 | goto error; | |
193 | ||
194 | mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | | |
195 | (mt76_rr(dev, MT_HW_REV) & 0xff); | |
196 | dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); | |
197 | ||
198 | if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { | |
199 | ret = mt792xu_wfsys_reset(dev); | |
200 | if (ret) | |
201 | goto error; | |
202 | } | |
203 | ||
204 | ret = mt792xu_mcu_power_on(dev); | |
205 | if (ret) | |
206 | goto error; | |
207 | ||
208 | ret = mt76u_alloc_mcu_queue(&dev->mt76); | |
209 | if (ret) | |
210 | goto error; | |
211 | ||
212 | ret = mt76u_alloc_queues(&dev->mt76); | |
213 | if (ret) | |
214 | goto error; | |
215 | ||
216 | ret = mt792xu_dma_init(dev, false); | |
217 | if (ret) | |
218 | goto error; | |
219 | ||
220 | hw = mt76_hw(dev); | |
221 | /* check hw sg support in order to enable AMSDU */ | |
222 | hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1; | |
223 | ||
224 | ret = mt7925_register_device(dev); | |
225 | if (ret) | |
226 | goto error; | |
227 | ||
228 | return 0; | |
229 | ||
230 | error: | |
231 | mt76u_queues_deinit(&dev->mt76); | |
232 | ||
233 | usb_set_intfdata(usb_intf, NULL); | |
234 | usb_put_dev(interface_to_usbdev(usb_intf)); | |
235 | ||
236 | mt76_free_device(&dev->mt76); | |
237 | ||
238 | return ret; | |
239 | } | |
240 | ||
241 | #ifdef CONFIG_PM | |
242 | static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state) | |
243 | { | |
244 | struct mt792x_dev *dev = usb_get_intfdata(intf); | |
245 | struct mt76_connac_pm *pm = &dev->pm; | |
246 | int err; | |
247 | ||
248 | pm->suspended = true; | |
249 | flush_work(&dev->reset_work); | |
250 | ||
251 | err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true); | |
252 | if (err) | |
253 | goto failed; | |
254 | ||
255 | mt76u_stop_rx(&dev->mt76); | |
256 | mt76u_stop_tx(&dev->mt76); | |
257 | ||
258 | return 0; | |
259 | ||
260 | failed: | |
261 | pm->suspended = false; | |
262 | ||
263 | if (err < 0) | |
264 | mt792x_reset(&dev->mt76); | |
265 | ||
266 | return err; | |
267 | } | |
268 | ||
269 | static int mt7925u_resume(struct usb_interface *intf) | |
270 | { | |
271 | struct mt792x_dev *dev = usb_get_intfdata(intf); | |
272 | struct mt76_connac_pm *pm = &dev->pm; | |
273 | bool reinit = true; | |
274 | int err, i; | |
275 | ||
276 | for (i = 0; i < 10; i++) { | |
277 | u32 val = mt76_rr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT); | |
278 | ||
279 | if (!(val & MT_WF_SW_SER_TRIGGER_SUSPEND)) { | |
280 | reinit = false; | |
281 | break; | |
282 | } | |
283 | if (val & MT_WF_SW_SER_DONE_SUSPEND) { | |
284 | mt76_wr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT, 0); | |
285 | break; | |
286 | } | |
287 | ||
288 | msleep(20); | |
289 | } | |
290 | ||
291 | if (reinit || mt792x_dma_need_reinit(dev)) { | |
292 | err = mt792xu_dma_init(dev, true); | |
293 | if (err) | |
294 | goto failed; | |
295 | } | |
296 | ||
297 | err = mt76u_resume_rx(&dev->mt76); | |
298 | if (err < 0) | |
299 | goto failed; | |
300 | ||
301 | err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false); | |
302 | failed: | |
303 | pm->suspended = false; | |
304 | ||
305 | if (err < 0) | |
306 | mt792x_reset(&dev->mt76); | |
307 | ||
308 | return err; | |
309 | } | |
310 | #endif /* CONFIG_PM */ | |
311 | ||
312 | MODULE_DEVICE_TABLE(usb, mt7925u_device_table); | |
313 | MODULE_FIRMWARE(MT7925_FIRMWARE_WM); | |
314 | MODULE_FIRMWARE(MT7925_ROM_PATCH); | |
315 | ||
316 | static struct usb_driver mt7925u_driver = { | |
317 | .name = KBUILD_MODNAME, | |
318 | .id_table = mt7925u_device_table, | |
319 | .probe = mt7925u_probe, | |
320 | .disconnect = mt792xu_disconnect, | |
321 | #ifdef CONFIG_PM | |
322 | .suspend = mt7925u_suspend, | |
323 | .resume = mt7925u_resume, | |
324 | .reset_resume = mt7925u_resume, | |
325 | #endif /* CONFIG_PM */ | |
326 | .soft_unbind = 1, | |
327 | .disable_hub_initiated_lpm = 1, | |
328 | }; | |
329 | module_usb_driver(mt7925u_driver); | |
330 | ||
331 | MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); | |
f3f8f050 | 332 | MODULE_DESCRIPTION("MediaTek MT7925U (USB) wireless driver"); |
c948b5da | 333 | MODULE_LICENSE("Dual BSD/GPL"); |