diff options
Diffstat (limited to 'drivers/soc/qcom/icnss.c')
-rw-r--r-- | drivers/soc/qcom/icnss.c | 125 |
1 files changed, 95 insertions, 30 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index d86957a7e09d..cd03c6ac1e5d 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -181,6 +181,7 @@ enum icnss_driver_event_type { struct icnss_event_pd_service_down_data { bool crashed; bool fw_rejuvenate; + bool wdog_bite; }; struct icnss_driver_event { @@ -205,6 +206,7 @@ enum icnss_driver_state { ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, ICNSS_WLFW_EXISTS, + ICNSS_WDOG_BITE, }; struct ce_irq_list { @@ -1700,6 +1702,23 @@ static int icnss_driver_event_server_exit(void *data) return 0; } +static int icnss_call_driver_uevent(struct icnss_priv *priv, + enum icnss_uevent uevent, void *data) +{ + struct icnss_uevent_data uevent_data; + + if (!priv->ops || !priv->ops->uevent) + return 0; + + icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n", + priv->state, uevent); + + uevent_data.uevent = uevent; + uevent_data.data = data; + + return priv->ops->uevent(&priv->pdev->dev, &uevent_data); +} + static int icnss_call_driver_probe(struct icnss_priv *priv) { int ret; @@ -1727,17 +1746,39 @@ out: return ret; } +static int icnss_call_driver_shutdown(struct icnss_priv *priv) +{ + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + goto out; + + if (!priv->ops || !priv->ops->shutdown) + goto out; + + icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state); + + priv->ops->shutdown(&priv->pdev->dev); + +out: + return 0; +} + static int icnss_pd_restart_complete(struct icnss_priv *priv) { int ret; - clear_bit(ICNSS_PD_RESTART, &priv->state); icnss_pm_relax(priv); + if (test_bit(ICNSS_WDOG_BITE, &priv->state)) { + icnss_call_driver_shutdown(priv); + clear_bit(ICNSS_WDOG_BITE, &priv->state); + } + + clear_bit(ICNSS_PD_RESTART, &priv->state); + if (!priv->ops || !priv->ops->reinit) goto out; - if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state)) goto call_probe; icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state); @@ -1774,6 +1815,8 @@ static int icnss_driver_event_fw_ready_ind(void *data) set_bit(ICNSS_FW_READY, &penv->state); + icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL); + icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state); icnss_hw_power_off(penv); @@ -1873,23 +1916,30 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) return 0; } -static int icnss_call_driver_shutdown(struct icnss_priv *priv) +static int icnss_fw_crashed(struct icnss_priv *priv, + struct icnss_event_pd_service_down_data *event_data) { - icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state); + icnss_pr_dbg("FW crashed, state: 0x%lx, wdog_bite: %d\n", + priv->state, event_data->wdog_bite); set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); icnss_pm_stay_awake(priv); - if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) - return 0; + icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL); - if (!priv->ops || !priv->ops->shutdown) - return 0; + if (event_data->wdog_bite) { + set_bit(ICNSS_WDOG_BITE, &priv->state); + goto out; + } - priv->ops->shutdown(&priv->pdev->dev); + icnss_call_driver_shutdown(priv); + if (event_data->fw_rejuvenate) + wlfw_rejuvenate_ack_send_sync_msg(priv); + +out: return 0; } @@ -1910,13 +1960,10 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, } if (event_data->crashed) - icnss_call_driver_shutdown(priv); + icnss_fw_crashed(priv, event_data); else icnss_call_driver_remove(priv); - if (event_data->fw_rejuvenate) - wlfw_rejuvenate_ack_send_sync_msg(priv); - out: ret = icnss_hw_power_off(priv); @@ -2063,7 +2110,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) return NOTIFY_OK; - icnss_pr_info("Modem went down, state: %lx\n", priv->state); + icnss_pr_info("Modem went down, state: %lx, crashed: %d\n", + priv->state, notif->crashed); event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); @@ -2072,6 +2120,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, event_data->crashed = notif->crashed; + if (notif->crashed == CRASH_STATUS_WDOG_BITE) + event_data->wdog_bite = true; + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); @@ -2136,30 +2187,41 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, enum pd_subsys_state *state = data; struct icnss_event_pd_service_down_data *event_data; - switch (notification) { - case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: - icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data, - priv->state); - event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); + icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n", + notification, priv->state); - if (event_data == NULL) - return notifier_from_errno(-ENOMEM); + if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01) + goto done; - if (state == NULL || *state != ROOT_PD_SHUTDOWN) - event_data->crashed = true; + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); - icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - ICNSS_EVENT_SYNC, event_data); + if (event_data == NULL) + return notifier_from_errno(-ENOMEM); + + if (state == NULL) { + event_data->crashed = true; + goto event_post; + } + + icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx\n", + *state, priv->state); + + switch (*state) { + case ROOT_PD_WDOG_BITE: + event_data->crashed = true; + event_data->wdog_bite = true; break; - case SERVREG_NOTIF_SERVICE_STATE_UP_V01: - icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state); + case ROOT_PD_SHUTDOWN: break; default: - icnss_pr_dbg("Service state Unknown, notification: 0x%lx, state: 0x%lx\n", - notification, priv->state); - return NOTIFY_DONE; + event_data->crashed = true; + break; } +event_post: + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + ICNSS_EVENT_SYNC, event_data); +done: return NOTIFY_OK; } @@ -3223,6 +3285,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_WLFW_EXISTS: seq_puts(s, "WLAN FW EXISTS"); continue; + case ICNSS_WDOG_BITE: + seq_puts(s, "MODEM WDOG BITE"); + continue; } seq_printf(s, "UNKNOWN-%d", i); |