Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a1c3261 authored by Anurag Chouhan's avatar Anurag Chouhan Committed by Hardik Kantilal Patel
Browse files

icnss: Defer modem graceful shutdown until probe complete



In case WLAN driver probe is in progress and modem graceful
shutdown occurs and if modem shutdown request is sent just
before the mode on request sent to firmware, firmware may end up
in illegal memory access.
To address this issue, modem notifier needs to be blocked needs for
probe to complete or max 5 seconds timeout.

CRs-Fixed: 2381846
Change-Id: I9e13a11c56059cb29e161c34df11de484f87ac5e
Signed-off-by: default avatarAnurag Chouhan <achouhan@codeaurora.org>
parent 96db99c7
Loading
Loading
Loading
Loading
+29 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -66,6 +66,8 @@
#define ICNSS_QUIRKS_DEFAULT		BIT(FW_REJUVENATE_ENABLE)
#define ICNSS_MAX_PROBE_CNT		2

#define PROBE_TIMEOUT			5000

static struct icnss_priv *penv;

unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
@@ -883,6 +885,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)

	icnss_hw_power_on(priv);

	set_bit(ICNSS_DRIVER_LOADING, &priv->state);
	reinit_completion(&penv->driver_probed);
	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
		ret = priv->ops->probe(&priv->pdev->dev);
		probe_cnt++;
@@ -892,9 +896,13 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
	if (ret < 0) {
		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
			     ret, priv->state, probe_cnt);
		complete(&penv->driver_probed);
		clear_bit(ICNSS_DRIVER_LOADING, &penv->state);
		goto out;
	}

	complete(&penv->driver_probed);
	clear_bit(ICNSS_DRIVER_LOADING, &priv->state);
	set_bit(ICNSS_DRIVER_PROBED, &priv->state);

	return 0;
@@ -1033,6 +1041,8 @@ static int icnss_driver_event_register_driver(void *data)
	if (ret)
		goto out;

	set_bit(ICNSS_DRIVER_LOADING, &penv->state);
	reinit_completion(&penv->driver_probed);
	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
		ret = penv->ops->probe(&penv->pdev->dev);
		probe_cnt++;
@@ -1042,9 +1052,13 @@ static int icnss_driver_event_register_driver(void *data)
	if (ret) {
		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
			     ret, penv->state, probe_cnt);
		clear_bit(ICNSS_DRIVER_LOADING, &penv->state);
		complete(&penv->driver_probed);
		goto power_off;
	}

	complete(&penv->driver_probed);
	clear_bit(ICNSS_DRIVER_LOADING, &penv->state);
	set_bit(ICNSS_DRIVER_PROBED, &penv->state);

	return 0;
@@ -1269,6 +1283,13 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
	if (code != SUBSYS_BEFORE_SHUTDOWN)
		return NOTIFY_OK;

	if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed &&
	    test_bit(ICNSS_DRIVER_LOADING, &priv->state)) {
		if (!wait_for_completion_timeout(&priv->driver_probed,
						 PROBE_TIMEOUT))
			icnss_pr_err("wlan driver probe timeout\n");
	}

	if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed) {
		ret = wlfw_send_modem_shutdown_msg(priv);
		if (ret < 0)
@@ -2579,6 +2600,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
			continue;
		case ICNSS_MODE_ON:
			seq_puts(s, "MODE ON DONE");
			continue;
		case ICNSS_DRIVER_LOADING:
			seq_puts(s, "WLAN DRIVER LOADING");
		}

		seq_printf(s, "UNKNOWN-%d", i);
@@ -3202,6 +3226,8 @@ static int icnss_probe(struct platform_device *pdev)

	penv = priv;

	init_completion(&priv->driver_probed);

	icnss_pr_info("Platform driver probed successfully\n");

	return 0;
@@ -3224,6 +3250,8 @@ static int icnss_remove(struct platform_device *pdev)

	icnss_debugfs_destroy(penv);

	complete_all(&penv->driver_probed);

	icnss_modem_ssr_unregister_notifier(penv);

	destroy_ramdump_device(penv->msa0_dump_dev);
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -156,6 +156,7 @@ enum icnss_driver_state {
	ICNSS_DRIVER_UNLOADING,
	ICNSS_REJUVENATE,
	ICNSS_MODE_ON,
	ICNSS_DRIVER_LOADING,
};

struct ce_irq_list {
@@ -354,6 +355,7 @@ struct icnss_priv {
	bool is_hyp_disabled;
	uint32_t fw_error_fatal_irq;
	uint32_t fw_early_crash_irq;
	struct completion driver_probed;
	char function_name[WLFW_FUNCTION_NAME_LEN + 1];
};