blob: aed5db066b94ca6a6c918248c1d3299251ef335c [file] [log] [blame]
developerd59e4772022-07-14 13:48:49 +08001From ae9d492fc2d9e15e6625187bc0fcbc154fb58e02 Mon Sep 17 00:00:00 2001
developere3d01472022-05-09 14:01:49 +08002From: Bo Jiao <Bo.Jiao@mediatek.com>
developerf64861f2022-06-22 11:44:53 +08003Date: Wed, 22 Jun 2022 10:51:59 +0800
4Subject: [PATCH 1006/1008] mt76: mt7915: add L0.5 system error recovery
developerbd398d52022-06-06 20:53:24 +08005 support
developere3d01472022-05-09 14:01:49 +08006
developere3d01472022-05-09 14:01:49 +08007---
developerd59e4772022-07-14 13:48:49 +08008 .../wireless/mediatek/mt76/mt7915/debugfs.c | 88 ++++--
9 .../net/wireless/mediatek/mt76/mt7915/dma.c | 48 +++
10 .../net/wireless/mediatek/mt76/mt7915/init.c | 8 +-
11 .../net/wireless/mediatek/mt76/mt7915/mac.c | 284 ++++++++++++++----
12 .../net/wireless/mediatek/mt76/mt7915/main.c | 19 +-
13 .../net/wireless/mediatek/mt76/mt7915/mcu.c | 95 +++++-
14 .../net/wireless/mediatek/mt76/mt7915/mcu.h | 3 +-
15 .../net/wireless/mediatek/mt76/mt7915/mmio.c | 8 +-
16 .../wireless/mediatek/mt76/mt7915/mt7915.h | 23 ++
17 .../net/wireless/mediatek/mt76/mt7915/regs.h | 16 +
developerf64861f2022-06-22 11:44:53 +080018 10 files changed, 493 insertions(+), 99 deletions(-)
developere3d01472022-05-09 14:01:49 +080019
20diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
developerd59e4772022-07-14 13:48:49 +080021index 0cfb6068..07de2eef 100644
developere3d01472022-05-09 14:01:49 +080022--- a/mt7915/debugfs.c
23+++ b/mt7915/debugfs.c
developerbd398d52022-06-06 20:53:24 +080024@@ -52,12 +52,17 @@ static ssize_t
developere3d01472022-05-09 14:01:49 +080025 mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
26 size_t count, loff_t *ppos)
27 {
28+#define SER_LEVEL GENMASK(3, 0)
29+#define SER_ACTION GENMASK(11, 8)
30+
31 struct mt7915_phy *phy = file->private_data;
32 struct mt7915_dev *dev = phy->dev;
33- bool ext_phy = phy != &dev->phy;
34+ u8 ser_action, ser_set, set_val;
35+ u8 band_idx = phy->band_idx;
36 char buf[16];
37 int ret = 0;
38 u16 val;
39+ u32 intr;
40
41 if (count >= sizeof(buf))
42 return -EINVAL;
developerbd398d52022-06-06 20:53:24 +080043@@ -73,28 +78,71 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
developere3d01472022-05-09 14:01:49 +080044 if (kstrtou16(buf, 0, &val))
45 return -EINVAL;
46
47- switch (val) {
48+ ser_action = FIELD_GET(SER_ACTION, val);
49+ ser_set = set_val = FIELD_GET(SER_LEVEL, val);
50+
51+ switch (ser_action) {
52 case SER_QUERY:
53 /* grab firmware SER stats */
54- ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy);
55+ ser_set = 0;
56 break;
57- case SER_SET_RECOVER_L1:
58- case SER_SET_RECOVER_L2:
59- case SER_SET_RECOVER_L3_RX_ABORT:
60- case SER_SET_RECOVER_L3_TX_ABORT:
61- case SER_SET_RECOVER_L3_TX_DISABLE:
62- case SER_SET_RECOVER_L3_BF:
63- ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), ext_phy);
64- if (ret)
65- return ret;
66-
67- ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy);
68+ case SER_SET:
69+ /*
70+ * 0x100: disable system error recovery function.
71+ * 0x101: enable system error recovery function.
72+ * 0x103: enable l0.5 recover function.
73+ */
74+ ser_set = !!set_val;
75+
76+ dev->ser.reset_enable = ser_set;
77+ intr = mt76_rr(dev, MT_WFDMA0_MCU_HOST_INT_ENA);
78+ if (dev->ser.reset_enable)
79+ intr |= MT_MCU_CMD_WDT_MASK;
80+ else
81+ intr &= ~MT_MCU_CMD_WDT_MASK;
82+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, intr);
83 break;
84- default:
85+ case SER_ENABLE:
86+ /*
87+ * 0x200: enable system error tracking.
88+ * 0x201: enable system error L1 recover.
89+ * 0x202: enable system error L2 recover.
90+ * 0x203: enable system error L3 rx abort.
91+ * 0x204: enable system error L3 tx abort.
92+ * 0x205: enable system error L3 tx disable.
93+ * 0x206: enable system error L3 bf recover.
94+ * 0x207: enable system error all recover.
95+ */
96+ ser_set = set_val > 7 ? 0x7f : BIT(set_val);
97+ break;
98+ case SER_RECOVER:
99+ /*
100+ * 0x300: trigger L0.5 recover.
101+ * 0x301: trigger L1 recover.
102+ * 0x302: trigger L2 recover.
103+ * 0x303: trigger L3 rx abort.
104+ * 0x304: trigger L3 tx abort
105+ * 0x305: trigger L3 tx disable.
106+ * 0x306: trigger L3 bf recover.
107+ */
108+ if (!ser_set) {
109+ if (dev->ser.reset_enable) {
110+ dev->reset_state |= MT_MCU_CMD_WDT_MASK;
111+ mt7915_reset(dev);
112+ } else {
113+ dev_info(dev->mt76.dev, "SER: chip full recovery not enable\n");
114+ }
115+ goto out;
116+ }
117 break;
118+ default:
119+ goto out;
120 }
121-
122- return ret ? ret : count;
123+ ret = mt7915_mcu_set_ser(dev, ser_action, ser_set, band_idx);
124+ if (ret)
125+ return ret;
126+out:
127+ return count;
128 }
129
130 static ssize_t
developerbd398d52022-06-06 20:53:24 +0800131@@ -143,6 +191,12 @@ mt7915_fw_ser_get(struct file *file, char __user *user_buf,
developere3d01472022-05-09 14:01:49 +0800132 "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n",
133 mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS));
134
135+ desc += scnprintf(buff + desc, bufsz - desc,
136+ "\nWF RESET STATUS: EN %d, WM %d, WA %d\n",
137+ dev->ser.reset_enable,
138+ dev->ser.wf_reset_wm_count,
139+ dev->ser.wf_reset_wa_count);
140+
141 ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
142 kfree(buff);
143 return ret;
144diff --git a/mt7915/dma.c b/mt7915/dma.c
developerd59e4772022-07-14 13:48:49 +0800145index c2d655cd..9e3d14db 100644
developere3d01472022-05-09 14:01:49 +0800146--- a/mt7915/dma.c
147+++ b/mt7915/dma.c
148@@ -486,6 +486,54 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
149 return 0;
150 }
151
152+int mt7915_dma_reset(struct mt7915_dev *dev, bool force)
153+{
154+ struct mt76_phy *mphy_ext = dev->mt76.phy2;
155+ int i;
156+
157+ /* clean up hw queues */
158+ for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) {
159+ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
160+ if (mphy_ext)
161+ mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
162+ }
163+
164+ for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
165+ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
166+
167+ mt76_for_each_q_rx(&dev->mt76, i)
168+ mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
169+
170+ /* reset wfsys */
171+ if (force)
172+ mt7915_wfsys_reset(dev);
173+
174+ /* disable wfdma */
175+ mt7915_dma_disable(dev, force);
176+
177+ /* reset hw queues */
178+ for (i = 0; i < __MT_TXQ_MAX; i++) {
179+ mt76_queue_reset(dev, dev->mphy.q_tx[i]);
180+ if (mphy_ext)
181+ mt76_queue_reset(dev, mphy_ext->q_tx[i]);
182+ }
183+
184+ for (i = 0; i < __MT_MCUQ_MAX; i++)
185+ mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
186+
187+ mt76_for_each_q_rx(&dev->mt76, i)
188+ mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
189+
190+ mt76_tx_status_check(&dev->mt76, true);
191+
192+ mt7915_dma_enable(dev);
193+
194+ mt76_for_each_q_rx(&dev->mt76, i)
195+ mt76_queue_rx_reset(dev, i);
196+
197+ return 0;
198+}
199+
200 void mt7915_dma_cleanup(struct mt7915_dev *dev)
201 {
202 mt7915_dma_disable(dev, true);
203diff --git a/mt7915/init.c b/mt7915/init.c
developerd59e4772022-07-14 13:48:49 +0800204index 02d1b6d8..0d5109a3 100644
developere3d01472022-05-09 14:01:49 +0800205--- a/mt7915/init.c
206+++ b/mt7915/init.c
207@@ -262,7 +262,7 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev,
208 mt7915_led_set_config(led_cdev, 0xff, 0);
209 }
210
211-static void
212+void
213 mt7915_init_txpower(struct mt7915_dev *dev,
214 struct ieee80211_supported_band *sband)
215 {
developerf64861f2022-06-22 11:44:53 +0800216@@ -449,7 +449,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
developere3d01472022-05-09 14:01:49 +0800217 mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
218 }
219
220-static void mt7915_mac_init(struct mt7915_dev *dev)
221+void mt7915_mac_init(struct mt7915_dev *dev)
222 {
223 int i;
224 u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680;
developerf64861f2022-06-22 11:44:53 +0800225@@ -479,7 +479,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
developere3d01472022-05-09 14:01:49 +0800226 }
227 }
228
229-static int mt7915_txbf_init(struct mt7915_dev *dev)
230+int mt7915_txbf_init(struct mt7915_dev *dev)
231 {
232 int ret;
233
developerf64861f2022-06-22 11:44:53 +0800234@@ -1162,6 +1162,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
developere3d01472022-05-09 14:01:49 +0800235
236 mt7915_init_debugfs(&dev->phy);
237
238+ dev->ser.hw_init_done = true;
239+
240 return 0;
241
242 unreg_thermal:
243diff --git a/mt7915/mac.c b/mt7915/mac.c
developerd59e4772022-07-14 13:48:49 +0800244index 24052f47..f13456bf 100644
developere3d01472022-05-09 14:01:49 +0800245--- a/mt7915/mac.c
246+++ b/mt7915/mac.c
247@@ -3,6 +3,7 @@
248
249 #include <linux/etherdevice.h>
250 #include <linux/timekeeping.h>
251+#include <linux/pci.h>
252 #include "mt7915.h"
253 #include "../dma.h"
254 #include "mac.h"
developerd59e4772022-07-14 13:48:49 +0800255@@ -1313,85 +1314,188 @@ mt7915_update_beacons(struct mt7915_dev *dev)
developere3d01472022-05-09 14:01:49 +0800256 mt7915_update_vif_beacon, dev->mt76.phy2->hw);
257 }
258
259-static void
260-mt7915_dma_reset(struct mt7915_dev *dev)
261+void mt7915_tx_token_put(struct mt7915_dev *dev)
262 {
263- struct mt76_phy *mphy_ext = dev->mt76.phy2;
264- u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
265- int i;
266+ struct mt76_txwi_cache *txwi;
267+ int id;
developerf64861f2022-06-22 11:44:53 +0800268+
developere3d01472022-05-09 14:01:49 +0800269+ spin_lock_bh(&dev->mt76.token_lock);
270+ idr_for_each_entry(&dev->mt76.token, txwi, id) {
271+ mt7915_txwi_free(dev, txwi, NULL, NULL);
272+ dev->mt76.token_count--;
273+ }
274+ spin_unlock_bh(&dev->mt76.token_lock);
275+ idr_destroy(&dev->mt76.token);
276+}
developerf64861f2022-06-22 11:44:53 +0800277+
developere3d01472022-05-09 14:01:49 +0800278+static int
279+mt7915_mac_reset(struct mt7915_dev *dev)
280+{
281+ struct mt7915_phy *phy2;
282+ struct mt76_phy *ext_phy;
283+ struct mt76_dev *mdev = &dev->mt76;
284+ int i, ret;
285+ u32 irq_mask;
developerf64861f2022-06-22 11:44:53 +0800286
287- mt76_clear(dev, MT_WFDMA0_GLO_CFG,
288- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
289- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
developere3d01472022-05-09 14:01:49 +0800290+ ext_phy = dev->mt76.phy2;
291+ phy2 = ext_phy ? ext_phy->priv : NULL;
developerf64861f2022-06-22 11:44:53 +0800292
293- if (is_mt7915(&dev->mt76))
294- mt76_clear(dev, MT_WFDMA1_GLO_CFG,
295- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
296- MT_WFDMA1_GLO_CFG_RX_DMA_EN);
developere3d01472022-05-09 14:01:49 +0800297+ /* irq disable */
298+ mt76_wr(dev, MT_INT_MASK_CSR, 0x0);
299+ mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
300 if (dev->hif2) {
301- mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
302- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
303- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
developerf64861f2022-06-22 11:44:53 +0800304-
305- if (is_mt7915(&dev->mt76))
306- mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
307- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
308- MT_WFDMA1_GLO_CFG_RX_DMA_EN);
developere3d01472022-05-09 14:01:49 +0800309+ mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
310+ mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
311+ }
312+ if (dev_is_pci(mdev->dev)) {
313+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
314+ if (dev->hif2)
315+ mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0);
developerf64861f2022-06-22 11:44:53 +0800316 }
developere3d01472022-05-09 14:01:49 +0800317
developerf64861f2022-06-22 11:44:53 +0800318- usleep_range(1000, 2000);
319-
320- for (i = 0; i < __MT_TXQ_MAX; i++) {
321- mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
322- if (mphy_ext)
323- mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
developere3d01472022-05-09 14:01:49 +0800324+ set_bit(MT76_RESET, &dev->mphy.state);
325+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
326+ wake_up(&dev->mt76.mcu.wait);
327+ if (ext_phy) {
328+ set_bit(MT76_RESET, &ext_phy->state);
329+ set_bit(MT76_MCU_RESET, &ext_phy->state);
330 }
331
developerf64861f2022-06-22 11:44:53 +0800332- for (i = 0; i < __MT_MCUQ_MAX; i++)
333- mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
developere3d01472022-05-09 14:01:49 +0800334+ /* lock/unlock all queues to ensure that no tx is pending */
335+ mt76_txq_schedule_all(&dev->mphy);
336+ if (ext_phy)
337+ mt76_txq_schedule_all(ext_phy);
338
developerf64861f2022-06-22 11:44:53 +0800339- mt76_for_each_q_rx(&dev->mt76, i)
340- mt76_queue_rx_reset(dev, i);
developere3d01472022-05-09 14:01:49 +0800341+ /* disable all tx/rx napi */
342+ mt76_worker_disable(&dev->mt76.tx_worker);
343+ mt76_for_each_q_rx(mdev, i) {
344+ if (mdev->q_rx[i].ndesc)
345+ napi_disable(&dev->mt76.napi[i]);
developerf64861f2022-06-22 11:44:53 +0800346+ }
developere3d01472022-05-09 14:01:49 +0800347+ napi_disable(&dev->mt76.tx_napi);
348
developerf64861f2022-06-22 11:44:53 +0800349- mt76_tx_status_check(&dev->mt76, true);
developere3d01472022-05-09 14:01:49 +0800350+ /* token reinit */
351+ mt7915_tx_token_put(dev);
352+ idr_init(&dev->mt76.token);
353
developere3d01472022-05-09 14:01:49 +0800354- /* re-init prefetch settings after reset */
355- mt7915_dma_prefetch(dev);
developerf64861f2022-06-22 11:44:53 +0800356+ mt7915_dma_reset(dev, true);
357
358- mt76_set(dev, MT_WFDMA0_GLO_CFG,
359- MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
360- if (is_mt7915(&dev->mt76))
361- mt76_set(dev, MT_WFDMA1_GLO_CFG,
362- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
363- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
364- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
365- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
developere3d01472022-05-09 14:01:49 +0800366+ local_bh_disable();
367+ mt76_for_each_q_rx(mdev, i) {
368+ if (mdev->q_rx[i].ndesc) {
369+ napi_enable(&dev->mt76.napi[i]);
370+ napi_schedule(&dev->mt76.napi[i]);
371+ }
372+ }
373+ local_bh_enable();
374+ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
375+ clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
developerf64861f2022-06-22 11:44:53 +0800376+
developere3d01472022-05-09 14:01:49 +0800377+ mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask);
378+ mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
379 if (dev->hif2) {
380- mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
381- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
382- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
developerf64861f2022-06-22 11:44:53 +0800383-
384- if (is_mt7915(&dev->mt76))
385- mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
386- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
387- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
388- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
389- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
developere3d01472022-05-09 14:01:49 +0800390+ mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
391+ mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
392+ }
393+ if (dev_is_pci(mdev->dev)) {
394+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
395+ if (dev->hif2)
396+ mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
397+ }
developerf64861f2022-06-22 11:44:53 +0800398+
developere3d01472022-05-09 14:01:49 +0800399+ /* load firmware */
400+ ret = mt7915_run_firmware(dev);
401+ if (ret)
402+ goto out;
403+
404+ /* set the necessary init items */
405+ ret = mt7915_mcu_set_eeprom(dev, dev->flash_mode);
406+ if (ret)
407+ goto out;
408+
409+ mt7915_mac_init(dev);
410+ mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband);
411+ mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband);
412+ ret = mt7915_txbf_init(dev);
developerbd398d52022-06-06 20:53:24 +0800413+
developere3d01472022-05-09 14:01:49 +0800414+ if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
415+ ret = __mt7915_start(dev->mphy.hw);
416+ if (ret)
417+ goto out;
developerbd398d52022-06-06 20:53:24 +0800418 }
developere3d01472022-05-09 14:01:49 +0800419+
420+ if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) {
421+ ret = __mt7915_start(ext_phy->hw);
422+ if (ret)
423+ goto out;
developerbd398d52022-06-06 20:53:24 +0800424+ }
developere3d01472022-05-09 14:01:49 +0800425+
426+out:
427+ /* reset done */
428+ clear_bit(MT76_RESET, &dev->mphy.state);
429+ if (phy2)
430+ clear_bit(MT76_RESET, &phy2->mt76->state);
431+
432+ local_bh_disable();
433+ napi_enable(&dev->mt76.tx_napi);
434+ napi_schedule(&dev->mt76.tx_napi);
435+ local_bh_enable();
436+
437+ mt76_worker_enable(&dev->mt76.tx_worker);
438+
439+ return ret;
440 }
441
442-void mt7915_tx_token_put(struct mt7915_dev *dev)
443+static void
444+mt7915_mac_full_reset(struct mt7915_dev *dev)
445 {
446- struct mt76_txwi_cache *txwi;
447- int id;
448+ struct mt7915_phy *phy2;
449+ struct mt76_phy *ext_phy;
450+ int i;
451
452- spin_lock_bh(&dev->mt76.token_lock);
453- idr_for_each_entry(&dev->mt76.token, txwi, id) {
454- mt7915_txwi_free(dev, txwi, NULL, NULL);
455- dev->mt76.token_count--;
456+ ext_phy = dev->mt76.phy2;
457+ phy2 = ext_phy ? ext_phy->priv : NULL;
458+
459+ dev->ser.hw_full_reset = true;
460+ if (READ_ONCE(dev->reset_state) & MT_MCU_CMD_WA_WDT)
461+ dev->ser.wf_reset_wa_count++;
462+ else
463+ dev->ser.wf_reset_wm_count++;
464+
465+ wake_up(&dev->mt76.mcu.wait);
466+ ieee80211_stop_queues(mt76_hw(dev));
467+ if (ext_phy)
468+ ieee80211_stop_queues(ext_phy->hw);
469+
470+ cancel_delayed_work_sync(&dev->mphy.mac_work);
471+ if (ext_phy)
472+ cancel_delayed_work_sync(&ext_phy->mac_work);
473+
474+ mutex_lock(&dev->mt76.mutex);
475+ for (i = 0; i < 10; i++) {
476+ if (!mt7915_mac_reset(dev))
477+ break;
478 }
479- spin_unlock_bh(&dev->mt76.token_lock);
480- idr_destroy(&dev->mt76.token);
481+ mutex_unlock(&dev->mt76.mutex);
482+
483+ if (i == 10)
484+ dev_err(dev->mt76.dev, "chip full reset failed\n");
485+
486+ ieee80211_restart_hw(mt76_hw(dev));
487+ if (ext_phy)
488+ ieee80211_restart_hw(ext_phy->hw);
489+
490+ ieee80211_wake_queues(mt76_hw(dev));
491+ if (ext_phy)
492+ ieee80211_wake_queues(ext_phy->hw);
493+
494+ dev->ser.hw_full_reset = false;
495+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
496+ MT7915_WATCHDOG_TIME);
497+ if (ext_phy)
498+ ieee80211_queue_delayed_work(ext_phy->hw,
499+ &ext_phy->mac_work,
500+ MT7915_WATCHDOG_TIME);
501 }
502
503 /* system error recovery */
developerd59e4772022-07-14 13:48:49 +0800504@@ -1406,6 +1510,36 @@ void mt7915_mac_reset_work(struct work_struct *work)
developere3d01472022-05-09 14:01:49 +0800505 ext_phy = dev->mt76.phy2;
506 phy2 = ext_phy ? ext_phy->priv : NULL;
507
508+ /* chip full reset */
509+ if (dev->ser.reset_type == SER_TYPE_FULL_RESET) {
510+ u32 intr;
511+
512+ /* disable WA/WM WDT */
513+ intr = mt76_rr(dev, MT_WFDMA0_MCU_HOST_INT_ENA);
514+ intr &= ~MT_MCU_CMD_WDT_MASK;
515+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, intr);
516+
517+ mt7915_mac_full_reset(dev);
518+
519+ /* enable the mcu irq*/
520+ mt7915_irq_enable(dev, MT_INT_MCU_CMD);
521+ mt7915_irq_disable(dev, 0);
522+
523+ /* re-enable WA/WM WDT */
524+ intr = mt76_rr(dev, MT_WFDMA0_MCU_HOST_INT_ENA);
525+ intr |= MT_MCU_CMD_WDT_MASK;
526+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, intr);
527+
528+ dev->reset_state = MT_MCU_CMD_NORMAL_STATE;
529+ dev->ser.reset_type = SER_TYPE_NONE;
530+ dev_info(dev->mt76.dev, "SER: chip full reset completed, WM %d, WA %d\n",
531+ dev->ser.wf_reset_wm_count,
532+ dev->ser.wf_reset_wa_count);
533+ return;
534+ }
535+
536+ /* chip partial reset */
537+ dev->ser.reset_type = SER_TYPE_NONE;
538 if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
539 return;
540
developerd59e4772022-07-14 13:48:49 +0800541@@ -1431,7 +1565,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
developere3d01472022-05-09 14:01:49 +0800542 mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
543
544 if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
545- mt7915_dma_reset(dev);
546+ mt7915_dma_reset(dev, false);
547
548 mt7915_tx_token_put(dev);
549 idr_init(&dev->mt76.token);
developerd59e4772022-07-14 13:48:49 +0800550@@ -1480,6 +1614,34 @@ void mt7915_mac_reset_work(struct work_struct *work)
developere3d01472022-05-09 14:01:49 +0800551 MT7915_WATCHDOG_TIME);
552 }
553
554+void mt7915_reset(struct mt7915_dev *dev)
555+{
556+ if (!dev->ser.hw_init_done)
557+ return;
558+
559+ if (dev->ser.hw_full_reset)
560+ return;
561+
562+ /* wm/wa exception: do full recovery */
563+ if (READ_ONCE(dev->reset_state) & MT_MCU_CMD_WDT_MASK) {
564+ dev_info(dev->mt76.dev, "SER: chip full recovery start, WM %d, WA %d\n",
565+ dev->ser.wf_reset_wm_count,
566+ dev->ser.wf_reset_wa_count);
567+
568+ dev->ser.reset_type = SER_TYPE_FULL_RESET;
569+
570+ mt7915_irq_disable(dev, MT_INT_MCU_CMD);
571+ queue_work(dev->mt76.wq, &dev->reset_work);
572+ return;
573+ }
574+
575+ dev_info(dev->mt76.dev, "SER: chip partial recovery, reset_state(0x%08X)\n",
576+ READ_ONCE(dev->reset_state));
577+ dev->ser.reset_type = SER_TYPE_PARTIAL_RESET;
578+ queue_work(dev->mt76.wq, &dev->reset_work);
579+ wake_up(&dev->reset_wait);
580+}
581+
582 void mt7915_mac_update_stats(struct mt7915_phy *phy)
583 {
584 struct mt7915_dev *dev = phy->dev;
585diff --git a/mt7915/main.c b/mt7915/main.c
developerd59e4772022-07-14 13:48:49 +0800586index d5fb947a..60d990f3 100644
developere3d01472022-05-09 14:01:49 +0800587--- a/mt7915/main.c
588+++ b/mt7915/main.c
589@@ -20,17 +20,13 @@ static bool mt7915_dev_running(struct mt7915_dev *dev)
590 return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
591 }
592
593-static int mt7915_start(struct ieee80211_hw *hw)
594+int __mt7915_start(struct ieee80211_hw *hw)
595 {
596 struct mt7915_dev *dev = mt7915_hw_dev(hw);
597 struct mt7915_phy *phy = mt7915_hw_phy(hw);
598 bool running;
599 int ret;
600
601- flush_work(&dev->init_work);
602-
603- mutex_lock(&dev->mt76.mutex);
604-
605 running = mt7915_dev_running(dev);
606
607 if (!running) {
developer4721e252022-06-21 16:41:28 +0800608@@ -84,6 +80,18 @@ static int mt7915_start(struct ieee80211_hw *hw)
developere3d01472022-05-09 14:01:49 +0800609 mt7915_mac_reset_counters(phy);
610
611 out:
612+ return ret;
613+}
614+
615+static int mt7915_start(struct ieee80211_hw *hw)
616+{
617+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
developere3d01472022-05-09 14:01:49 +0800618+ int ret;
619+
620+ flush_work(&dev->init_work);
621+
622+ mutex_lock(&dev->mt76.mutex);
623+ ret = __mt7915_start(hw);
624 mutex_unlock(&dev->mt76.mutex);
625
626 return ret;
developer4721e252022-06-21 16:41:28 +0800627@@ -95,6 +103,7 @@ static void mt7915_stop(struct ieee80211_hw *hw)
developere3d01472022-05-09 14:01:49 +0800628 struct mt7915_phy *phy = mt7915_hw_phy(hw);
629
630 cancel_delayed_work_sync(&phy->mt76->mac_work);
631+ cancel_work_sync(&dev->reset_work);
632
633 mutex_lock(&dev->mt76.mutex);
634
635diff --git a/mt7915/mcu.c b/mt7915/mcu.c
developerd59e4772022-07-14 13:48:49 +0800636index 53d4cd44..62412c23 100644
developere3d01472022-05-09 14:01:49 +0800637--- a/mt7915/mcu.c
638+++ b/mt7915/mcu.c
developerf64861f2022-06-22 11:44:53 +0800639@@ -150,19 +150,90 @@ mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
developere3d01472022-05-09 14:01:49 +0800640 ht_mcs[nss] = sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
641 }
642
643+static int
644+mt7915_fw_exception_chk(struct mt7915_dev *dev)
645+{
646+ u32 reg_val;
647+
648+ reg_val = mt76_rr(dev, MT_EXCEPTION_ADDR);
649+
650+ if (is_mt7915(&dev->mt76))
651+ reg_val >>= 8;
652+
653+ return !!(reg_val & 0xff);
654+}
655+
656+static void
657+mt7915_fw_heart_beat_chk(struct mt7915_dev *dev)
658+{
659+#define WM_TIMEOUT_COUNT_CHECK 5
660+#define WM_HANG_COUNT_CHECK 9
661+ static u32 cidx_rec[5], didx_rec[5];
662+ u32 cnt, cidx, didx, queue;
663+ u32 idx, i;
664+
665+ if (dev->ser.hw_full_reset)
666+ return;
667+
668+ if (dev->ser.cmd_fail_cnt >= WM_TIMEOUT_COUNT_CHECK) {
669+ cnt = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 4);
670+ cidx = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 8);
671+ didx = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 12);
672+ queue = (didx > cidx) ?
673+ (didx - cidx - 1) : (didx - cidx + cnt - 1);
674+
675+ idx = (dev->ser.cmd_fail_cnt - WM_TIMEOUT_COUNT_CHECK) % 5;
676+ cidx_rec[idx] = cidx;
677+ didx_rec[idx] = didx;
678+
679+ if ((cnt - 1) == queue &&
680+ dev->ser.cmd_fail_cnt >= WM_HANG_COUNT_CHECK) {
681+
682+ for (i = 0; i < 5; i++) {
683+ if (cidx_rec[i] != cidx ||
684+ didx_rec[i] != didx)
685+ return;
686+ }
687+ dev_err(dev->mt76.dev, "detect mem dma hang!\n");
688+ if (dev->ser.reset_enable) {
689+ dev->reset_state |= MT_MCU_CMD_WDT_MASK;
690+ mt7915_reset(dev);
691+ }
692+ dev->ser.cmd_fail_cnt = 0;
693+ }
694+ }
695+}
696+
697 static int
698 mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd,
699 struct sk_buff *skb, int seq)
700 {
701+ struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
developerf64861f2022-06-22 11:44:53 +0800702 struct mt76_connac2_mcu_rxd *rxd;
developere3d01472022-05-09 14:01:49 +0800703 int ret = 0;
704
705 if (!skb) {
706 dev_err(mdev->dev, "Message %08x (seq %d) timeout\n",
707 cmd, seq);
708+
709+ dev->ser.cmd_fail_cnt++;
710+
711+ if (dev->ser.cmd_fail_cnt < 5) {
712+ int exp_type = mt7915_fw_exception_chk(dev);
713+
714+ dev_err(mdev->dev, "Fw is status(%d)\n", exp_type);
715+ if (exp_type && dev->ser.reset_enable) {
716+ dev->reset_state |= MT_MCU_CMD_WDT_MASK;
717+ mt7915_reset(dev);
718+ }
719+ }
720+ mt7915_fw_heart_beat_chk(dev);
721+
722 return -ETIMEDOUT;
723 }
724
725+ dev->ser.cmd_fail_cnt = 0;
726+
developerf64861f2022-06-22 11:44:53 +0800727 rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
developere3d01472022-05-09 14:01:49 +0800728 if (seq != rxd->seq)
729 return -EAGAIN;
developerd59e4772022-07-14 13:48:49 +0800730@@ -2273,18 +2344,10 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
developere3d01472022-05-09 14:01:49 +0800731 sizeof(req), true);
732 }
733
734-int mt7915_mcu_init(struct mt7915_dev *dev)
735+int mt7915_run_firmware(struct mt7915_dev *dev)
736 {
737- static const struct mt76_mcu_ops mt7915_mcu_ops = {
developerf64861f2022-06-22 11:44:53 +0800738- .headroom = sizeof(struct mt76_connac2_mcu_txd),
developere3d01472022-05-09 14:01:49 +0800739- .mcu_skb_send_msg = mt7915_mcu_send_message,
740- .mcu_parse_response = mt7915_mcu_parse_response,
741- .mcu_restart = mt76_connac_mcu_restart,
742- };
743 int ret;
744
745- dev->mt76.mcu_ops = &mt7915_mcu_ops;
746-
747 /* force firmware operation mode into normal state,
748 * which should be set before firmware download stage.
749 */
developerd59e4772022-07-14 13:48:49 +0800750@@ -2333,6 +2396,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev)
developere3d01472022-05-09 14:01:49 +0800751 MCU_WA_PARAM_RED, 0, 0);
752 }
753
754+int mt7915_mcu_init(struct mt7915_dev *dev)
755+{
756+ static const struct mt76_mcu_ops mt7915_mcu_ops = {
developerf64861f2022-06-22 11:44:53 +0800757+ .headroom = sizeof(struct mt76_connac2_mcu_txd),
developere3d01472022-05-09 14:01:49 +0800758+ .mcu_skb_send_msg = mt7915_mcu_send_message,
759+ .mcu_parse_response = mt7915_mcu_parse_response,
760+ .mcu_restart = mt76_connac_mcu_restart,
761+ };
developere3d01472022-05-09 14:01:49 +0800762+
763+ dev->mt76.mcu_ops = &mt7915_mcu_ops;
764+
765+ return mt7915_run_firmware(dev);
766+}
767+
768 void mt7915_mcu_exit(struct mt7915_dev *dev)
769 {
770 __mt76_mcu_restart(&dev->mt76);
771diff --git a/mt7915/mcu.h b/mt7915/mcu.h
developerd59e4772022-07-14 13:48:49 +0800772index aab1a6a3..c4850644 100644
developere3d01472022-05-09 14:01:49 +0800773--- a/mt7915/mcu.h
774+++ b/mt7915/mcu.h
developerd59e4772022-07-14 13:48:49 +0800775@@ -453,8 +453,9 @@ enum {
developere3d01472022-05-09 14:01:49 +0800776
777 enum {
778 SER_QUERY,
779+ SER_SET,
780 /* recovery */
781- SER_SET_RECOVER_L1,
782+ SER_SET_RECOVER_L1 = 1,
783 SER_SET_RECOVER_L2,
784 SER_SET_RECOVER_L3_RX_ABORT,
785 SER_SET_RECOVER_L3_TX_ABORT,
786diff --git a/mt7915/mmio.c b/mt7915/mmio.c
developerd59e4772022-07-14 13:48:49 +0800787index d0e792f8..6e140e2d 100644
developere3d01472022-05-09 14:01:49 +0800788--- a/mt7915/mmio.c
789+++ b/mt7915/mmio.c
developerbd398d52022-06-06 20:53:24 +0800790@@ -24,6 +24,7 @@ static const u32 mt7915_reg[] = {
developere3d01472022-05-09 14:01:49 +0800791 [INFRA_MCU_ADDR_END] = 0x7c3fffff,
developerbd398d52022-06-06 20:53:24 +0800792 [FW_EXCEPTION_ADDR] = 0x219848,
developere3d01472022-05-09 14:01:49 +0800793 [SWDEF_BASE_ADDR] = 0x41f200,
794+ [EXCEPTION_BASE_ADDR] = 0x219848,
795 };
796
797 static const u32 mt7916_reg[] = {
developerbd398d52022-06-06 20:53:24 +0800798@@ -40,6 +41,7 @@ static const u32 mt7916_reg[] = {
developere3d01472022-05-09 14:01:49 +0800799 [INFRA_MCU_ADDR_END] = 0x7c085fff,
developerbd398d52022-06-06 20:53:24 +0800800 [FW_EXCEPTION_ADDR] = 0x022050bc,
developere3d01472022-05-09 14:01:49 +0800801 [SWDEF_BASE_ADDR] = 0x411400,
802+ [EXCEPTION_BASE_ADDR] = 0x022050BC,
803 };
804
805 static const u32 mt7986_reg[] = {
developerbd398d52022-06-06 20:53:24 +0800806@@ -56,6 +58,7 @@ static const u32 mt7986_reg[] = {
developere3d01472022-05-09 14:01:49 +0800807 [INFRA_MCU_ADDR_END] = 0x7c085fff,
developerbd398d52022-06-06 20:53:24 +0800808 [FW_EXCEPTION_ADDR] = 0x02204ffc,
developere3d01472022-05-09 14:01:49 +0800809 [SWDEF_BASE_ADDR] = 0x411400,
810+ [EXCEPTION_BASE_ADDR] = 0x02204FFC,
811 };
812
813 static const u32 mt7915_offs[] = {
developerbd398d52022-06-06 20:53:24 +0800814@@ -613,10 +616,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
developere3d01472022-05-09 14:01:49 +0800815 u32 val = mt76_rr(dev, MT_MCU_CMD);
816
817 mt76_wr(dev, MT_MCU_CMD, val);
818- if (val & MT_MCU_CMD_ERROR_MASK) {
819+ if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) {
820 dev->reset_state = val;
821- queue_work(dev->mt76.wq, &dev->reset_work);
822- wake_up(&dev->reset_wait);
823+ mt7915_reset(dev);
824 }
825 }
826 }
827diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
developerd59e4772022-07-14 13:48:49 +0800828index 5aafaac0..07a1c9ce 100644
developere3d01472022-05-09 14:01:49 +0800829--- a/mt7915/mt7915.h
830+++ b/mt7915/mt7915.h
developerf64861f2022-06-22 11:44:53 +0800831@@ -352,6 +352,15 @@ struct mt7915_dev {
developere3d01472022-05-09 14:01:49 +0800832 struct work_struct reset_work;
833 wait_queue_head_t reset_wait;
834 u32 reset_state;
835+ struct {
836+ bool hw_full_reset:1;
837+ bool hw_init_done:1;
838+ bool reset_enable:1;
839+ u32 reset_type;
840+ u32 cmd_fail_cnt;
841+ u32 wf_reset_wm_count;
842+ u32 wf_reset_wa_count;
843+ }ser;
844
845 struct list_head sta_rc_list;
846 struct list_head sta_poll_list;
developer4721e252022-06-21 16:41:28 +0800847@@ -416,6 +425,12 @@ enum {
developere3d01472022-05-09 14:01:49 +0800848 __MT_WFDMA_MAX,
849 };
850
851+enum {
852+ SER_TYPE_NONE,
853+ SER_TYPE_PARTIAL_RESET,
854+ SER_TYPE_FULL_RESET,
855+};
856+
857 enum {
developerf64861f2022-06-22 11:44:53 +0800858 MT_RX_SEL0,
859 MT_RX_SEL1,
developer4721e252022-06-21 16:41:28 +0800860@@ -513,6 +528,14 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band);
developere3d01472022-05-09 14:01:49 +0800861 int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2);
862 void mt7915_dma_prefetch(struct mt7915_dev *dev);
863 void mt7915_dma_cleanup(struct mt7915_dev *dev);
864+void mt7915_reset(struct mt7915_dev *dev);
865+int mt7915_dma_reset(struct mt7915_dev *dev, bool force);
866+int __mt7915_start(struct ieee80211_hw *hw);
867+void mt7915_init_txpower(struct mt7915_dev *dev,
868+ struct ieee80211_supported_band *sband);
869+int mt7915_txbf_init(struct mt7915_dev *dev);
870+void mt7915_mac_init(struct mt7915_dev *dev);
871+int mt7915_run_firmware(struct mt7915_dev *dev);
872 int mt7915_mcu_init(struct mt7915_dev *dev);
873 int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
874 struct mt7915_vif *mvif,
875diff --git a/mt7915/regs.h b/mt7915/regs.h
developerd59e4772022-07-14 13:48:49 +0800876index c7c9e411..47bae86e 100644
developere3d01472022-05-09 14:01:49 +0800877--- a/mt7915/regs.h
878+++ b/mt7915/regs.h
developerbd398d52022-06-06 20:53:24 +0800879@@ -32,6 +32,7 @@ enum reg_rev {
developere3d01472022-05-09 14:01:49 +0800880 INFRA_MCU_ADDR_END,
developerbd398d52022-06-06 20:53:24 +0800881 FW_EXCEPTION_ADDR,
developere3d01472022-05-09 14:01:49 +0800882 SWDEF_BASE_ADDR,
883+ EXCEPTION_BASE_ADDR,
884 __MT_REG_MAX,
885 };
886
developerbd398d52022-06-06 20:53:24 +0800887@@ -113,6 +114,11 @@ enum offs_rev {
developere3d01472022-05-09 14:01:49 +0800888 #define __REG(id) (dev->reg.reg_rev[(id)])
889 #define __OFFS(id) (dev->reg.offs_rev[(id)])
890
891+/* MEM WFDMA */
892+#define WF_WFDMA_MEM_DMA 0x58000000
893+
894+#define WF_WFDMA_MEM_DMA_RX_RING_CTL (WF_WFDMA_MEM_DMA + (0x510))
895+
896 /* MCU WFDMA0 */
897 #define MT_MCU_WFDMA0_BASE 0x2000
898 #define MT_MCU_WFDMA0(ofs) (MT_MCU_WFDMA0_BASE + (ofs))
developerbd398d52022-06-06 20:53:24 +0800899@@ -565,6 +571,10 @@ enum offs_rev {
developere3d01472022-05-09 14:01:49 +0800900 #define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4)
901 #define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8)
902
903+#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4)
904+#define MT_WFDMA0_MT_WA_WDT_INT BIT(31)
905+#define MT_WFDMA0_MT_WM_WDT_INT BIT(30)
906+
907 /* WFDMA1 */
908 #define MT_WFDMA1_BASE 0xd5000
909 #define MT_WFDMA1(ofs) (MT_WFDMA1_BASE + (ofs))
developerbd398d52022-06-06 20:53:24 +0800910@@ -710,6 +720,10 @@ enum offs_rev {
developere3d01472022-05-09 14:01:49 +0800911 #define MT_MCU_CMD_NORMAL_STATE BIT(5)
912 #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1)
913
914+#define MT_MCU_CMD_WA_WDT BIT(31)
915+#define MT_MCU_CMD_WM_WDT BIT(30)
916+#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30)
917+
918 /* TOP RGU */
919 #define MT_TOP_RGU_BASE 0x18000000
920 #define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0))
developerbd398d52022-06-06 20:53:24 +0800921@@ -985,6 +999,8 @@ enum offs_rev {
developere3d01472022-05-09 14:01:49 +0800922 #define MT_CPU_UTIL_PEAK_IDLE_CNT MT_CPU_UTIL(0x0c)
923 #define MT_CPU_UTIL_CTRL MT_CPU_UTIL(0x1c)
924
925+#define MT_EXCEPTION_ADDR __REG(EXCEPTION_BASE_ADDR)
926+
927 /* LED */
928 #define MT_LED_TOP_BASE 0x18013000
929 #define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n))
930--
developerd59e4772022-07-14 13:48:49 +08009312.25.1
developere3d01472022-05-09 14:01:49 +0800932