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

Commit f04d3824 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "net: stmmac: Low power management with IPA offload"

parents 9c2b4813 1615358d
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-19, Linaro Limited
// Copyright (c) 2018-20, Linaro Limited

#include <linux/module.h>
#include <linux/of.h>
@@ -1287,6 +1287,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
	ethqos->ipa_enabled = true;
	priv->rx_queue[IPA_DMA_RX_CH].skip_sw = true;
	priv->tx_queue[IPA_DMA_TX_CH].skip_sw = true;
	ethqos_ipa_offload_event_handler(ethqos, EV_PROBE_INIT);
#endif
	return ret;

@@ -1328,7 +1329,9 @@ static int qcom_ethqos_suspend(struct device *dev)
	struct qcom_ethqos *ethqos;
	struct net_device *ndev = NULL;
	int ret;
	int allow_suspend = 0;

	ETHQOSDBG("Suspend Enter\n");
	if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) {
		ETHQOSDBG("smmu return\n");
		return 0;
@@ -1340,6 +1343,12 @@ static int qcom_ethqos_suspend(struct device *dev)

	ndev = dev_get_drvdata(dev);

	ethqos_ipa_offload_event_handler(&allow_suspend, EV_DPM_SUSPEND);
	if (!allow_suspend) {
		enable_irq_wake(ndev->irq);
		ETHQOSDBG("Suspend Exit enable IRQ\n");
		return 0;
	}
	if (!ndev || !netif_running(ndev))
		return -EINVAL;

@@ -1372,10 +1381,18 @@ static int qcom_ethqos_resume(struct device *dev)
		return -EINVAL;
	}

	if (!ethqos->clks_suspended) {
		disable_irq_wake(ndev->irq);
		ETHQOSDBG("Resume Exit disable IRQ\n");
		return 0;
	}

	qcom_ethqos_phy_resume_clks(ethqos, ndev);

	ret = stmmac_resume(dev);

	ethqos_ipa_offload_event_handler(NULL, EV_DPM_RESUME);

	ETHQOSDBG("<--Resume Exit\n");
	return ret;
}
+17 −16
Original line number Diff line number Diff line
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
/* Copyright (c) 2020, 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
@@ -35,21 +35,6 @@
		    buf1[QTAG_ETH_TYPE_OFFSET + 1]));\
} while (0)

enum  IPA_OFFLOAD_EVENT {
	EV_INVALID = 0,
	EV_DEV_OPEN = 1,
	EV_DEV_CLOSE = 2,
	EV_IPA_READY = 3,
	EV_IPA_UC_READY = 4,
	EV_PHY_LINK_UP = 5,
	EV_PHY_LINK_DOWN = 6,
	EV_DPM_SUSPEND = 7,
	EV_DPM_RESUME = 8,
	EV_USR_SUSPEND = 9,
	EV_USR_RESUME = 10,
	EV_IPA_OFFLOAD_MAX = 11,
	EV_IPA_OFFLOAD_REMOVE = 12,
};

#ifdef CONFIG_ETH_IPA_OFFLOAD
void ethqos_ipa_offload_event_handler(void *data, int ev);
@@ -59,4 +44,20 @@ static inline void ethqos_ipa_offload_event_handler(void *data, int ev)
}
#endif

#define EV_INVALID 0
#define EV_PROBE_INIT (EV_INVALID + 1)
#define EV_IPA_ENABLED (EV_PROBE_INIT + 1)
#define EV_DEV_OPEN (EV_IPA_ENABLED + 1)
#define EV_DEV_CLOSE (EV_DEV_OPEN + 1)
#define EV_IPA_READY (EV_DEV_CLOSE + 1)
#define EV_IPA_UC_READY (EV_IPA_READY + 1)
#define EV_PHY_LINK_UP (EV_IPA_UC_READY + 1)
#define EV_PHY_LINK_DOWN (EV_PHY_LINK_UP + 1)
#define EV_DPM_SUSPEND (EV_PHY_LINK_DOWN + 1)
#define EV_DPM_RESUME (EV_DPM_SUSPEND + 1)
#define EV_USR_SUSPEND (EV_DPM_RESUME + 1)
#define EV_USR_RESUME (EV_USR_SUSPEND + 1)
#define EV_IPA_OFFLOAD_REMOVE (EV_USR_RESUME + 1)
#define EV_IPA_OFFLOAD_MAX (EV_IPA_OFFLOAD_REMOVE + 1)

#endif
+122 −107
Original line number Diff line number Diff line
@@ -995,7 +995,7 @@ static void ntn_ipa_notify_cb(void *priv, enum ipa_dp_evt_type evt,
			      unsigned long data)
{
	struct qcom_ethqos *ethqos = eth_ipa_ctx.ethqos;
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	struct sk_buff *skb = (struct sk_buff *)data;
	struct iphdr *iph = NULL;
	int stat = NET_RX_SUCCESS;
@@ -1007,14 +1007,14 @@ static void ntn_ipa_notify_cb(void *priv, enum ipa_dp_evt_type evt,
		return;
	}

	if (!ntn_ipa) {
		ETHQOSERR("Null Param ntn_ipa %pK\n", ntn_ipa);
	if (!eth_ipa) {
		ETHQOSERR("Null Param eth_ipa %pK\n", eth_ipa);
		return;
	}

	if (!ntn_ipa->ipa_offload_conn) {
	if (!eth_ipa->ipa_offload_conn) {
		ETHQOSERR("ipa_cb before offload is ready %d\n",
			  ntn_ipa->ipa_offload_conn);
			  eth_ipa->ipa_offload_conn);
		return;
	}

@@ -1065,7 +1065,7 @@ static int ethqos_ipa_offload_init(struct qcom_ethqos *pdata)
{
	struct ipa_uc_offload_intf_params in;
	struct ipa_uc_offload_out_params out;
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	struct ethhdr eth_l2_hdr_v4;
	struct ethhdr eth_l2_hdr_v6;
#ifdef ETHQOS_IPA_OFFLOAD_VLAN
@@ -1145,9 +1145,9 @@ static int ethqos_ipa_offload_init(struct qcom_ethqos *pdata)
		ret = -1;
		return ret;
	}
	ntn_ipa->ipa_client_hndl = out.clnt_hndl;
	eth_ipa->ipa_client_hndl = out.clnt_hndl;
	ETHQOSDBG("Recevied IPA Offload Client Handle %d",
		  ntn_ipa->ipa_client_hndl);
		  eth_ipa->ipa_client_hndl);

	pdata->ipa_enabled = true;
	return 0;
@@ -1155,7 +1155,7 @@ static int ethqos_ipa_offload_init(struct qcom_ethqos *pdata)

static int ethqos_ipa_offload_cleanup(struct qcom_ethqos *ethqos)
{
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	int ret = 0;

	ETHQOSDBG("begin\n");
@@ -1165,12 +1165,12 @@ static int ethqos_ipa_offload_cleanup(struct qcom_ethqos *ethqos)
		return -ENOMEM;
	}

	if (!ntn_ipa->ipa_client_hndl) {
	if (!eth_ipa->ipa_client_hndl) {
		ETHQOSERR("cleanup called with NULL IPA client handle\n");
		return -ENOMEM;
	}

	ret = ipa_uc_offload_cleanup(ntn_ipa->ipa_client_hndl);
	ret = ipa_uc_offload_cleanup(eth_ipa->ipa_client_hndl);
	if (ret) {
		ETHQOSERR("Could not cleanup IPA Offload ret %d\n", ret);
		ret = -1;
@@ -1183,14 +1183,23 @@ static int ethqos_ipa_offload_cleanup(struct qcom_ethqos *ethqos)
	return ret;
}

static bool ethqos_is_phy_link_up(struct stmmac_priv *priv)
static inline void *ethqos_get_priv(struct qcom_ethqos *ethqos)
{
	struct platform_device *pdev = ethqos->pdev;
	struct net_device *dev = platform_get_drvdata(pdev);
	struct stmmac_priv *priv = netdev_priv(dev);

	return priv;
}

static bool ethqos_is_phy_link_up(struct qcom_ethqos *ethqos)
{
	/* PHY driver initializes phydev->link=1.
	 * So, phydev->link is 1 even on booup with no PHY connected.
	 * phydev->link is valid only after adjust_link is called once.
	 * Use (pdata->oldlink != -1) to indicate phy link is not up
	 */
	struct qcom_ethqos *ethqos = priv->plat->bsp_priv;
	struct stmmac_priv *priv = ethqos_get_priv(ethqos);

	return ethqos->always_on_phy ? 1 :
	((priv->oldlink != -1) && priv->dev->phydev && priv->dev->phydev->link);
