33 * Copyright (C) 2022 MediaTek Inc.
44 */
55
6+ #include <linux/delay.h>
7+ #include <linux/gpio/consumer.h>
68#include <linux/kernel.h>
79#include <linux/module.h>
810#include <linux/pci.h>
@@ -142,6 +144,12 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
142144 if (hif2 )
143145 hif2 -> mt7996 = dev ;
144146
147+ /* Optional reset line for slot_reset() recovery; absent on stock DT. */
148+ dev -> reset_gpio = devm_gpiod_get_optional (& pdev -> dev , "reset" ,
149+ GPIOD_OUT_LOW );
150+ if (IS_ERR (dev -> reset_gpio ))
151+ dev -> reset_gpio = NULL ;
152+
145153 mt76_npu_init (mdev , pci_resource_start (pdev , 0 ),
146154 pdev -> bus && pci_domain_nr (pdev -> bus ) ? 3 : 2 );
147155
@@ -198,6 +206,11 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
198206 if (ret )
199207 goto free_hif2_irq ;
200208
209+ /* Snapshot config space so slot_reset() can restore BARs and the
210+ * command register after a PCIe bus reset clobbers them.
211+ */
212+ pci_save_state (pdev );
213+
201214 return 0 ;
202215
203216free_hif2_irq :
@@ -243,37 +256,36 @@ static void mt7996_pci_remove(struct pci_dev *pdev)
243256 mt7996_unregister_device (dev );
244257}
245258
246- static pci_ers_result_t mt7996_pci_mark_removed (struct mt76_dev * mdev )
259+ static void mt7996_pci_mark_removed (struct mt76_dev * mdev )
247260{
248261 /* Flag the device gone so every liveness check - including
249262 * mt7996_dev_gone() guarding the reset worker - short-circuits before
250- * it can touch the now-unreachable MMIO window. This is the same idiom
251- * the mt792x PCIe driver uses on surprise removal, and it does not rely
252- * on the controller updating pci_dev->error_state.
263+ * it can touch the now-unreachable MMIO window, and wake anyone blocked
264+ * on an MCU response so it fails fast instead of stalling to timeout.
253265 */
254266 set_bit (MT76_REMOVED , & mdev -> phy .state );
255-
256- /* Wake anyone blocked on an MCU response so it fails fast instead of
257- * stalling to timeout against a dead endpoint.
258- */
259267 wake_up (& mdev -> mcu .wait );
260-
261- /* The MAC and firmware state cannot survive a PCIe link loss and this
262- * driver has no slot_reset re-init path, so request a clean disconnect
263- * rather than a doomed recovery attempt.
264- */
265- return PCI_ERS_RESULT_DISCONNECT ;
266268}
267269
268270static pci_ers_result_t
269271mt7996_pci_error_detected (struct pci_dev * pdev , pci_channel_state_t state )
270272{
271273 struct mt76_dev * mdev = pci_get_drvdata (pdev );
272274
273- dev_err (& pdev -> dev , "PCIe error detected (state=%u), marking device removed\n" ,
275+ if (state == pci_channel_io_perm_failure )
276+ return PCI_ERS_RESULT_DISCONNECT ;
277+
278+ dev_err (& pdev -> dev , "PCIe error detected (state=%u), requesting slot reset\n" ,
274279 state );
275280
276- return mt7996_pci_mark_removed (mdev );
281+ mt7996_pci_mark_removed (mdev );
282+
283+ /* Ask the PCIe core to perform a secondary bus reset (PERST#) and then
284+ * call .slot_reset, where the chip is reinitialised. If recovery there
285+ * fails, slot_reset returns DISCONNECT and we end up in the same safe
286+ * state as before - WiFi down, the rest of the system alive.
287+ */
288+ return PCI_ERS_RESULT_NEED_RESET ;
277289}
278290
279291static pci_ers_result_t
@@ -282,26 +294,103 @@ mt7996_hif_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
282294 struct mt7996_hif * hif = pci_get_drvdata (pdev );
283295 struct mt7996_dev * dev = hif ? hif -> mt7996 : NULL ;
284296
297+ if (state == pci_channel_io_perm_failure )
298+ return PCI_ERS_RESULT_DISCONNECT ;
299+
285300 dev_err (& pdev -> dev , "PCIe error detected (state=%u) on hif2\n" , state );
286301
287302 /* hif2 is the second PCIe function of the same physical chip; a link
288303 * loss here downs the whole device. Mark the owning mt76 device removed
289- * so its reset worker bails out too. The primary function may not have
290- * claimed this hif yet (still probing), in which case there is nothing
291- * to mark - just report the disconnect .
304+ * so its reset worker bails out too, then request the shared slot reset.
305+ * If the primary function has not claimed this hif yet, there is nothing
306+ * to reinitialise from here - just disconnect this function .
292307 */
293308 if (!dev )
294309 return PCI_ERS_RESULT_DISCONNECT ;
295310
296- return mt7996_pci_mark_removed (& dev -> mt76 );
311+ mt7996_pci_mark_removed (& dev -> mt76 );
312+
313+ return PCI_ERS_RESULT_NEED_RESET ;
314+ }
315+
316+ static pci_ers_result_t mt7996_pci_slot_reset (struct pci_dev * pdev )
317+ {
318+ struct mt76_dev * mdev = pci_get_drvdata (pdev );
319+ struct mt7996_dev * dev = container_of (mdev , struct mt7996_dev , mt76 );
320+
321+ dev_info (& pdev -> dev , "PCIe slot reset, reinitialising device\n" );
322+
323+ /* The secondary bus reset clobbered config space; restore BARs and the
324+ * command register before any MMIO, and re-enable bus mastering.
325+ */
326+ pci_restore_state (pdev );
327+ pci_set_master (pdev );
328+
329+ /* Optional hard power-cycle of the card via a board GPIO wired in
330+ * parallel with the manual power switch. PERST# from the bus reset above
331+ * may not clear an MCU wedged by a supply brownout; cycling the rail
332+ * does. No-op when no reset-gpios is described in DT.
333+ */
334+ if (dev -> reset_gpio ) {
335+ gpiod_set_value_cansleep (dev -> reset_gpio , 1 );
336+ msleep (20 );
337+ gpiod_set_value_cansleep (dev -> reset_gpio , 0 );
338+ msleep (50 );
339+ }
340+
341+ /* Confirm the chip is actually back on the bus before promising the
342+ * core a recovery; a dead read returns all-ones.
343+ */
344+ if (mt76_rr (dev , MT_HW_REV ) == 0xffffffff ) {
345+ dev_err (& pdev -> dev , "device unreachable after slot reset\n" );
346+ return PCI_ERS_RESULT_DISCONNECT ;
347+ }
348+
349+ /* Config space is valid and the channel is online again, so the
350+ * mt7996_dev_gone() guard must stop tripping for the reinit to run.
351+ */
352+ clear_bit (MT76_REMOVED , & mdev -> phy .state );
353+
354+ /* Reload firmware and rebuild DMA/NAPI/token state through the existing
355+ * full chip reset path (mt7996_mac_reset_work -> mt7996_mac_full_reset).
356+ */
357+ dev -> recovery .restart = true;
358+ queue_work (mdev -> wq , & dev -> reset_work );
359+
360+ return PCI_ERS_RESULT_RECOVERED ;
361+ }
362+
363+ static pci_ers_result_t mt7996_hif_slot_reset (struct pci_dev * pdev )
364+ {
365+ /* hif2 shares the chip with the primary function, which drives the
366+ * actual reinit from its own slot_reset. Here we only need this
367+ * function's own config space restored so the reinit can reach the
368+ * hif2 register window.
369+ */
370+ pci_restore_state (pdev );
371+ pci_set_master (pdev );
372+
373+ return PCI_ERS_RESULT_RECOVERED ;
374+ }
375+
376+ static void mt7996_pci_io_resume (struct pci_dev * pdev )
377+ {
378+ /* The chip reinit queued from slot_reset ends in ieee80211_restart_hw(),
379+ * which restarts the queues, so nothing extra is required here.
380+ */
381+ dev_info (& pdev -> dev , "PCIe recovery complete\n" );
297382}
298383
299384static const struct pci_error_handlers mt7996_pci_err_handler = {
300385 .error_detected = mt7996_pci_error_detected ,
386+ .slot_reset = mt7996_pci_slot_reset ,
387+ .resume = mt7996_pci_io_resume ,
301388};
302389
303390static const struct pci_error_handlers mt7996_hif_err_handler = {
304391 .error_detected = mt7996_hif_error_detected ,
392+ .slot_reset = mt7996_hif_slot_reset ,
393+ .resume = mt7996_pci_io_resume ,
305394};
306395
307396struct pci_driver mt7996_hif_driver = {
0 commit comments