diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath12k/core.c')
-rw-r--r-- | drivers/net/wireless/ath/ath12k/core.c | 209 |
1 files changed, 140 insertions, 69 deletions
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 6663f4e1792d..51252e8bc1ae 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -16,6 +16,7 @@ #include "hif.h" #include "fw.h" #include "debugfs.h" +#include "wow.h" unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); @@ -42,27 +43,48 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) return ret; } -int ath12k_core_suspend(struct ath12k_base *ab) +/* Check if we need to continue with suspend/resume operation. + * Return: + * a negative value: error happens and don't continue. + * 0: no error but don't continue. + * positive value: no error and do continue. + */ +static int ath12k_core_continue_suspend_resume(struct ath12k_base *ab) { struct ath12k *ar; - int ret, i; if (!ab->hw_params->supports_suspend) return -EOPNOTSUPP; - rcu_read_lock(); + /* so far single_pdev_only chips have supports_suspend as true + * so pass 0 as a dummy pdev_id here. + */ + ar = ab->pdevs[0].ar; + if (!ar || !ar->ah || ar->ah->state != ATH12K_HW_STATE_OFF) + return 0; + + return 1; +} + +int ath12k_core_suspend(struct ath12k_base *ab) +{ + struct ath12k *ar; + int ret, i; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + for (i = 0; i < ab->num_radios; i++) { - ar = ath12k_mac_get_ar_by_pdev_id(ab, i); + ar = ab->pdevs[i].ar; if (!ar) continue; ret = ath12k_mac_wait_tx_complete(ar); if (ret) { ath12k_warn(ab, "failed to wait tx complete: %d\n", ret); - rcu_read_unlock(); return ret; } } - rcu_read_unlock(); /* PM framework skips suspend_late/resume_early callbacks * if other devices report errors in their suspend callbacks. @@ -83,8 +105,13 @@ EXPORT_SYMBOL(ath12k_core_suspend); int ath12k_core_suspend_late(struct ath12k_base *ab) { - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + int ret; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + ath12k_acpi_stop(ab); ath12k_hif_irq_disable(ab); ath12k_hif_ce_irq_disable(ab); @@ -99,8 +126,9 @@ int ath12k_core_resume_early(struct ath12k_base *ab) { int ret; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; reinit_completion(&ab->restart_completed); ret = ath12k_hif_power_up(ab); @@ -114,9 +142,11 @@ EXPORT_SYMBOL(ath12k_core_resume_early); int ath12k_core_resume(struct ath12k_base *ab) { long time_left; + int ret; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; time_left = wait_for_completion_timeout(&ab->restart_completed, ATH12K_RESET_TIMEOUT_HZ); @@ -994,9 +1024,8 @@ void ath12k_core_halt(struct ath12k *ar) static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) { struct ath12k *ar; - struct ath12k_pdev *pdev; struct ath12k_hw *ah; - int i; + int i, j; spin_lock_bh(&ab->base_lock); ab->stats.fw_crash_counter++; @@ -1006,35 +1035,32 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); for (i = 0; i < ab->num_hw; i++) { - if (!ab->ah[i]) + ah = ab->ah[i]; + if (!ah || ah->state == ATH12K_HW_STATE_OFF) continue; - ah = ab->ah[i]; ieee80211_stop_queues(ah->hw); - } - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar || ar->state == ATH12K_STATE_OFF) - continue; + for (j = 0; j < ah->num_radio; j++) { + ar = &ah->radio[j]; - ath12k_mac_drain_tx(ar); - complete(&ar->scan.started); - complete(&ar->scan.completed); - complete(&ar->scan.on_channel); - complete(&ar->peer_assoc_done); - complete(&ar->peer_delete_done); - complete(&ar->install_key_done); - complete(&ar->vdev_setup_done); - complete(&ar->vdev_delete_done); - complete(&ar->bss_survey_done); - - wake_up(&ar->dp.tx_empty_waitq); - idr_for_each(&ar->txmgmt_idr, - ath12k_mac_tx_mgmt_pending_free, ar); - idr_destroy(&ar->txmgmt_idr); - wake_up(&ar->txmgmt_empty_waitq); + ath12k_mac_drain_tx(ar); + complete(&ar->scan.started); + complete(&ar->scan.completed); + complete(&ar->scan.on_channel); + complete(&ar->peer_assoc_done); + complete(&ar->peer_delete_done); + complete(&ar->install_key_done); + complete(&ar->vdev_setup_done); + complete(&ar->vdev_delete_done); + complete(&ar->bss_survey_done); + + wake_up(&ar->dp.tx_empty_waitq); + idr_for_each(&ar->txmgmt_idr, + ath12k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + wake_up(&ar->txmgmt_empty_waitq); + } } wake_up(&ab->wmi_ab.tx_credits_wq); @@ -1043,48 +1069,57 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) { + struct ath12k_hw *ah; struct ath12k *ar; - struct ath12k_pdev *pdev; - int i; + int i, j; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar || ar->state == ATH12K_STATE_OFF) + for (i = 0; i < ab->num_hw; i++) { + ah = ab->ah[i]; + if (!ah || ah->state == ATH12K_HW_STATE_OFF) continue; - mutex_lock(&ar->conf_mutex); + mutex_lock(&ah->hw_mutex); + + switch (ah->state) { + case ATH12K_HW_STATE_ON: + ah->state = ATH12K_HW_STATE_RESTARTING; + + for (j = 0; j < ah->num_radio; j++) { + ar = &ah->radio[j]; + + mutex_lock(&ar->conf_mutex); + ath12k_core_halt(ar); + mutex_unlock(&ar->conf_mutex); + } - switch (ar->state) { - case ATH12K_STATE_ON: - ar->state = ATH12K_STATE_RESTARTING; - ath12k_core_halt(ar); - ieee80211_restart_hw(ath12k_ar_to_hw(ar)); break; - case ATH12K_STATE_OFF: + case ATH12K_HW_STATE_OFF: ath12k_warn(ab, - "cannot restart radio %d that hasn't been started\n", + "cannot restart hw %d that hasn't been started\n", i); break; - case ATH12K_STATE_RESTARTING: + case ATH12K_HW_STATE_RESTARTING: break; - case ATH12K_STATE_RESTARTED: - ar->state = ATH12K_STATE_WEDGED; + case ATH12K_HW_STATE_RESTARTED: + ah->state = ATH12K_HW_STATE_WEDGED; fallthrough; - case ATH12K_STATE_WEDGED: + case ATH12K_HW_STATE_WEDGED: ath12k_warn(ab, - "device is wedged, will not restart radio %d\n", i); + "device is wedged, will not restart hw %d\n", i); break; } - mutex_unlock(&ar->conf_mutex); + + mutex_unlock(&ah->hw_mutex); } + complete(&ab->driver_recovery); } static void ath12k_core_restart(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, restart_work); - int ret; + struct ath12k_hw *ah; + int ret, i; ret = ath12k_core_reconfigure_on_crash(ab); if (ret) { @@ -1092,8 +1127,12 @@ static void ath12k_core_restart(struct work_struct *work) return; } - if (ab->is_reset) - complete_all(&ab->reconfigure_complete); + if (ab->is_reset) { + for (i = 0; i < ab->num_hw; i++) { + ah = ab->ah[i]; + ieee80211_restart_hw(ah->hw); + } + } complete(&ab->restart_completed); } @@ -1147,20 +1186,14 @@ static void ath12k_core_reset(struct work_struct *work) ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n"); ab->is_reset = true; - atomic_set(&ab->recovery_start_count, 0); - reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_count, 0); ath12k_core_pre_reconfigure_recovery(ab); - reinit_completion(&ab->reconfigure_complete); ath12k_core_post_reconfigure_recovery(ab); ath12k_dbg(ab, ATH12K_DBG_BOOT, "waiting recovery start...\n"); - time_left = wait_for_completion_timeout(&ab->recovery_start, - ATH12K_RECOVER_START_TIMEOUT_HZ); - ath12k_hif_irq_disable(ab); ath12k_hif_ce_irq_disable(ab); @@ -1185,6 +1218,29 @@ int ath12k_core_pre_init(struct ath12k_base *ab) return 0; } +static int ath12k_core_panic_handler(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ath12k_base *ab = container_of(nb, struct ath12k_base, + panic_nb); + + return ath12k_hif_panic_handler(ab); +} + +static int ath12k_core_panic_notifier_register(struct ath12k_base *ab) +{ + ab->panic_nb.notifier_call = ath12k_core_panic_handler; + + return atomic_notifier_chain_register(&panic_notifier_list, + &ab->panic_nb); +} + +static void ath12k_core_panic_notifier_unregister(struct ath12k_base *ab) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &ab->panic_nb); +} + int ath12k_core_init(struct ath12k_base *ab) { int ret; @@ -1195,11 +1251,17 @@ int ath12k_core_init(struct ath12k_base *ab) return ret; } + ret = ath12k_core_panic_notifier_register(ab); + if (ret) + ath12k_warn(ab, "failed to register panic handler: %d\n", ret); + return 0; } void ath12k_core_deinit(struct ath12k_base *ab) { + ath12k_core_panic_notifier_unregister(ab); + mutex_lock(&ab->core_lock); ath12k_core_pdev_destroy(ab); @@ -1243,8 +1305,6 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); - init_completion(&ab->reconfigure_complete); - init_completion(&ab->recovery_start); INIT_LIST_HEAD(&ab->peers); init_waitqueue_head(&ab->peer_mapping_wq); @@ -1256,12 +1316,23 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->restart_completed); + init_completion(&ab->wow.wakeup_completed); ab->dev = dev; ab->hif.bus = bus; ab->qmi.num_radios = U8_MAX; ab->mlo_capable_flags = ATH12K_INTRA_DEVICE_MLO_SUPPORT; + /* Device index used to identify the devices in a group. + * + * In Intra-device MLO, only one device present in a group, + * so it is always zero. + * + * In Inter-device MLO, Multiple device present in a group, + * expect non-zero value. + */ + ab->device_id = 0; + return ab; err_free_wq: |