@@ -1202,11 +1211,8 @@ static ssize_t read_ipa_offload_status(struct file *file,
{
	unsigned int len = 0, buf_len = NTN_IPA_DBG_MAX_MSG_LEN;
	struct qcom_ethqos *ethqos = file->private_data;
	struct platform_device *pdev = ethqos->pdev;
	struct net_device *dev = platform_get_drvdata(pdev);
	struct stmmac_priv *priv = netdev_priv(dev);

	if (ethqos_is_phy_link_up(priv)) {
	if (ethqos_is_phy_link_up(ethqos)) {
		if (eth_ipa_ctx.ipa_offload_susp)
			len += scnprintf(buf + len, buf_len - len,
					 "IPA Offload suspended\n");
@@ -1259,7 +1265,7 @@ static ssize_t suspend_resume_ipa_offload(struct file *file,
	if (kstrtos8(in_buf, 0, &option))
		return -EFAULT;

	if (ethqos_is_phy_link_up(priv)) {
	if (ethqos_is_phy_link_up(ethqos)) {
		if (option == 1)
			ethqos_ipa_offload_event_handler(priv, EV_USR_SUSPEND);
		else if (option == 0)
@@ -1276,12 +1282,12 @@ static ssize_t read_ipa_stats(struct file *file,
			      loff_t *ppos)
{
	struct qcom_ethqos *ethqos = file->private_data;
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	char *buf;
	unsigned int len = 0, buf_len = 2000;
	ssize_t ret_cnt;

	if (!ethqos || !ntn_ipa) {
	if (!ethqos || !eth_ipa) {
		ETHQOSERR("NULL Pointer\n");
		return -EINVAL;
	}
@@ -1410,7 +1416,7 @@ static ssize_t read_ntn_dma_stats(struct file *file,
				  loff_t *ppos)
{
	struct qcom_ethqos *ethqos = file->private_data;
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	struct ethqos_ipa_stats *dma_stats = &eth_ipa_ctx.ipa_stats;
	char *buf;
	unsigned int len = 0, buf_len = 3000;
@@ -1446,7 +1452,7 @@ static ssize_t read_ntn_dma_stats(struct file *file,
			 "RX Tail Pointer Index: ",
			 dma_stats->ipa_rx_tail_ptr_indx);
	len += scnprintf(buf + len, buf_len - len, "%-50s 0x%x\n",
			 "RX Doorbell Address: ", ntn_ipa->uc_db_rx_addr);
			 "RX Doorbell Address: ", eth_ipa->uc_db_rx_addr);
	len += scnprintf(buf + len, buf_len - len, "\n");

	len += scnprintf(buf + len, buf_len - len, "%-50s 0x%x\n",
@@ -1501,7 +1507,7 @@ static ssize_t read_ntn_dma_stats(struct file *file,
	len += scnprintf(buf + len, buf_len - len, "%-50s %10lu\n",
		"TX Tail Pointer Index: ", dma_stats->ipa_tx_tail_ptr_indx);
	len += scnprintf(buf + len, buf_len - len, "%-50s 0x%x\n",
		"TX Doorbell Address: ", ntn_ipa->uc_db_tx_addr);
		"TX Doorbell Address: ", eth_ipa->uc_db_tx_addr);
	len += scnprintf(buf + len, buf_len - len, "\n");

	len += scnprintf(buf + len, buf_len - len, "%-50s 0x%x\n",
@@ -1577,22 +1583,22 @@ static const struct file_operations fops_ntn_ipa_offload_en = {

static int ethqos_ipa_cleanup_debugfs(struct qcom_ethqos *ethqos)
{
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;

	if (!ethqos || !ntn_ipa) {
	if (!ethqos || !eth_ipa) {
		ETHQOSERR("Null Param\n");
		return -ENOMEM;
	}

	if (ethqos->debugfs_dir) {
		debugfs_remove(ntn_ipa->debugfs_ipa_stats);
		ntn_ipa->debugfs_ipa_stats = NULL;
		debugfs_remove(eth_ipa->debugfs_ipa_stats);
		eth_ipa->debugfs_ipa_stats = NULL;

		debugfs_remove(ntn_ipa->debugfs_dma_stats);
		ntn_ipa->debugfs_dma_stats = NULL;
		debugfs_remove(eth_ipa->debugfs_dma_stats);
		eth_ipa->debugfs_dma_stats = NULL;

		debugfs_remove(ntn_ipa->debugfs_suspend_ipa_offload);
		ntn_ipa->debugfs_suspend_ipa_offload = NULL;
		debugfs_remove(eth_ipa->debugfs_suspend_ipa_offload);
		eth_ipa->debugfs_suspend_ipa_offload = NULL;
	}

	ETHQOSDBG("IPA debugfs Deleted Successfully\n");
@@ -1608,38 +1614,38 @@ static int ethqos_ipa_cleanup_debugfs(struct qcom_ethqos *ethqos)
 */
static int ethqos_ipa_create_debugfs(struct qcom_ethqos *ethqos)
{
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;

	ntn_ipa->debugfs_suspend_ipa_offload =
	eth_ipa->debugfs_suspend_ipa_offload =
		debugfs_create_file("suspend_ipa_offload", 0600,
				    ethqos->debugfs_dir, ethqos,
				    &fops_ntn_ipa_offload_en);
	if (!ntn_ipa->debugfs_suspend_ipa_offload ||
	    IS_ERR(ntn_ipa->debugfs_suspend_ipa_offload)) {
	if (!eth_ipa->debugfs_suspend_ipa_offload ||
	    IS_ERR(eth_ipa->debugfs_suspend_ipa_offload)) {
		ETHQOSERR("Cannot create debugfs ipa_offload_en %d\n",
			  (int)ntn_ipa->debugfs_suspend_ipa_offload);
			  (int)eth_ipa->debugfs_suspend_ipa_offload);
		goto fail;
	}

	ntn_ipa->debugfs_ipa_stats =
	eth_ipa->debugfs_ipa_stats =
		debugfs_create_file("ipa_stats", 0600,
				    ethqos->debugfs_dir, ethqos,
				    &fops_ipa_stats);
	if (!ntn_ipa->debugfs_ipa_stats ||
	    IS_ERR(ntn_ipa->debugfs_ipa_stats)) {
	if (!eth_ipa->debugfs_ipa_stats ||
	    IS_ERR(eth_ipa->debugfs_ipa_stats)) {
		ETHQOSERR("Cannot create debugfs_ipa_stats %d\n",
			  (int)ntn_ipa->debugfs_ipa_stats);
			  (int)eth_ipa->debugfs_ipa_stats);
		goto fail;
	}

	ntn_ipa->debugfs_dma_stats =
	eth_ipa->debugfs_dma_stats =
		debugfs_create_file("dma_stats", 0600,
				    ethqos->debugfs_dir, ethqos,
				    &fops_ntn_dma_stats);
	if (!ntn_ipa->debugfs_suspend_ipa_offload ||
	    IS_ERR(ntn_ipa->debugfs_suspend_ipa_offload)) {
	if (!eth_ipa->debugfs_suspend_ipa_offload ||
	    IS_ERR(eth_ipa->debugfs_suspend_ipa_offload)) {
		ETHQOSERR("Cannot create debugfs_dma_stats %d\n",
			  (int)ntn_ipa->debugfs_dma_stats);
			  (int)eth_ipa->debugfs_dma_stats);
		goto fail;
	}

@@ -1652,7 +1658,7 @@ static int ethqos_ipa_create_debugfs(struct qcom_ethqos *ethqos)

static int ethqos_ipa_offload_connect(struct qcom_ethqos *ethqos)
{
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	struct ipa_uc_offload_conn_in_params in;
	struct ipa_uc_offload_conn_out_params out;
	struct ipa_ntn_setup_info rx_setup_info = {0};
@@ -1678,7 +1684,7 @@ static int ethqos_ipa_offload_connect(struct qcom_ethqos *ethqos)
	memset(&out, 0, sizeof(out));
	memset(&profile, 0, sizeof(profile));

	in.clnt_hndl = ntn_ipa->ipa_client_hndl;
	in.clnt_hndl = eth_ipa->ipa_client_hndl;

	/* Uplink Setup */
	if (emac_emb_smmu_ctx.valid)
@@ -1798,8 +1804,8 @@ static int ethqos_ipa_offload_connect(struct qcom_ethqos *ethqos)
		goto mem_free;
	}

	ntn_ipa->uc_db_rx_addr = out.u.ntn.ul_uc_db_pa;
	ntn_ipa->uc_db_tx_addr = out.u.ntn.dl_uc_db_pa;
	eth_ipa->uc_db_rx_addr = out.u.ntn.ul_uc_db_pa;
	eth_ipa->uc_db_tx_addr = out.u.ntn.dl_uc_db_pa;

	/* Set Perf Profile For PROD/CONS Pipes */
	profile.max_supported_bw_mbps = ethqos->speed;
@@ -1857,7 +1863,7 @@ static int ethqos_ipa_offload_connect(struct qcom_ethqos *ethqos)

static int ethqos_ipa_offload_disconnect(struct qcom_ethqos *ethqos)
{
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;
	int ret = 0;

	ETHQOSDBG("- begin\n");
@@ -1867,7 +1873,7 @@ static int ethqos_ipa_offload_disconnect(struct qcom_ethqos *ethqos)
		return -ENOMEM;
	}

	ret = ipa_uc_offload_disconn_pipes(ntn_ipa->ipa_client_hndl);
	ret = ipa_uc_offload_disconn_pipes(eth_ipa->ipa_client_hndl);
	if (ret) {
		ETHQOSERR("Could not cleanup IPA Offload ret %d\n", ret);
		return ret;
@@ -2129,7 +2135,7 @@ static void ethqos_ipaucrdy_wq(struct work_struct *work)
static void ethqos_ipa_uc_ready_cb(void *user_data)
{
	struct qcom_ethqos *pdata = (struct qcom_ethqos *)user_data;
	struct ethqos_prv_ipa_data *ntn_ipa = &eth_ipa_ctx;
	struct ethqos_prv_ipa_data *eth_ipa = &eth_ipa_ctx;

	if (!pdata) {
		ETHQOSERR("Null Param pdata %pK\n", pdata);
@@ -2137,8 +2143,8 @@ static void ethqos_ipa_uc_ready_cb(void *user_data)
	}

	ETHQOSDBG("Received IPA UC ready callback\n");
	INIT_WORK(&ntn_ipa->ntn_ipa_rdy_work, ethqos_ipaucrdy_wq);
	queue_work(system_unbound_wq, &ntn_ipa->ntn_ipa_rdy_work);
	INIT_WORK(&eth_ipa->ntn_ipa_rdy_work, ethqos_ipaucrdy_wq);
	queue_work(system_unbound_wq, &eth_ipa->ntn_ipa_rdy_work);
}

static int ethqos_ipa_uc_ready(struct qcom_ethqos *pdata)
@@ -2165,10 +2171,14 @@ static int ethqos_ipa_uc_ready(struct qcom_ethqos *pdata)
void ethqos_ipa_offload_event_handler(void *data,
				      int ev)
{
	struct stmmac_priv *priv = data;
	struct qcom_ethqos *pdata = priv->plat->bsp_priv;

	ETHQOSDBG("Enter: event=%d\n", ev);

	if (ev == EV_PROBE_INIT) {
		eth_ipa_ctx.ethqos = data;
		mutex_init(&eth_ipa_ctx.ipa_lock);
		return;
	}

	IPA_LOCK();

	switch (ev) {
@@ -2181,7 +2191,7 @@ void ethqos_ipa_offload_event_handler(void *data,
			    !eth_ipa_ctx.ipa_offload_conn)
				break;

			if (!ethqos_ipa_offload_suspend(pdata))
			if (!ethqos_ipa_offload_suspend(eth_ipa_ctx.ethqos))
				eth_ipa_ctx.ipa_offload_link_down = true;
		}
		break;
@@ -2194,43 +2204,39 @@ void ethqos_ipa_offload_event_handler(void *data,

			/* Link up event is expected only after link down */
			if (eth_ipa_ctx.ipa_offload_link_down) {
				ethqos_ipa_offload_resume(pdata);
				ethqos_ipa_offload_resume(eth_ipa_ctx.ethqos);
			} else if (eth_ipa_ctx.emac_dev_ready &&
				eth_ipa_ctx.ipa_uc_ready) {
				ethqos_enable_ipa_offload(pdata);
				ethqos_enable_ipa_offload(eth_ipa_ctx.ethqos);
			}

			eth_ipa_ctx.ipa_offload_link_down = false;
		}
		break;

	case EV_DEV_OPEN:
		{
			eth_ipa_ctx.ethqos = pdata;
			ethqos_ipa_config_queues(pdata);
			ethqos_ipa_config_queues(eth_ipa_ctx.ethqos);
			eth_ipa_ctx.emac_dev_ready = true;

			if (!eth_ipa_ctx.ipa_ready)
				ethqos_ipa_ready(pdata);
				ethqos_ipa_ready(eth_ipa_ctx.ethqos);

			if (!eth_ipa_ctx.ipa_uc_ready)
					ethqos_ipa_uc_ready(pdata);
				ethqos_ipa_uc_ready(eth_ipa_ctx.ethqos);
		}
		break;

	case EV_IPA_READY:
		{
			eth_ipa_ctx.ipa_ready = true;

			if (!eth_ipa_ctx.ipa_uc_ready)
					ethqos_ipa_uc_ready(pdata);
				ethqos_ipa_uc_ready(eth_ipa_ctx.ethqos);

			if (eth_ipa_ctx.ipa_uc_ready &&
				    ethqos_is_phy_link_up(priv))
					ethqos_enable_ipa_offload(pdata);
			    ethqos_is_phy_link_up(eth_ipa_ctx.ethqos))
				ethqos_enable_ipa_offload(eth_ipa_ctx.ethqos);
		}
		break;

	case EV_IPA_UC_READY:
		{
			eth_ipa_ctx.ipa_uc_ready = true;
@@ -2240,16 +2246,16 @@ void ethqos_ipa_offload_event_handler(void *data,
				break;
			if (eth_ipa_ctx.ipa_ready) {
				if (!eth_ipa_ctx.ipa_offload_init) {
					if (!ethqos_ipa_offload_init(pdata))
					if (!ethqos_ipa_offload_init(
							eth_ipa_ctx.ethqos))
						eth_ipa_ctx.ipa_offload_init
						= true;
					}
				}
			if (ethqos_is_phy_link_up(priv))
				ethqos_enable_ipa_offload(pdata);
			if (ethqos_is_phy_link_up(eth_ipa_ctx.ethqos))
				ethqos_enable_ipa_offload(eth_ipa_ctx.ethqos);
		}
		break;

	case EV_DEV_CLOSE:
		{
			eth_ipa_ctx.emac_dev_ready = false;
@@ -2257,27 +2263,35 @@ void ethqos_ipa_offload_event_handler(void *data,
			if (eth_ipa_ctx.ipa_uc_ready)
				ipa_uc_offload_dereg_rdyCB(IPA_UC_NTN);

			ethqos_disable_ipa_offload(pdata);
			ethqos_disable_ipa_offload(eth_ipa_ctx.ethqos);

			/* reset link down on dev close */
			eth_ipa_ctx.ipa_offload_link_down = 0;
		}
		break;
	case EV_DPM_SUSPEND:
		{
			if (eth_ipa_ctx.ipa_offload_conn)
				*(int *)data = false;
			else
				*(int *)data = true;
		}
		break;
	case EV_USR_SUSPEND:
		{
			if (!eth_ipa_ctx.ipa_offload_susp &&
				!eth_ipa_ctx.ipa_offload_link_down)
				if (!ethqos_ipa_offload_suspend(pdata))
					eth_ipa_ctx.ipa_offload_susp = true;
				if (!ethqos_ipa_offload_suspend(
						eth_ipa_ctx.ethqos))
					eth_ipa_ctx.ipa_offload_susp
					= true;
		}
		break;
	case EV_DPM_RESUME:
		{
			if (eth_ipa_ctx.ipa_offload_susp) {
				if (ethqos_is_phy_link_up(priv)) {
					if (!ethqos_ipa_offload_resume(pdata))
			if (ethqos_is_phy_link_up(eth_ipa_ctx.ethqos)) {
				if (!ethqos_ipa_offload_resume(
						eth_ipa_ctx.ethqos))
					eth_ipa_ctx.ipa_offload_susp
					= false;
			} else {
@@ -2294,24 +2308,25 @@ void ethqos_ipa_offload_event_handler(void *data,
				= true;
			}
	}
		}
		break;
	case EV_USR_RESUME:
		{
			if (eth_ipa_ctx.ipa_offload_susp) {
				if (!ethqos_ipa_offload_resume(pdata))
					eth_ipa_ctx.ipa_offload_susp = false;
				if (!ethqos_ipa_offload_resume(
					eth_ipa_ctx.ethqos))
					eth_ipa_ctx.ipa_offload_susp
					= false;
			}
		}
		break;
	case EV_IPA_OFFLOAD_REMOVE:
		{
			ethqos_rx_buf_free_mem(pdata, IPA_DMA_RX_CH);
			ethqos_tx_buf_free_mem(pdata, IPA_DMA_TX_CH);
			ethqos_rx_desc_free_mem(pdata, IPA_DMA_RX_CH);
			ethqos_tx_desc_free_mem(pdata, IPA_DMA_TX_CH);
			ethqos_free_ipa_rx_queue_struct(pdata);
			ethqos_free_ipa_tx_queue_struct(pdata);
		   ethqos_rx_buf_free_mem(eth_ipa_ctx.ethqos, IPA_DMA_RX_CH);
		   ethqos_tx_buf_free_mem(eth_ipa_ctx.ethqos, IPA_DMA_TX_CH);
		   ethqos_rx_desc_free_mem(eth_ipa_ctx.ethqos, IPA_DMA_RX_CH);
		   ethqos_tx_desc_free_mem(eth_ipa_ctx.ethqos, IPA_DMA_TX_CH);
		   ethqos_free_ipa_rx_queue_struct(eth_ipa_ctx.ethqos);
		   ethqos_free_ipa_tx_queue_struct(eth_ipa_ctx.ethqos);
		}
		break;
	case EV_INVALID: