summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
authorPrashanth Bhatta <bhattap@codeaurora.org>2017-02-08 09:27:57 -0800
committerPrashanth Bhatta <bhattap@codeaurora.org>2017-02-09 16:41:22 -0800
commitb70b942f246420e6165d6985f5a11f8037e6d2c9 (patch)
tree279728c6243ef9572f3f74f7ce6057298b0db675 /drivers/soc/qcom
parent97daa00ee4a585fdc289e772ef5357c2b6e240c3 (diff)
icnss: For WDOG bite, shutdown after FW ready
When there is Modem WDOG bite, Q6 will not get chance to reset the hardware and if shutdown is called as part of PD down notification then freeing buffers already submitted to hardware may cause exceptions as hardware may try to access DDR location which is already freed. Fix the issue by delaying the shutdown till FW ready happens and hardware is reset to clean state this way buffers are freed after hardware is in reset state. CRs-fixed: 2000709 Change-Id: Iacea5e8b712dd4ca310e5b502e43f4beb99f6981 Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/icnss.c125
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);