Merge "icnss: Add support for early assert indication"
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt
index ad9d190..e70109d 100644
--- a/Documentation/devicetree/bindings/cnss/icnss.txt
+++ b/Documentation/devicetree/bindings/cnss/icnss.txt
@@ -30,6 +30,7 @@
- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
- qcom,wlan-msa-fixed-region: phandle, specifier pairs to children of /reserved-memory
- qcom,gpio-force-fatal-error: SMP2P bit triggered by WLAN FW to force error fatal.
+ - qcom,gpio-early-crash-ind: SMP2P bit triggered by WLAN FW to indicate FW is in assert.
Example:
@@ -61,4 +62,5 @@
vdd-0.8-cx-mx-supply = <&pm8998_l5>;
qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>;
qcom,gpio-forced-fatal-error = <&smp2pgpio_wlan_1_in 0 0>;
+ qcom,gpio-early-crash-ind = <&smp2pgpio_wlan_1_in 1 0>;
};
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index e3a50e3..d92b495 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -195,6 +195,7 @@
ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
ICNSS_DRIVER_EVENT_MAX,
};
@@ -464,6 +465,7 @@
bool bypass_s1_smmu;
bool force_err_fatal;
bool allow_recursive_recovery;
+ bool early_crash_ind;
u8 cause_for_rejuvenation;
u8 requesting_sub_system;
u16 line_number;
@@ -608,6 +610,8 @@
return "UNREGISTER_DRIVER";
case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
return "PD_SERVICE_DOWN";
+ case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+ return "FW_EARLY_CRASH_IND";
case ICNSS_DRIVER_EVENT_MAX:
return "EVENT_MAX";
}
@@ -1194,7 +1198,24 @@
return IRQ_HANDLED;
}
-static void icnss_register_force_error_fatal(struct icnss_priv *priv)
+static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
+{
+ struct icnss_priv *priv = ctx;
+
+ icnss_pr_err("Received early crash indication from FW\n");
+
+ if (priv) {
+ set_bit(ICNSS_FW_DOWN, &priv->state);
+ icnss_ignore_qmi_timeout(true);
+ }
+
+ icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+ 0, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static void register_fw_error_notifications(struct icnss_priv *priv)
{
int gpio, irq, ret;
@@ -1217,11 +1238,38 @@
ret = request_irq(irq, fw_error_fatal_handler,
IRQF_TRIGGER_RISING, "wlanfw-err", priv);
if (ret < 0) {
- icnss_pr_err("Unable to regiser for error fatal IRQ handler %d",
+ icnss_pr_err("Unable to register for error fatal IRQ handler %d",
irq);
return;
}
icnss_pr_dbg("FW force error fatal handler registered\n");
+
+ if (!of_find_property(priv->pdev->dev.of_node,
+ "qcom,gpio-early-crash-ind", NULL)) {
+ icnss_pr_dbg("FW early crash indication handler not registered\n");
+ return;
+ }
+ gpio = of_get_named_gpio(priv->pdev->dev.of_node,
+ "qcom,gpio-early-crash-ind", 0);
+ if (!gpio_is_valid(gpio)) {
+ icnss_pr_err("Invalid GPIO for early crash indication %d\n",
+ gpio);
+ return;
+ }
+ irq = gpio_to_irq(gpio);
+ if (irq < 0) {
+ icnss_pr_err("Invalid IRQ for early crash indication %u\n",
+ irq);
+ return;
+ }
+ ret = request_irq(irq, fw_crash_indication_handler,
+ IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv);
+ if (ret < 0) {
+ icnss_pr_err("Unable to register for early crash indication IRQ handler %d",
+ irq);
+ return;
+ }
+ icnss_pr_dbg("FW crash indication handler registered\n");
}
static int wlfw_msa_mem_info_send_sync_msg(void)
@@ -2113,7 +2161,7 @@
icnss_init_vph_monitor(penv);
- icnss_register_force_error_fatal(penv);
+ register_fw_error_notifications(penv);
return ret;
@@ -2213,6 +2261,7 @@
icnss_call_driver_shutdown(priv);
clear_bit(ICNSS_PD_RESTART, &priv->state);
+ priv->early_crash_ind = false;
if (!priv->ops || !priv->ops->reinit)
goto out;
@@ -2367,7 +2416,7 @@
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
- if (event_data->fw_rejuvenate)
+ if (event_data && event_data->fw_rejuvenate)
wlfw_rejuvenate_ack_send_sync_msg(priv);
return 0;
@@ -2382,6 +2431,12 @@
if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
goto out;
+ if (priv->early_crash_ind) {
+ icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
+ event_data->crashed, priv->state);
+ goto out;
+ }
+
if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
event_data->crashed, priv->state);
@@ -2403,6 +2458,25 @@
return ret;
}
+static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+ goto out;
+
+ priv->early_crash_ind = true;
+ icnss_fw_crashed(priv, NULL);
+
+out:
+ kfree(data);
+ icnss_ignore_qmi_timeout(false);
+
+ return ret;
+}
+
+
static void icnss_driver_event_work(struct work_struct *work)
{
struct icnss_driver_event *event;
@@ -2444,6 +2518,10 @@
ret = icnss_driver_event_pd_service_down(penv,
event->data);
break;
+ case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+ ret = icnss_driver_event_early_crash_ind(penv,
+ event->data);
+ break;
default:
icnss_pr_err("Invalid Event type: %d", event->type);
kfree(event);