Loading Documentation/devicetree/bindings/cnss/icnss.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ Optional properties: - qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs. - 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. Example: Loading Loading @@ -59,4 +60,5 @@ Example: qcom,smmu-s1-bypass; 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>; }; drivers/soc/qcom/icnss.c +59 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <linux/thread_info.h> #include <linux/uaccess.h> #include <linux/etherdevice.h> #include <linux/of_gpio.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/icnss.h> #include <soc/qcom/secure_buffer.h> Loading Loading @@ -607,6 +608,47 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) { struct icnss_priv *priv = ctx; if (priv) priv->force_err_fatal = true; icnss_pr_err("Received force error fatal request from FW\n"); return IRQ_HANDLED; } static void icnss_register_force_error_fatal(struct icnss_priv *priv) { int gpio, irq, ret; if (!of_find_property(priv->pdev->dev.of_node, "qcom,gpio-force-fatal-error", NULL)) { icnss_pr_dbg("Error fatal smp2p handler not registered\n"); return; } gpio = of_get_named_gpio(priv->pdev->dev.of_node, "qcom,gpio-force-fatal-error", 0); if (!gpio_is_valid(gpio)) { icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio); return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq); return; } 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", irq); return; } icnss_pr_dbg("FW force error fatal handler registered\n"); } int icnss_call_driver_uevent(struct icnss_priv *priv, enum icnss_uevent uevent, void *data) Loading Loading @@ -678,6 +720,8 @@ static int icnss_driver_event_server_arrive(void *data) wlfw_dynamic_feature_mask_send_sync_msg(penv, dynamic_feature_mask); icnss_register_force_error_fatal(penv); return ret; err_setup_msa: Loading Loading @@ -772,6 +816,12 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) if (!priv->ops || !priv->ops->reinit) goto out; if (test_bit(ICNSS_FW_DOWN, &priv->state)) { icnss_pr_err("FW is in bad state, state: 0x%lx\n", priv->state); goto out; } if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state)) goto call_probe; Loading Loading @@ -844,6 +894,12 @@ static int icnss_driver_event_register_driver(void *data) if (test_bit(SKIP_QMI, &quirks)) set_bit(ICNSS_FW_READY, &penv->state); if (test_bit(ICNSS_FW_DOWN, &penv->state)) { icnss_pr_err("FW is in bad state, state: 0x%lx\n", penv->state); return -ENODEV; } if (!test_bit(ICNSS_FW_READY, &penv->state)) { icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n", penv->state); Loading Loading @@ -952,6 +1008,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, goto out; } if (priv->force_err_fatal) ICNSS_ASSERT(0); if (event_data->crashed) icnss_fw_crashed(priv, event_data); else Loading drivers/soc/qcom/icnss_private.h +1 −0 Original line number Diff line number Diff line Loading @@ -332,6 +332,7 @@ struct icnss_priv { atomic_t pm_count; struct ramdump_device *msa0_dump_dev; bool bypass_s1_smmu; bool force_err_fatal; u8 cause_for_rejuvenation; u8 requesting_sub_system; u16 line_number; Loading Loading
Documentation/devicetree/bindings/cnss/icnss.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ Optional properties: - qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs. - 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. Example: Loading Loading @@ -59,4 +60,5 @@ Example: qcom,smmu-s1-bypass; 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>; };
drivers/soc/qcom/icnss.c +59 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <linux/thread_info.h> #include <linux/uaccess.h> #include <linux/etherdevice.h> #include <linux/of_gpio.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/icnss.h> #include <soc/qcom/secure_buffer.h> Loading Loading @@ -607,6 +608,47 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) { struct icnss_priv *priv = ctx; if (priv) priv->force_err_fatal = true; icnss_pr_err("Received force error fatal request from FW\n"); return IRQ_HANDLED; } static void icnss_register_force_error_fatal(struct icnss_priv *priv) { int gpio, irq, ret; if (!of_find_property(priv->pdev->dev.of_node, "qcom,gpio-force-fatal-error", NULL)) { icnss_pr_dbg("Error fatal smp2p handler not registered\n"); return; } gpio = of_get_named_gpio(priv->pdev->dev.of_node, "qcom,gpio-force-fatal-error", 0); if (!gpio_is_valid(gpio)) { icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio); return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq); return; } 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", irq); return; } icnss_pr_dbg("FW force error fatal handler registered\n"); } int icnss_call_driver_uevent(struct icnss_priv *priv, enum icnss_uevent uevent, void *data) Loading Loading @@ -678,6 +720,8 @@ static int icnss_driver_event_server_arrive(void *data) wlfw_dynamic_feature_mask_send_sync_msg(penv, dynamic_feature_mask); icnss_register_force_error_fatal(penv); return ret; err_setup_msa: Loading Loading @@ -772,6 +816,12 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) if (!priv->ops || !priv->ops->reinit) goto out; if (test_bit(ICNSS_FW_DOWN, &priv->state)) { icnss_pr_err("FW is in bad state, state: 0x%lx\n", priv->state); goto out; } if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state)) goto call_probe; Loading Loading @@ -844,6 +894,12 @@ static int icnss_driver_event_register_driver(void *data) if (test_bit(SKIP_QMI, &quirks)) set_bit(ICNSS_FW_READY, &penv->state); if (test_bit(ICNSS_FW_DOWN, &penv->state)) { icnss_pr_err("FW is in bad state, state: 0x%lx\n", penv->state); return -ENODEV; } if (!test_bit(ICNSS_FW_READY, &penv->state)) { icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n", penv->state); Loading Loading @@ -952,6 +1008,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, goto out; } if (priv->force_err_fatal) ICNSS_ASSERT(0); if (event_data->crashed) icnss_fw_crashed(priv, event_data); else Loading
drivers/soc/qcom/icnss_private.h +1 −0 Original line number Diff line number Diff line Loading @@ -332,6 +332,7 @@ struct icnss_priv { atomic_t pm_count; struct ramdump_device *msa0_dump_dev; bool bypass_s1_smmu; bool force_err_fatal; u8 cause_for_rejuvenation; u8 requesting_sub_system; u16 line_number; Loading