Merge "icnss: Handle graceful Modem shutdown"
This commit is contained in:
commit
9033361e55
1 changed files with 100 additions and 38 deletions
|
@ -34,6 +34,7 @@
|
||||||
#include <linux/qmi_encdec.h>
|
#include <linux/qmi_encdec.h>
|
||||||
#include <linux/ipc_logging.h>
|
#include <linux/ipc_logging.h>
|
||||||
#include <linux/msm-bus.h>
|
#include <linux/msm-bus.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/qpnp/qpnp-adc.h>
|
#include <linux/qpnp/qpnp-adc.h>
|
||||||
#include <soc/qcom/memory_dump.h>
|
#include <soc/qcom/memory_dump.h>
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
#include <soc/qcom/msm_qmi_interface.h>
|
#include <soc/qcom/msm_qmi_interface.h>
|
||||||
#include <soc/qcom/secure_buffer.h>
|
#include <soc/qcom/secure_buffer.h>
|
||||||
#include <soc/qcom/subsystem_notif.h>
|
#include <soc/qcom/subsystem_notif.h>
|
||||||
|
#include <soc/qcom/subsystem_restart.h>
|
||||||
#include <soc/qcom/service-locator.h>
|
#include <soc/qcom/service-locator.h>
|
||||||
#include <soc/qcom/service-notifier.h>
|
#include <soc/qcom/service-notifier.h>
|
||||||
#include <soc/qcom/socinfo.h>
|
#include <soc/qcom/socinfo.h>
|
||||||
|
@ -270,6 +272,10 @@ enum icnss_driver_event_type {
|
||||||
ICNSS_DRIVER_EVENT_MAX,
|
ICNSS_DRIVER_EVENT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct icnss_event_pd_service_down_data {
|
||||||
|
bool crashed;
|
||||||
|
};
|
||||||
|
|
||||||
struct icnss_driver_event {
|
struct icnss_driver_event {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
enum icnss_driver_event_type type;
|
enum icnss_driver_event_type type;
|
||||||
|
@ -535,9 +541,9 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
|
||||||
int gfp = GFP_KERNEL;
|
int gfp = GFP_KERNEL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
icnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n",
|
icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n",
|
||||||
icnss_driver_event_to_str(type), type,
|
current->comm, icnss_driver_event_to_str(type),
|
||||||
sync ? "-sync" : "", penv->state);
|
sync ? "-sync" : "", type, penv->state);
|
||||||
|
|
||||||
if (type >= ICNSS_DRIVER_EVENT_MAX) {
|
if (type >= ICNSS_DRIVER_EVENT_MAX) {
|
||||||
icnss_pr_err("Invalid Event type: %d, can't post", type);
|
icnss_pr_err("Invalid Event type: %d, can't post", type);
|
||||||
|
@ -2311,6 +2317,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
|
||||||
if (!priv->ops || !priv->ops->probe)
|
if (!priv->ops || !priv->ops->probe)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
|
||||||
|
|
||||||
icnss_hw_power_on(priv);
|
icnss_hw_power_on(priv);
|
||||||
|
|
||||||
ret = priv->ops->probe(&priv->pdev->dev);
|
ret = priv->ops->probe(&priv->pdev->dev);
|
||||||
|
@ -2339,6 +2347,8 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv)
|
||||||
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
|
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
|
||||||
|
|
||||||
icnss_hw_power_on(priv);
|
icnss_hw_power_on(priv);
|
||||||
|
|
||||||
ret = priv->ops->reinit(&priv->pdev->dev);
|
ret = priv->ops->reinit(&priv->pdev->dev);
|
||||||
|
@ -2463,36 +2473,67 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
|
static int icnss_call_driver_remove(struct icnss_priv *priv)
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
int ret = 0;
|
icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
|
||||||
|
|
||||||
if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
|
clear_bit(ICNSS_FW_READY, &priv->state);
|
||||||
icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n",
|
|
||||||
priv->state);
|
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
|
||||||
ICNSS_ASSERT(0);
|
return 0;
|
||||||
goto out;
|
|
||||||
}
|
if (!priv->ops || !priv->ops->remove)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
penv->ops->remove(&priv->pdev->dev);
|
||||||
|
|
||||||
|
clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int icnss_call_driver_shutdown(struct icnss_priv *priv)
|
||||||
|
{
|
||||||
|
icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
|
||||||
|
|
||||||
set_bit(ICNSS_PD_RESTART, &priv->state);
|
set_bit(ICNSS_PD_RESTART, &priv->state);
|
||||||
clear_bit(ICNSS_FW_READY, &priv->state);
|
clear_bit(ICNSS_FW_READY, &priv->state);
|
||||||
|
|
||||||
if (!priv->ops || !priv->ops->shutdown)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
|
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
|
||||||
goto out;
|
return 0;
|
||||||
|
|
||||||
|
if (!priv->ops || !priv->ops->shutdown)
|
||||||
|
return 0;
|
||||||
|
|
||||||
priv->ops->shutdown(&priv->pdev->dev);
|
priv->ops->shutdown(&priv->pdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct icnss_event_pd_service_down_data *event_data = data;
|
||||||
|
|
||||||
|
if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
|
||||||
|
icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
|
||||||
|
event_data->crashed, priv->state);
|
||||||
|
ICNSS_ASSERT(0);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_data->crashed)
|
||||||
|
icnss_call_driver_shutdown(priv);
|
||||||
|
else
|
||||||
|
icnss_call_driver_remove(priv);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
icnss_remove_msa_permissions(priv);
|
icnss_remove_msa_permissions(priv);
|
||||||
|
|
||||||
ret = icnss_hw_power_off(priv);
|
ret = icnss_hw_power_off(priv);
|
||||||
|
|
||||||
icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n",
|
kfree(data);
|
||||||
ret, priv->state);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -2533,7 +2574,8 @@ static void icnss_driver_event_work(struct work_struct *work)
|
||||||
ret = icnss_driver_event_unregister_driver(event->data);
|
ret = icnss_driver_event_unregister_driver(event->data);
|
||||||
break;
|
break;
|
||||||
case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
|
case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
|
||||||
icnss_driver_event_pd_service_down(penv, event->data);
|
ret = icnss_driver_event_pd_service_down(penv,
|
||||||
|
event->data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
icnss_pr_err("Invalid Event type: %d", event->type);
|
icnss_pr_err("Invalid Event type: %d", event->type);
|
||||||
|
@ -2543,6 +2585,11 @@ static void icnss_driver_event_work(struct work_struct *work)
|
||||||
|
|
||||||
penv->stats.events[event->type].processed++;
|
penv->stats.events[event->type].processed++;
|
||||||
|
|
||||||
|
icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
|
||||||
|
icnss_driver_event_to_str(event->type),
|
||||||
|
event->sync ? "-sync" : "", event->type, ret,
|
||||||
|
penv->state);
|
||||||
|
|
||||||
spin_lock_irqsave(&penv->event_lock, flags);
|
spin_lock_irqsave(&penv->event_lock, flags);
|
||||||
if (event->sync) {
|
if (event->sync) {
|
||||||
event->ret = ret;
|
event->ret = ret;
|
||||||
|
@ -2590,23 +2637,31 @@ static struct notifier_block wlfw_clnt_nb = {
|
||||||
.notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
|
.notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int icnss_modem_notifier_nb(struct notifier_block *this,
|
static int icnss_modem_notifier_nb(struct notifier_block *nb,
|
||||||
unsigned long code,
|
unsigned long code,
|
||||||
void *ss_handle)
|
void *data)
|
||||||
{
|
{
|
||||||
|
struct icnss_event_pd_service_down_data *event_data;
|
||||||
|
struct notif_data *notif = data;
|
||||||
|
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
|
||||||
|
modem_ssr_nb);
|
||||||
|
|
||||||
icnss_pr_dbg("Modem-Notify: event %lu\n", code);
|
icnss_pr_dbg("Modem-Notify: event %lu\n", code);
|
||||||
|
|
||||||
if (code == SUBSYS_AFTER_POWERUP) {
|
if (code != SUBSYS_BEFORE_SHUTDOWN)
|
||||||
icnss_pr_dbg("Modem-Notify: Powerup\n");
|
return NOTIFY_OK;
|
||||||
} else if (code == SUBSYS_BEFORE_SHUTDOWN) {
|
|
||||||
icnss_pr_info("Modem-Notify: Before shutdown\n");
|
icnss_pr_info("Modem went down, state: %lx\n", priv->state);
|
||||||
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
|
|
||||||
true, NULL);
|
event_data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
} else if (code == SUBSYS_AFTER_SHUTDOWN) {
|
|
||||||
icnss_pr_info("Modem-Notify: After Shutdown\n");
|
if (event_data == NULL)
|
||||||
} else {
|
return notifier_from_errno(-ENOMEM);
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
event_data->crashed = notif->crashed;
|
||||||
|
|
||||||
|
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
|
||||||
|
true, event_data);
|
||||||
|
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
@ -2665,14 +2720,23 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
|
||||||
{
|
{
|
||||||
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
|
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
|
||||||
service_notifier_nb);
|
service_notifier_nb);
|
||||||
|
enum pd_subsys_state *state = data;
|
||||||
|
struct icnss_event_pd_service_down_data *event_data;
|
||||||
|
|
||||||
switch (notification) {
|
switch (notification) {
|
||||||
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
|
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
|
||||||
icnss_pr_info("Service down, state: 0x%lx\n", priv->state);
|
icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data,
|
||||||
|
priv->state);
|
||||||
|
event_data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (event_data == NULL)
|
||||||
|
return notifier_from_errno(-ENOMEM);
|
||||||
|
|
||||||
|
if (state == NULL || *state != SHUTDOWN)
|
||||||
|
event_data->crashed = true;
|
||||||
|
|
||||||
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
|
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
|
||||||
true, NULL);
|
true, event_data);
|
||||||
icnss_pr_dbg("Service down completed, state: 0x%lx\n",
|
|
||||||
priv->state);
|
|
||||||
break;
|
break;
|
||||||
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
|
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
|
||||||
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
|
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
|
||||||
|
@ -2818,8 +2882,6 @@ enable_pdr:
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
icnss_modem_ssr_unregister_notifier(priv);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue