/*
* Copyright (c) 2017 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
 * DOC: define utility API related to the pmo component
 * called by other components
 */

#include "wlan_pmo_obj_mgmt_api.h"
#include "wlan_pmo_tgt_api.h"
#include "wlan_pmo_static_config.h"
#include "wlan_pmo_main.h"
#include "target_if_pmo.h"

QDF_STATUS pmo_init(void)
{
	QDF_STATUS status;
	struct wlan_pmo_ctx *pmo_ctx;

	PMO_ENTER();
	if (pmo_allocate_ctx() != QDF_STATUS_SUCCESS) {
		pmo_err("unable to allocate psoc ctx");
		status = QDF_STATUS_E_FAULT;
		goto out;
	}

	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_INVAL;
		goto out;
	}

	status = wlan_objmgr_register_psoc_create_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_psoc_object_created_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to register psoc create handle");
		goto out;
	}

	status = wlan_objmgr_register_psoc_destroy_handler(
			WLAN_UMAC_COMP_PMO,
			 pmo_psoc_object_destroyed_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to register psoc create handle");
		goto out;
	}

	status = wlan_objmgr_register_vdev_create_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_vdev_object_created_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to register vdev create handle");
		goto out;
	}

	status = wlan_objmgr_register_vdev_destroy_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_vdev_object_destroyed_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS)
		pmo_err("unable to register vdev create handle");
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_deinit(void)
{
	QDF_STATUS status;
	struct wlan_pmo_ctx *pmo_ctx;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		status =  QDF_STATUS_E_FAILURE;
		goto out;
	}

	status = wlan_objmgr_unregister_psoc_create_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_psoc_object_created_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to unregister psoc create handle");
		goto out;
	}

	status = wlan_objmgr_unregister_psoc_destroy_handler(
			WLAN_UMAC_COMP_PMO,
			 pmo_psoc_object_destroyed_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to unregister psoc create handle");
		goto out;
	}

	status = wlan_objmgr_unregister_vdev_create_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_vdev_object_created_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to unregister vdev create handle");
		goto out;
	}

	status = wlan_objmgr_unregister_vdev_destroy_handler(
			WLAN_UMAC_COMP_PMO,
			pmo_vdev_object_destroyed_notification,
			(void *)pmo_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("unable to unregister vdev create handle");
		goto out;
	}

out:
	pmo_free_ctx();
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_psoc_object_created_notification(
		struct wlan_objmgr_psoc *psoc, void *arg)
{
	struct pmo_psoc_priv_obj *psoc_ctx = NULL;
	QDF_STATUS status;
	struct wlan_pmo_ctx *pmo_ctx;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		QDF_ASSERT(0);
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	psoc_ctx = qdf_mem_malloc(sizeof(*psoc_ctx));
	if (psoc_ctx == NULL) {
		pmo_err("Failed to allocate pmo_psoc");
		status = QDF_STATUS_E_NOMEM;
		goto out;
	}

	status = wlan_objmgr_psoc_component_obj_attach(psoc,
			WLAN_UMAC_COMP_PMO,
			psoc_ctx,
			QDF_STATUS_SUCCESS);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("Failed to attach psoc_ctx with psoc");
		qdf_mem_free(psoc_ctx);
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}
	qdf_spinlock_create(&psoc_ctx->lock);
	qdf_wake_lock_create(&psoc_ctx->wow.wow_wake_lock, "pmo_wow_wl");
	status = qdf_event_create(&psoc_ctx->wow.target_suspend);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("target suspend event initialization failed");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}
	status = qdf_event_create(&psoc_ctx->wow.target_resume);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("target resume event initialization failed");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}
	/* Register PMO tx ops*/
	target_if_pmo_register_tx_ops(&psoc_ctx->pmo_tx_ops);
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_psoc_object_destroyed_notification(
		struct wlan_objmgr_psoc *psoc, void *arg)
{
	struct pmo_psoc_priv_obj *psoc_ctx = NULL;
	QDF_STATUS status;

	PMO_ENTER();

