summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/soc/qcom/icnss.c136
1 files changed, 99 insertions, 37 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index edaaa0b2d3c8..1feb7fcce48f 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -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 (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ return 0;
+
if (!priv->ops || !priv->ops->shutdown)
- goto out;
+ return 0;
- if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ 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;
+ }
- priv->ops->shutdown(&priv->pdev->dev);
+ 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;
}