Merge "icnss: Handle graceful Modem shutdown"

This commit is contained in:
Linux Build Service Account 2016-10-06 01:07:03 -07:00 committed by Gerrit - the friendly Code Review server
commit 9033361e55

View file

@ -34,6 +34,7 @@
#include <linux/qmi_encdec.h>
#include <linux/ipc_logging.h>
#include <linux/msm-bus.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/qpnp/qpnp-adc.h>
#include <soc/qcom/memory_dump.h>
@ -41,6 +42,7 @@
#include <soc/qcom/msm_qmi_interface.h>
#include <soc/qcom/secure_buffer.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/service-locator.h>
#include <soc/qcom/service-notifier.h>
#include <soc/qcom/socinfo.h>
@ -270,6 +272,10 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_MAX,
};
struct icnss_event_pd_service_down_data {
bool crashed;
};
struct icnss_driver_event {
struct list_head list;
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 ret = 0;
icnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n",
icnss_driver_event_to_str(type), type,
sync ? "-sync" : "", penv->state);
icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n",
current->comm, icnss_driver_event_to_str(type),
sync ? "-sync" : "", type, penv->state);
if (type >= ICNSS_DRIVER_EVENT_MAX) {
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)
return 0;
icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
icnss_hw_power_on(priv);
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))
goto out;
icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
icnss_hw_power_on(priv);
ret = priv->ops->reinit(&priv->pdev->dev);
@ -2463,36 +2473,67 @@ out:
return 0;
}
static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
void *data)
static int icnss_call_driver_remove(struct icnss_priv *priv)
{
int ret = 0;
icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n",
priv->state);
ICNSS_ASSERT(0);
goto out;
}
clear_bit(ICNSS_FW_READY, &priv->state);
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
return 0;
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);
clear_bit(ICNSS_FW_READY, &priv->state);
if (!priv->ops || !priv->ops->shutdown)
goto out;
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);
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:
icnss_remove_msa_permissions(priv);
ret = icnss_hw_power_off(priv);
icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n",
ret, priv->state);
kfree(data);
return ret;
}
@ -2533,7 +2574,8 @@ static void icnss_driver_event_work(struct work_struct *work)
ret = icnss_driver_event_unregister_driver(event->data);
break;
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;
default:
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++;
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);
if (event->sync) {
event->ret = ret;
@ -2590,23 +2637,31 @@ static struct notifier_block wlfw_clnt_nb = {
.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,
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);
if (code == SUBSYS_AFTER_POWERUP) {
icnss_pr_dbg("Modem-Notify: Powerup\n");
} else if (code == SUBSYS_BEFORE_SHUTDOWN) {
icnss_pr_info("Modem-Notify: Before shutdown\n");
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
true, NULL);
} else if (code == SUBSYS_AFTER_SHUTDOWN) {
icnss_pr_info("Modem-Notify: After Shutdown\n");
} else {
return NOTIFY_DONE;
}
if (code != SUBSYS_BEFORE_SHUTDOWN)
return NOTIFY_OK;
icnss_pr_info("Modem went down, state: %lx\n", priv->state);
event_data = kzalloc(sizeof(*data), GFP_KERNEL);
if (event_data == NULL)
return notifier_from_errno(-ENOMEM);
event_data->crashed = notif->crashed;
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
true, event_data);
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,
service_notifier_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, 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,
true, NULL);
icnss_pr_dbg("Service down completed, state: 0x%lx\n",
priv->state);
true, event_data);
break;
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
@ -2818,8 +2882,6 @@ enable_pdr:
if (ret)
return ret;
icnss_modem_ssr_unregister_notifier(priv);
return 0;
}