1 | // SPDX-License-Identifier: ISC |
2 | /* Copyright (C) 2023 MediaTek Inc. |
3 | * |
4 | * Author: Lorenzo Bianconi <lorenzo@kernel.org> |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/usb.h> |
10 | |
11 | #include "mt792x.h" |
12 | #include "mt76_connac2_mac.h" |
13 | |
14 | u32 mt792xu_rr(struct mt76_dev *dev, u32 addr) |
15 | { |
16 | u32 ret; |
17 | |
18 | mutex_lock(&dev->usb.usb_ctrl_mtx); |
19 | ret = ___mt76u_rr(dev, req: MT_VEND_READ_EXT, |
20 | USB_DIR_IN | MT_USB_TYPE_VENDOR, addr); |
21 | mutex_unlock(lock: &dev->usb.usb_ctrl_mtx); |
22 | |
23 | return ret; |
24 | } |
25 | EXPORT_SYMBOL_GPL(mt792xu_rr); |
26 | |
27 | void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val) |
28 | { |
29 | mutex_lock(&dev->usb.usb_ctrl_mtx); |
30 | ___mt76u_wr(dev, req: MT_VEND_WRITE_EXT, |
31 | USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); |
32 | mutex_unlock(lock: &dev->usb.usb_ctrl_mtx); |
33 | } |
34 | EXPORT_SYMBOL_GPL(mt792xu_wr); |
35 | |
36 | u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val) |
37 | { |
38 | mutex_lock(&dev->usb.usb_ctrl_mtx); |
39 | val |= ___mt76u_rr(dev, req: MT_VEND_READ_EXT, |
40 | USB_DIR_IN | MT_USB_TYPE_VENDOR, addr) & ~mask; |
41 | ___mt76u_wr(dev, req: MT_VEND_WRITE_EXT, |
42 | USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); |
43 | mutex_unlock(lock: &dev->usb.usb_ctrl_mtx); |
44 | |
45 | return val; |
46 | } |
47 | EXPORT_SYMBOL_GPL(mt792xu_rmw); |
48 | |
49 | void mt792xu_copy(struct mt76_dev *dev, u32 offset, const void *data, int len) |
50 | { |
51 | struct mt76_usb *usb = &dev->usb; |
52 | int ret, i = 0, batch_len; |
53 | const u8 *val = data; |
54 | |
55 | len = round_up(len, 4); |
56 | |
57 | mutex_lock(&usb->usb_ctrl_mtx); |
58 | while (i < len) { |
59 | batch_len = min_t(int, usb->data_len, len - i); |
60 | memcpy(usb->data, val + i, batch_len); |
61 | ret = __mt76u_vendor_request(dev, req: MT_VEND_WRITE_EXT, |
62 | USB_DIR_OUT | MT_USB_TYPE_VENDOR, |
63 | val: (offset + i) >> 16, offset: offset + i, |
64 | buf: usb->data, len: batch_len); |
65 | if (ret < 0) |
66 | break; |
67 | |
68 | i += batch_len; |
69 | } |
70 | mutex_unlock(lock: &usb->usb_ctrl_mtx); |
71 | } |
72 | EXPORT_SYMBOL_GPL(mt792xu_copy); |
73 | |
74 | int mt792xu_mcu_power_on(struct mt792x_dev *dev) |
75 | { |
76 | int ret; |
77 | |
78 | ret = mt76u_vendor_request(dev: &dev->mt76, req: MT_VEND_POWER_ON, |
79 | USB_DIR_OUT | MT_USB_TYPE_VENDOR, |
80 | val: 0x0, offset: 0x1, NULL, len: 0); |
81 | if (ret) |
82 | return ret; |
83 | |
84 | if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, |
85 | MT_TOP_MISC2_FW_PWR_ON, 500)) { |
86 | dev_err(dev->mt76.dev, "Timeout for power on\n" ); |
87 | ret = -EIO; |
88 | } |
89 | |
90 | return ret; |
91 | } |
92 | EXPORT_SYMBOL_GPL(mt792xu_mcu_power_on); |
93 | |
94 | static void mt792xu_cleanup(struct mt792x_dev *dev) |
95 | { |
96 | clear_bit(nr: MT76_STATE_INITIALIZED, addr: &dev->mphy.state); |
97 | mt792xu_wfsys_reset(dev); |
98 | skb_queue_purge(list: &dev->mt76.mcu.res_q); |
99 | mt76u_queues_deinit(dev: &dev->mt76); |
100 | } |
101 | |
102 | static u32 mt792xu_uhw_rr(struct mt76_dev *dev, u32 addr) |
103 | { |
104 | u32 ret; |
105 | |
106 | mutex_lock(&dev->usb.usb_ctrl_mtx); |
107 | ret = ___mt76u_rr(dev, req: MT_VEND_DEV_MODE, |
108 | USB_DIR_IN | MT_USB_TYPE_UHW_VENDOR, addr); |
109 | mutex_unlock(lock: &dev->usb.usb_ctrl_mtx); |
110 | |
111 | return ret; |
112 | } |
113 | |
114 | static void mt792xu_uhw_wr(struct mt76_dev *dev, u32 addr, u32 val) |
115 | { |
116 | mutex_lock(&dev->usb.usb_ctrl_mtx); |
117 | ___mt76u_wr(dev, req: MT_VEND_WRITE, |
118 | USB_DIR_OUT | MT_USB_TYPE_UHW_VENDOR, addr, val); |
119 | mutex_unlock(lock: &dev->usb.usb_ctrl_mtx); |
120 | } |
121 | |
122 | static void mt792xu_dma_prefetch(struct mt792x_dev *dev) |
123 | { |
124 | #define DMA_PREFETCH_CONF(_idx_, _cnt_, _base_) \ |
125 | mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL((_idx_)), \ |
126 | MT_WPDMA0_MAX_CNT_MASK | MT_WPDMA0_BASE_PTR_MASK, \ |
127 | FIELD_PREP(MT_WPDMA0_MAX_CNT_MASK, (_cnt_)) | \ |
128 | FIELD_PREP(MT_WPDMA0_BASE_PTR_MASK, (_base_))) |
129 | |
130 | DMA_PREFETCH_CONF(0, 4, 0x080); |
131 | DMA_PREFETCH_CONF(1, 4, 0x0c0); |
132 | DMA_PREFETCH_CONF(2, 4, 0x100); |
133 | DMA_PREFETCH_CONF(3, 4, 0x140); |
134 | DMA_PREFETCH_CONF(4, 4, 0x180); |
135 | DMA_PREFETCH_CONF(16, 4, 0x280); |
136 | DMA_PREFETCH_CONF(17, 4, 0x2c0); |
137 | } |
138 | |
139 | static void mt792xu_wfdma_init(struct mt792x_dev *dev) |
140 | { |
141 | int i; |
142 | |
143 | mt792xu_dma_prefetch(dev); |
144 | |
145 | mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_OMIT_RX_INFO); |
146 | mt76_set(dev, MT_UWFDMA0_GLO_CFG, |
147 | MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | |
148 | MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 | |
149 | MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL | |
150 | MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
151 | MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
152 | |
153 | mt76_rmw(dev, MT_DMASHDL_REFILL, MT_DMASHDL_REFILL_MASK, 0xffe00000); |
154 | mt76_clear(dev, MT_DMASHDL_PAGE, MT_DMASHDL_GROUP_SEQ_ORDER); |
155 | mt76_rmw(dev, MT_DMASHDL_PKT_MAX_SIZE, |
156 | MT_DMASHDL_PKT_MAX_SIZE_PLE | MT_DMASHDL_PKT_MAX_SIZE_PSE, |
157 | FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PLE, 1) | |
158 | FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PSE, 0)); |
159 | for (i = 0; i < 5; i++) |
160 | mt76_wr(dev, MT_DMASHDL_GROUP_QUOTA(i), |
161 | FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x3) | |
162 | FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0xfff)); |
163 | for (i = 5; i < 16; i++) |
164 | mt76_wr(dev, MT_DMASHDL_GROUP_QUOTA(i), |
165 | FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x0) | |
166 | FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x0)); |
167 | mt76_wr(dev, MT_DMASHDL_Q_MAP(0), 0x32013201); |
168 | mt76_wr(dev, MT_DMASHDL_Q_MAP(1), 0x32013201); |
169 | mt76_wr(dev, MT_DMASHDL_Q_MAP(2), 0x55555444); |
170 | mt76_wr(dev, MT_DMASHDL_Q_MAP(3), 0x55555444); |
171 | |
172 | mt76_wr(dev, MT_DMASHDL_SCHED_SET(0), 0x76540132); |
173 | mt76_wr(dev, MT_DMASHDL_SCHED_SET(1), 0xFEDCBA98); |
174 | |
175 | mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); |
176 | } |
177 | |
178 | static int mt792xu_dma_rx_evt_ep4(struct mt792x_dev *dev) |
179 | { |
180 | if (!mt76_poll(dev, MT_UWFDMA0_GLO_CFG, |
181 | MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000)) |
182 | return -ETIMEDOUT; |
183 | |
184 | mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
185 | mt76_set(dev, MT_WFDMA_HOST_CONFIG, |
186 | MT_WFDMA_HOST_CONFIG_USB_RXEVT_EP4_EN); |
187 | mt76_set(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | static void mt792xu_epctl_rst_opt(struct mt792x_dev *dev, bool reset) |
193 | { |
194 | u32 val; |
195 | |
196 | /* usb endpoint reset opt |
197 | * bits[4,9]: out blk ep 4-9 |
198 | * bits[20,21]: in blk ep 4-5 |
199 | * bits[22]: in int ep 6 |
200 | */ |
201 | val = mt792xu_uhw_rr(dev: &dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT); |
202 | if (reset) |
203 | val |= GENMASK(9, 4) | GENMASK(22, 20); |
204 | else |
205 | val &= ~(GENMASK(9, 4) | GENMASK(22, 20)); |
206 | mt792xu_uhw_wr(dev: &dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); |
207 | } |
208 | |
209 | int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) |
210 | { |
211 | int err; |
212 | |
213 | mt792xu_wfdma_init(dev); |
214 | |
215 | mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); |
216 | |
217 | mt76_set(dev, MT_UDMA_WLCFG_0, |
218 | MT_WL_RX_EN | MT_WL_TX_EN | |
219 | MT_WL_RX_MPSZ_PAD0 | MT_TICK_1US_EN); |
220 | mt76_clear(dev, MT_UDMA_WLCFG_0, |
221 | MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT); |
222 | mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT); |
223 | |
224 | if (resume) |
225 | return 0; |
226 | |
227 | err = mt792xu_dma_rx_evt_ep4(dev); |
228 | if (err) |
229 | return err; |
230 | |
231 | mt792xu_epctl_rst_opt(dev, reset: false); |
232 | |
233 | return 0; |
234 | } |
235 | EXPORT_SYMBOL_GPL(mt792xu_dma_init); |
236 | |
237 | int mt792xu_wfsys_reset(struct mt792x_dev *dev) |
238 | { |
239 | u32 val; |
240 | int i; |
241 | |
242 | mt792xu_epctl_rst_opt(dev, reset: false); |
243 | |
244 | val = mt792xu_uhw_rr(dev: &dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); |
245 | val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; |
246 | mt792xu_uhw_wr(dev: &dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); |
247 | |
248 | usleep_range(min: 10, max: 20); |
249 | |
250 | val = mt792xu_uhw_rr(dev: &dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); |
251 | val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; |
252 | mt792xu_uhw_wr(dev: &dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); |
253 | |
254 | mt792xu_uhw_wr(dev: &dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, val: 0); |
255 | for (i = 0; i < MT792x_WFSYS_INIT_RETRY_COUNT; i++) { |
256 | val = mt792xu_uhw_rr(dev: &dev->mt76, MT_UDMA_CONN_INFRA_STATUS); |
257 | if (val & MT_UDMA_CONN_WFSYS_INIT_DONE) |
258 | break; |
259 | |
260 | msleep(msecs: 100); |
261 | } |
262 | |
263 | if (i == MT792x_WFSYS_INIT_RETRY_COUNT) |
264 | return -ETIMEDOUT; |
265 | |
266 | return 0; |
267 | } |
268 | EXPORT_SYMBOL_GPL(mt792xu_wfsys_reset); |
269 | |
270 | int mt792xu_init_reset(struct mt792x_dev *dev) |
271 | { |
272 | set_bit(nr: MT76_RESET, addr: &dev->mphy.state); |
273 | |
274 | wake_up(&dev->mt76.mcu.wait); |
275 | skb_queue_purge(list: &dev->mt76.mcu.res_q); |
276 | |
277 | mt76u_stop_rx(dev: &dev->mt76); |
278 | mt76u_stop_tx(dev: &dev->mt76); |
279 | |
280 | mt792xu_wfsys_reset(dev); |
281 | |
282 | clear_bit(nr: MT76_RESET, addr: &dev->mphy.state); |
283 | |
284 | return mt76u_resume_rx(dev: &dev->mt76); |
285 | } |
286 | EXPORT_SYMBOL_GPL(mt792xu_init_reset); |
287 | |
288 | void mt792xu_stop(struct ieee80211_hw *hw) |
289 | { |
290 | struct mt792x_dev *dev = mt792x_hw_dev(hw); |
291 | |
292 | mt76u_stop_tx(dev: &dev->mt76); |
293 | mt792x_stop(hw); |
294 | } |
295 | EXPORT_SYMBOL_GPL(mt792xu_stop); |
296 | |
297 | void mt792xu_disconnect(struct usb_interface *usb_intf) |
298 | { |
299 | struct mt792x_dev *dev = usb_get_intfdata(intf: usb_intf); |
300 | |
301 | cancel_work_sync(work: &dev->init_work); |
302 | if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) |
303 | return; |
304 | |
305 | mt76_unregister_device(dev: &dev->mt76); |
306 | mt792xu_cleanup(dev); |
307 | |
308 | usb_set_intfdata(intf: usb_intf, NULL); |
309 | usb_put_dev(interface_to_usbdev(usb_intf)); |
310 | |
311 | mt76_free_device(dev: &dev->mt76); |
312 | } |
313 | EXPORT_SYMBOL_GPL(mt792xu_disconnect); |
314 | |
315 | MODULE_DESCRIPTION("MediaTek MT792x USB helpers" ); |
316 | MODULE_LICENSE("Dual BSD/GPL" ); |
317 | MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>" ); |
318 | |