1313#include <linux/mhi.h>
1414#include <linux/module.h>
1515#include <linux/pci.h>
16+ #include <linux/workqueue.h>
1617
1718#define MHI_PCI_DEFAULT_BAR_NUM 0
1819
@@ -186,6 +187,7 @@ enum mhi_pci_device_status {
186187struct mhi_pci_device {
187188 struct mhi_controller mhi_cntrl ;
188189 struct pci_saved_state * pci_state ;
190+ struct work_struct recovery_work ;
189191 unsigned long status ;
190192};
191193
@@ -313,6 +315,50 @@ static void mhi_pci_runtime_put(struct mhi_controller *mhi_cntrl)
313315 /* no PM for now */
314316}
315317
318+ static void mhi_pci_recovery_work (struct work_struct * work )
319+ {
320+ struct mhi_pci_device * mhi_pdev = container_of (work , struct mhi_pci_device ,
321+ recovery_work );
322+ struct mhi_controller * mhi_cntrl = & mhi_pdev -> mhi_cntrl ;
323+ struct pci_dev * pdev = to_pci_dev (mhi_cntrl -> cntrl_dev );
324+ int err ;
325+
326+ dev_warn (& pdev -> dev , "device recovery started\n" );
327+
328+ /* Clean up MHI state */
329+ if (test_and_clear_bit (MHI_PCI_DEV_STARTED , & mhi_pdev -> status )) {
330+ mhi_power_down (mhi_cntrl , false);
331+ mhi_unprepare_after_power_down (mhi_cntrl );
332+ }
333+
334+ /* Check if we can recover without full reset */
335+ pci_set_power_state (pdev , PCI_D0 );
336+ pci_load_saved_state (pdev , mhi_pdev -> pci_state );
337+ pci_restore_state (pdev );
338+
339+ if (!mhi_pci_is_alive (mhi_cntrl ))
340+ goto err_try_reset ;
341+
342+ err = mhi_prepare_for_power_up (mhi_cntrl );
343+ if (err )
344+ goto err_try_reset ;
345+
346+ err = mhi_sync_power_up (mhi_cntrl );
347+ if (err )
348+ goto err_unprepare ;
349+
350+ dev_dbg (& pdev -> dev , "Recovery completed\n" );
351+
352+ set_bit (MHI_PCI_DEV_STARTED , & mhi_pdev -> status );
353+ return ;
354+
355+ err_unprepare :
356+ mhi_unprepare_after_power_down (mhi_cntrl );
357+ err_try_reset :
358+ if (pci_reset_function (pdev ))
359+ dev_err (& pdev -> dev , "Recovery failed\n" );
360+ }
361+
316362static int mhi_pci_probe (struct pci_dev * pdev , const struct pci_device_id * id )
317363{
318364 const struct mhi_pci_dev_info * info = (struct mhi_pci_dev_info * ) id -> driver_data ;
@@ -328,6 +374,8 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
328374 if (!mhi_pdev )
329375 return - ENOMEM ;
330376
377+ INIT_WORK (& mhi_pdev -> recovery_work , mhi_pci_recovery_work );
378+
331379 mhi_cntrl_config = info -> config ;
332380 mhi_cntrl = & mhi_pdev -> mhi_cntrl ;
333381
@@ -391,6 +439,8 @@ static void mhi_pci_remove(struct pci_dev *pdev)
391439 struct mhi_pci_device * mhi_pdev = pci_get_drvdata (pdev );
392440 struct mhi_controller * mhi_cntrl = & mhi_pdev -> mhi_cntrl ;
393441
442+ cancel_work_sync (& mhi_pdev -> recovery_work );
443+
394444 if (test_and_clear_bit (MHI_PCI_DEV_STARTED , & mhi_pdev -> status )) {
395445 mhi_power_down (mhi_cntrl , true);
396446 mhi_unprepare_after_power_down (mhi_cntrl );
@@ -456,12 +506,67 @@ static const struct pci_error_handlers mhi_pci_err_handler = {
456506 .reset_done = mhi_pci_reset_done ,
457507};
458508
509+ static int __maybe_unused mhi_pci_suspend (struct device * dev )
510+ {
511+ struct pci_dev * pdev = to_pci_dev (dev );
512+ struct mhi_pci_device * mhi_pdev = dev_get_drvdata (dev );
513+ struct mhi_controller * mhi_cntrl = & mhi_pdev -> mhi_cntrl ;
514+
515+ cancel_work_sync (& mhi_pdev -> recovery_work );
516+
517+ /* Transition to M3 state */
518+ mhi_pm_suspend (mhi_cntrl );
519+
520+ pci_save_state (pdev );
521+ pci_disable_device (pdev );
522+ pci_wake_from_d3 (pdev , true);
523+ pci_set_power_state (pdev , PCI_D3hot );
524+
525+ return 0 ;
526+ }
527+
528+ static int __maybe_unused mhi_pci_resume (struct device * dev )
529+ {
530+ struct pci_dev * pdev = to_pci_dev (dev );
531+ struct mhi_pci_device * mhi_pdev = dev_get_drvdata (dev );
532+ struct mhi_controller * mhi_cntrl = & mhi_pdev -> mhi_cntrl ;
533+ int err ;
534+
535+ pci_set_power_state (pdev , PCI_D0 );
536+ pci_restore_state (pdev );
537+ pci_set_master (pdev );
538+
539+ err = pci_enable_device (pdev );
540+ if (err )
541+ goto err_recovery ;
542+
543+ /* Exit M3, transition to M0 state */
544+ err = mhi_pm_resume (mhi_cntrl );
545+ if (err ) {
546+ dev_err (& pdev -> dev , "failed to resume device: %d\n" , err );
547+ goto err_recovery ;
548+ }
549+
550+ return 0 ;
551+
552+ err_recovery :
553+ /* The device may have loose power or crashed, try recovering it */
554+ queue_work (system_long_wq , & mhi_pdev -> recovery_work );
555+
556+ return err ;
557+ }
558+
559+ static const struct dev_pm_ops mhi_pci_pm_ops = {
560+ SET_SYSTEM_SLEEP_PM_OPS (mhi_pci_suspend , mhi_pci_resume )
561+ };
562+
459563static struct pci_driver mhi_pci_driver = {
460564 .name = "mhi-pci-generic" ,
461565 .id_table = mhi_pci_id_table ,
462566 .probe = mhi_pci_probe ,
463567 .remove = mhi_pci_remove ,
464568 .err_handler = & mhi_pci_err_handler ,
569+ .driver .pm = & mhi_pci_pm_ops
465570};
466571module_pci_driver (mhi_pci_driver );
467572
0 commit comments