	psoc_ctx = pmo_psoc_get_priv(psoc);

	status = wlan_objmgr_psoc_component_obj_detach(psoc,
			WLAN_UMAC_COMP_PMO,
			psoc_ctx);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("Failed to detach psoc_ctx from psoc");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	qdf_spinlock_destroy(&psoc_ctx->lock);
	qdf_event_destroy(&psoc_ctx->wow.target_suspend);
	qdf_event_destroy(&psoc_ctx->wow.target_resume);
	qdf_wake_lock_destroy(&psoc_ctx->wow.wow_wake_lock);
	qdf_mem_zero(psoc_ctx, sizeof(*psoc_ctx));
	qdf_mem_free(psoc_ctx);
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_vdev_object_created_notification(
		struct wlan_objmgr_vdev *vdev, void *arg)
{
	struct pmo_psoc_priv_obj *psoc_ctx = NULL;
	struct wlan_objmgr_psoc *psoc;
	struct pmo_vdev_priv_obj *vdev_ctx;
	QDF_STATUS status;

	PMO_ENTER();

	psoc = pmo_vdev_get_psoc(vdev);

	psoc_ctx = pmo_psoc_get_priv(psoc);

	vdev_ctx = qdf_mem_malloc(sizeof(*vdev_ctx));
	if (vdev_ctx == NULL) {
		pmo_err("Failed to allocate vdev_ctx");
		status = QDF_STATUS_E_NOMEM;
		goto out;
	}

	status = wlan_objmgr_vdev_component_obj_attach(vdev,
			 WLAN_UMAC_COMP_PMO,
			(void *)vdev_ctx, QDF_STATUS_SUCCESS);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("Failed to attach vdev_ctx with vdev");
		qdf_mem_free(vdev_ctx);
		goto out;
	}

