Loading Documentation/devicetree/bindings/cnss/icnss.txt +11 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,13 @@ 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. - qcom,gpio-early-crash-ind: SMP2P bit triggered by WLAN FW to indicate FW is in assert. - qcom,hyp_disabled: Boolean context flag to disable hyperviser WLAN SMP2P sub nodes - qcom,smp2p_map_wlan_1_in - represents the in smp2p to wlan driver from modem. Example: qcom,icnss@0a000000 { Loading Loading @@ -62,7 +65,11 @@ 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>; qcom,gpio-early-crash-ind = <&smp2pgpio_wlan_1_in 1 0>; qcom,hyp_disabled; qcom,smp2p_map_wlan_1_in { interrupts-extended = <&smp2p_wlan_1_in 0 0>, <&smp2p_wlan_1_in 1 0>; interrupt-names = "qcom,smp2p-force-fatal-error", "qcom,smp2p-early-crash-ind"; }; }; drivers/soc/qcom/icnss.c +75 −40 Original line number Diff line number Diff line Loading @@ -36,7 +36,8 @@ #include <linux/thread_info.h> #include <linux/uaccess.h> #include <linux/etherdevice.h> #include <linux/of_gpio.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/soc/qcom/qmi.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/icnss.h> Loading Loading @@ -653,10 +654,17 @@ static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) { struct icnss_priv *priv = ctx; struct icnss_uevent_fw_down_data fw_down_data = {0}; icnss_pr_err("Received early crash indication from FW\n"); if (priv) { if (test_bit(ICNSS_FW_READY, &priv->state) && !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state)) { fw_down_data.crashed = true; icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); } set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_fw_timeout(true); } Loading @@ -667,61 +675,84 @@ static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) return IRQ_HANDLED; } static void register_fw_error_notifications(struct icnss_priv *priv) static void register_fw_error_notifications(struct device *dev) { int gpio, irq, ret; struct icnss_priv *priv = dev_get_drvdata(dev); struct device_node *dev_node; int irq = 0, ret = 0; 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); if (!priv) return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq); dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in"); if (!dev_node) { icnss_pr_err("Failed to get smp2p node for force-fatal-error\n"); return; } ret = request_irq(irq, fw_error_fatal_handler, IRQF_TRIGGER_RISING, "wlanfw-err", priv); icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name); if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) { ret = irq = of_irq_get_byname(dev_node, "qcom,smp2p-force-fatal-error"); if (ret < 0) { icnss_pr_err("Unable to register for error fatal IRQ handler %d", icnss_pr_err("Unable to get force-fatal-error irq %d\n", 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"); ret = devm_request_threaded_irq(dev, irq, NULL, fw_error_fatal_handler, IRQF_TRIGGER_RISING, "wlanfw-err", priv); if (ret < 0) { icnss_pr_err("Unable to register for error fatal IRQ handler %d ret = %d", irq, ret); 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); icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq); priv->fw_error_fatal_irq = irq; } static void register_early_crash_notifications(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); struct device_node *dev_node; int irq = 0, ret = 0; if (!priv) return; dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in"); if (!dev_node) { icnss_pr_err("Failed to get smp2p node for early-crash-ind\n"); return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for early crash indication %u\n", icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name); if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) { ret = irq = of_irq_get_byname(dev_node, "qcom,smp2p-early-crash-ind"); if (ret < 0) { icnss_pr_err("Unable to get early-crash-ind irq %d\n", irq); return; } ret = request_irq(irq, fw_crash_indication_handler, IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv); } ret = devm_request_threaded_irq(dev, irq, NULL, 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); icnss_pr_err("Unable to register for early crash indication IRQ handler %d ret = %d", irq, ret); return; } icnss_pr_dbg("FW crash indication handler registered\n"); icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq); priv->fw_early_crash_irq = irq; } int icnss_call_driver_uevent(struct icnss_priv *priv, Loading Loading @@ -794,7 +825,11 @@ static int icnss_driver_event_server_arrive(void *data) wlfw_dynamic_feature_mask_send_sync_msg(penv, dynamic_feature_mask); register_fw_error_notifications(penv); if (!penv->fw_error_fatal_irq) register_fw_error_notifications(&penv->pdev->dev); if (!penv->fw_early_crash_irq) register_early_crash_notifications(&penv->pdev->dev); return ret; Loading drivers/soc/qcom/icnss_private.h +2 −0 Original line number Diff line number Diff line Loading @@ -348,6 +348,8 @@ struct icnss_priv { u16 line_number; struct mutex dev_lock; bool is_hyp_disabled; uint32_t fw_error_fatal_irq; uint32_t fw_early_crash_irq; char function_name[WLFW_FUNCTION_NAME_LEN + 1]; }; Loading Loading
Documentation/devicetree/bindings/cnss/icnss.txt +11 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,13 @@ 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. - qcom,gpio-early-crash-ind: SMP2P bit triggered by WLAN FW to indicate FW is in assert. - qcom,hyp_disabled: Boolean context flag to disable hyperviser WLAN SMP2P sub nodes - qcom,smp2p_map_wlan_1_in - represents the in smp2p to wlan driver from modem. Example: qcom,icnss@0a000000 { Loading Loading @@ -62,7 +65,11 @@ 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>; qcom,gpio-early-crash-ind = <&smp2pgpio_wlan_1_in 1 0>; qcom,hyp_disabled; qcom,smp2p_map_wlan_1_in { interrupts-extended = <&smp2p_wlan_1_in 0 0>, <&smp2p_wlan_1_in 1 0>; interrupt-names = "qcom,smp2p-force-fatal-error", "qcom,smp2p-early-crash-ind"; }; };
drivers/soc/qcom/icnss.c +75 −40 Original line number Diff line number Diff line Loading @@ -36,7 +36,8 @@ #include <linux/thread_info.h> #include <linux/uaccess.h> #include <linux/etherdevice.h> #include <linux/of_gpio.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/soc/qcom/qmi.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/icnss.h> Loading Loading @@ -653,10 +654,17 @@ static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) { struct icnss_priv *priv = ctx; struct icnss_uevent_fw_down_data fw_down_data = {0}; icnss_pr_err("Received early crash indication from FW\n"); if (priv) { if (test_bit(ICNSS_FW_READY, &priv->state) && !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state)) { fw_down_data.crashed = true; icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); } set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_fw_timeout(true); } Loading @@ -667,61 +675,84 @@ static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) return IRQ_HANDLED; } static void register_fw_error_notifications(struct icnss_priv *priv) static void register_fw_error_notifications(struct device *dev) { int gpio, irq, ret; struct icnss_priv *priv = dev_get_drvdata(dev); struct device_node *dev_node; int irq = 0, ret = 0; 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); if (!priv) return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq); dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in"); if (!dev_node) { icnss_pr_err("Failed to get smp2p node for force-fatal-error\n"); return; } ret = request_irq(irq, fw_error_fatal_handler, IRQF_TRIGGER_RISING, "wlanfw-err", priv); icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name); if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) { ret = irq = of_irq_get_byname(dev_node, "qcom,smp2p-force-fatal-error"); if (ret < 0) { icnss_pr_err("Unable to register for error fatal IRQ handler %d", icnss_pr_err("Unable to get force-fatal-error irq %d\n", 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"); ret = devm_request_threaded_irq(dev, irq, NULL, fw_error_fatal_handler, IRQF_TRIGGER_RISING, "wlanfw-err", priv); if (ret < 0) { icnss_pr_err("Unable to register for error fatal IRQ handler %d ret = %d", irq, ret); 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); icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq); priv->fw_error_fatal_irq = irq; } static void register_early_crash_notifications(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); struct device_node *dev_node; int irq = 0, ret = 0; if (!priv) return; dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in"); if (!dev_node) { icnss_pr_err("Failed to get smp2p node for early-crash-ind\n"); return; } irq = gpio_to_irq(gpio); if (irq < 0) { icnss_pr_err("Invalid IRQ for early crash indication %u\n", icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name); if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) { ret = irq = of_irq_get_byname(dev_node, "qcom,smp2p-early-crash-ind"); if (ret < 0) { icnss_pr_err("Unable to get early-crash-ind irq %d\n", irq); return; } ret = request_irq(irq, fw_crash_indication_handler, IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv); } ret = devm_request_threaded_irq(dev, irq, NULL, 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); icnss_pr_err("Unable to register for early crash indication IRQ handler %d ret = %d", irq, ret); return; } icnss_pr_dbg("FW crash indication handler registered\n"); icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq); priv->fw_early_crash_irq = irq; } int icnss_call_driver_uevent(struct icnss_priv *priv, Loading Loading @@ -794,7 +825,11 @@ static int icnss_driver_event_server_arrive(void *data) wlfw_dynamic_feature_mask_send_sync_msg(penv, dynamic_feature_mask); register_fw_error_notifications(penv); if (!penv->fw_error_fatal_irq) register_fw_error_notifications(&penv->pdev->dev); if (!penv->fw_early_crash_irq) register_early_crash_notifications(&penv->pdev->dev); return ret; Loading
drivers/soc/qcom/icnss_private.h +2 −0 Original line number Diff line number Diff line Loading @@ -348,6 +348,8 @@ struct icnss_priv { u16 line_number; struct mutex dev_lock; bool is_hyp_disabled; uint32_t fw_error_fatal_irq; uint32_t fw_early_crash_irq; char function_name[WLFW_FUNCTION_NAME_LEN + 1]; }; Loading