Loading Documentation/devicetree/bindings/cnss/icnss.txt +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ Optional properties: - 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: Loading Loading @@ -61,4 +62,5 @@ Example: 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>; }; drivers/soc/qcom/icnss.c +82 −4 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ enum icnss_driver_event_type { 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, }; Loading Loading @@ -464,6 +465,7 @@ static struct icnss_priv { 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; Loading Loading @@ -608,6 +610,8 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) 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"; } Loading Loading @@ -1194,7 +1198,24 @@ static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) 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; Loading @@ -1217,11 +1238,38 @@ static void icnss_register_force_error_fatal(struct icnss_priv *priv) 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) Loading Loading @@ -2113,7 +2161,7 @@ static int icnss_driver_event_server_arrive(void *data) icnss_init_vph_monitor(penv); icnss_register_force_error_fatal(penv); register_fw_error_notifications(penv); return ret; Loading Loading @@ -2213,6 +2261,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) 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; Loading Loading @@ -2367,7 +2416,7 @@ static int icnss_fw_crashed(struct icnss_priv *priv, 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; Loading @@ -2382,6 +2431,12 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, 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); Loading @@ -2403,6 +2458,25 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, 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; Loading Loading @@ -2444,6 +2518,10 @@ static void icnss_driver_event_work(struct work_struct *work) 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); Loading Loading
Documentation/devicetree/bindings/cnss/icnss.txt +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ Optional properties: - 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: Loading Loading @@ -61,4 +62,5 @@ Example: 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>; };
drivers/soc/qcom/icnss.c +82 −4 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ enum icnss_driver_event_type { 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, }; Loading Loading @@ -464,6 +465,7 @@ static struct icnss_priv { 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; Loading Loading @@ -608,6 +610,8 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) 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"; } Loading Loading @@ -1194,7 +1198,24 @@ static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) 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; Loading @@ -1217,11 +1238,38 @@ static void icnss_register_force_error_fatal(struct icnss_priv *priv) 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) Loading Loading @@ -2113,7 +2161,7 @@ static int icnss_driver_event_server_arrive(void *data) icnss_init_vph_monitor(penv); icnss_register_force_error_fatal(penv); register_fw_error_notifications(penv); return ret; Loading Loading @@ -2213,6 +2261,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) 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; Loading Loading @@ -2367,7 +2416,7 @@ static int icnss_fw_crashed(struct icnss_priv *priv, 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; Loading @@ -2382,6 +2431,12 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, 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); Loading @@ -2403,6 +2458,25 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, 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; Loading Loading @@ -2444,6 +2518,10 @@ static void icnss_driver_event_work(struct work_struct *work) 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); Loading