	qdf_spinlock_create(&vdev_ctx->pmo_vdev_lock);
	vdev_ctx->ptrn_match_enable =
		psoc_ctx->psoc_cfg.ptrn_match_enable_all_vdev;
	vdev_ctx->pmo_psoc_ctx = psoc_ctx;
	qdf_atomic_init(&vdev_ctx->gtk_err_enable);

out:
	PMO_EXIT();

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_vdev_ready(struct wlan_objmgr_vdev *vdev)
{
	QDF_STATUS status;

	PMO_ENTER();

	status = pmo_vdev_get_ref(vdev);
	if (QDF_IS_STATUS_ERROR(status))
		return status;

	/* Register static configuration with firmware */
	pmo_register_wow_wakeup_events(vdev);
	pmo_register_action_frame_patterns(vdev);

	/* Register default wow patterns with firmware */
	pmo_register_wow_default_patterns(vdev);

	pmo_vdev_put_ref(vdev);

	PMO_EXIT();

	/*
	 * The above APIs should return a status but don't.
	 * Just return success for now.
	 */
	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_vdev_object_destroyed_notification(
		struct wlan_objmgr_vdev *vdev, void *arg)
{
	struct pmo_vdev_priv_obj *vdev_ctx = NULL;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	PMO_ENTER();

	vdev_ctx = pmo_vdev_get_priv(vdev);

	status = wlan_objmgr_vdev_component_obj_detach(vdev,
			 WLAN_UMAC_COMP_PMO,
			(void *)vdev_ctx);
	if (status != QDF_STATUS_SUCCESS)
		pmo_err("Failed to detach vdev_ctx with vdev");

	qdf_spinlock_destroy(&vdev_ctx->pmo_vdev_lock);
	qdf_mem_free(vdev_ctx);

	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_register_suspend_handler(
		enum wlan_umac_comp_id id,
		pmo_psoc_suspend_handler handler,
		void *arg)
{
	struct wlan_pmo_ctx *pmo_ctx;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		QDF_ASSERT(0);
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	if (id > WLAN_UMAC_MAX_COMPONENTS || id < 0) {
		pmo_err("component id: %d is %s then valid components id",
			id, id < 0 ? "Less" : "More");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	qdf_spin_lock_bh(&pmo_ctx->lock);
	pmo_ctx->pmo_suspend_handler[id] = handler;
	pmo_ctx->pmo_suspend_handler_arg[id] = arg;
	qdf_spin_unlock_bh(&pmo_ctx->lock);
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_unregister_suspend_handler(
		enum wlan_umac_comp_id id,
		pmo_psoc_suspend_handler handler)
{
	struct wlan_pmo_ctx *pmo_ctx;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		QDF_ASSERT(0);
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	if (id > WLAN_UMAC_MAX_COMPONENTS || id < 0) {
		pmo_err("component id: %d is %s then valid components id",
			id, id < 0 ? "Less" : "More");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	qdf_spin_lock_bh(&pmo_ctx->lock);
	if (pmo_ctx->pmo_suspend_handler[id] == handler) {
		pmo_ctx->pmo_suspend_handler[id] = NULL;
		pmo_ctx->pmo_suspend_handler_arg[id] = NULL;
		qdf_spin_unlock_bh(&pmo_ctx->lock);
	} else {
		qdf_spin_unlock_bh(&pmo_ctx->lock);
		pmo_err("can't find suspend handler for component id: %d ", id);
		status = QDF_STATUS_E_FAILURE;
	}
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_register_resume_handler(
		enum wlan_umac_comp_id id,
		pmo_psoc_resume_handler handler,
		void *arg)
{
	struct wlan_pmo_ctx *pmo_ctx;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	if (id > WLAN_UMAC_MAX_COMPONENTS || id < 0) {
		pmo_err("component id: %d is %s then valid components id",
			id, id < 0 ? "Less" : "More");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	qdf_spin_lock_bh(&pmo_ctx->lock);
	pmo_ctx->pmo_resume_handler[id] = handler;
	pmo_ctx->pmo_resume_handler_arg[id] = arg;
	qdf_spin_unlock_bh(&pmo_ctx->lock);
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_unregister_resume_handler(
		enum wlan_umac_comp_id id,
		pmo_psoc_resume_handler handler)
{
	struct wlan_pmo_ctx *pmo_ctx;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	PMO_ENTER();
	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	if (id > WLAN_UMAC_MAX_COMPONENTS || id < 0) {
		pmo_err("component id: %d is %s then valid components id",
			id, id < 0 ? "Less" : "More");
		status = QDF_STATUS_E_FAILURE;
		goto out;
	}

	qdf_spin_lock_bh(&pmo_ctx->lock);
	if (pmo_ctx->pmo_resume_handler[id] == handler) {
		pmo_ctx->pmo_resume_handler[id] = NULL;
		pmo_ctx->pmo_resume_handler_arg[id] = NULL;
		qdf_spin_unlock_bh(&pmo_ctx->lock);
	} else {
		qdf_spin_unlock_bh(&pmo_ctx->lock);
		pmo_err("can't find resume handler for component id: %d ", id);
		status = QDF_STATUS_E_FAILURE;
	}
out:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc,
				      enum qdf_suspend_type suspend_type)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	QDF_STATUS resume_status;
	struct wlan_pmo_ctx *pmo_ctx;
	uint8_t i;
	pmo_psoc_suspend_handler handler;
	void *arg;

	PMO_ENTER();

	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		QDF_ASSERT(0);
		status = QDF_STATUS_E_FAILURE;
		goto exit_with_status;
	}

	/* call each component's suspend handler */
	for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) {
		qdf_spin_lock_bh(&pmo_ctx->lock);
		handler = pmo_ctx->pmo_suspend_handler[i];
		arg = pmo_ctx->pmo_suspend_handler_arg[i];
		qdf_spin_unlock_bh(&pmo_ctx->lock);

		if (!handler)
			continue;

		status = handler(psoc, arg);
		if (QDF_IS_STATUS_ERROR(status)) {
			pmo_err("component %d failed to suspend; status: %d",
				i, status);
			QDF_ASSERT(0);
			goto suspend_recovery;
		}
	}

	goto exit_with_status;

suspend_recovery:
	/* resume, starting with the last successfully suspended component */
	for (i -= 1; i >= 0; i--) {
		qdf_spin_lock_bh(&pmo_ctx->lock);
		handler = pmo_ctx->pmo_resume_handler[i];
		arg = pmo_ctx->pmo_resume_handler_arg[i];
		qdf_spin_unlock_bh(&pmo_ctx->lock);

		if (!handler)
			continue;

		resume_status = handler(psoc, arg);
		if (QDF_IS_STATUS_ERROR(resume_status)) {
			pmo_fatal("Non-recoverable failure occurred!");
			pmo_fatal("component %d failed to resume; status: %d",
				  i, resume_status);
			QDF_BUG(0);
		}
	}

exit_with_status:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_resume_all_components(struct wlan_objmgr_psoc *psoc,
				     enum qdf_suspend_type suspend_type)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	struct wlan_pmo_ctx *pmo_ctx;
	uint8_t i;
	pmo_psoc_suspend_handler handler;
	void *arg;

	PMO_ENTER();

	pmo_ctx = pmo_get_context();
	if (!pmo_ctx) {
		pmo_err("unable to get pmo ctx");
		QDF_ASSERT(0);
		status = QDF_STATUS_E_FAILURE;
		goto exit_with_status;
	}

	/* call each component's resume handler */
	for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) {
		qdf_spin_lock_bh(&pmo_ctx->lock);
		handler = pmo_ctx->pmo_resume_handler[i];
		arg = pmo_ctx->pmo_resume_handler_arg[i];
		qdf_spin_unlock_bh(&pmo_ctx->lock);

		if (!handler)
			continue;

		status = handler(psoc, arg);
		if (QDF_IS_STATUS_ERROR(status)) {
			pmo_fatal("Non-recoverable failure occurred!");
			pmo_fatal("component %d failed to resume; status: %d",
				  i, status);
			QDF_BUG(0);
		}
	}

exit_with_status:
	PMO_EXIT();

	return status;
}

QDF_STATUS pmo_register_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc,
		pmo_notify_pause_bitmap handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_notify_vdev_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	psoc_ctx->pause_bitmap_notifier = handler;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_unregister_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc,
		pmo_notify_pause_bitmap handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_notify_vdev_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	if (psoc_ctx->pause_bitmap_notifier == handler)
		psoc_ctx->pause_bitmap_notifier = NULL;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_register_get_pause_bitmap(struct wlan_objmgr_psoc *psoc,
		pmo_get_pause_bitmap handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_get_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	psoc_ctx->get_pause_bitmap = handler;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_unregister_get_pause_bitmap(struct wlan_objmgr_psoc *psoc,
		pmo_get_pause_bitmap handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_get_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	if (psoc_ctx->get_pause_bitmap == handler)
		psoc_ctx->get_pause_bitmap = NULL;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_register_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc,
		pmo_is_device_in_low_pwr_mode handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_get_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	psoc_ctx->is_device_in_low_pwr_mode = handler;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS pmo_unregister_is_device_in_low_pwr_mode(
			struct wlan_objmgr_psoc *psoc,
			pmo_is_device_in_low_pwr_mode handler)
{
	struct pmo_psoc_priv_obj *psoc_ctx;
	QDF_STATUS status;

	if (!psoc) {
		pmo_err("psoc is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	if (!handler) {
		pmo_err("pmo_get_pause_bitmap is null");
		return QDF_STATUS_E_NULL_VALUE;
	}

	status = pmo_psoc_get_ref(psoc);
	if (status != QDF_STATUS_SUCCESS) {
		pmo_err("pmo cannot get the reference out of psoc");
		return status;
	}
	psoc_ctx = pmo_psoc_get_priv(psoc);
	qdf_spin_lock_bh(&psoc_ctx->lock);
	psoc_ctx->is_device_in_low_pwr_mode = NULL;
	qdf_spin_unlock_bh(&psoc_ctx->lock);
	pmo_psoc_put_ref(psoc);

	return QDF_STATUS_SUCCESS;
}


