/*
 * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**=========================================================================
  
  @file  wlan_qct_pal_device.c
  
  @brief 
               
  This file implements the device specific HW access interface
  required by the WLAN Platform Abstraction Layer (WPAL)

========================================================================*/

/*===========================================================================

                      EDIT HISTORY FOR FILE


  This section contains comments describing changes made to the module.
  Notice that changes are listed in reverse chronological order.


  $Header:$ $DateTime: $ $Author: $


  when        who  what, where, why
  ----------  ---  -----------------------------------------------------------
  2011-03-01  jtj  Initial version for Linux/Android with Wcnss

===========================================================================*/

/*----------------------------------------------------------------------------
 * Include Files
 * -------------------------------------------------------------------------*/
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#ifdef EXISTS_MSM_SMSM
#include <mach/msm_smsm.h>
#else
#include <linux/version.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
#include <linux/soc/qcom/smem_state.h>
#else
#include <soc/qcom/smsm.h>
#endif
#endif
#include "wlan_qct_pal_api.h"
#include "wlan_qct_pal_device.h"
#include "wlan_hdd_main.h"
#include "linux/wcnss_wlan.h"
#include <linux/ratelimit.h>

/*----------------------------------------------------------------------------
 * Preprocessor Definitions and Constants
 * -------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * Type Declarations
 * -------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * Global Data Definitions
 * -------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * Static Variable Definitions
 * -------------------------------------------------------------------------*/

typedef struct {
   struct resource *wcnss_memory;
   void __iomem    *mmio;
   int              tx_irq;
   wpalIsrType      tx_isr;
   void            *tx_context;
   int              rx_irq;
   wpalIsrType      rx_isr;
   void            *rx_context;
   int              rx_registered;
   int              tx_registered;
   u8               rx_isr_enabled;
   u64              *rx_disable_return;
   u64              *rx_enable_return;
   u8               rx_isr_enable_failure;
   u8               rx_isr_enable_partial_failure;
   u8               tx_isr_enabled;
} wcnss_env;

static wcnss_env  gEnv;
static wcnss_env *gpEnv = NULL;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
struct smem_state {
	struct qcom_smem_state *tx_enable_state;
	unsigned tx_enable_state_bit;
} g_smem_state;
#endif

#define WPAL_READ_REGISTER_RATELIMIT_INTERVAL 20*HZ
#define WPAL_READ_REGISTER_RATELIMIT_BURST    1

static DEFINE_RATELIMIT_STATE(wpalReadRegister_rs, \
        WPAL_READ_REGISTER_RATELIMIT_INTERVAL,     \
        WPAL_READ_REGISTER_RATELIMIT_BURST);
/*----------------------------------------------------------------------------
 * Static Function Declarations and Definitions
 * -------------------------------------------------------------------------*/

/**
  @brief wpalTxIsr is the interrupt service routine which handles
         the DXE TX Complete interrupt
  
  wpalTxIsr is registered with the Operating System to handle the
  DXE TX Complete interrupt during system initialization.  When a DXE
  TX Complete interrupt occurs, it is dispatched to the handler which
  had previously been registered via wpalRegisterInterrupt.
  
  @param  irq:    Enumeration of the interrupt that occurred
  @param  dev_id: User-supplied data passed back via the ISR

  @see    wpalRegisterInterrupt

  @return IRQ_HANDLED since it is a dedicated interrupt
*/
static irqreturn_t wpalTxIsr
(
   int irq,
   void *dev_id
)
{
   if ((NULL != gpEnv) && (NULL != gpEnv->tx_isr)) {
      gpEnv->tx_isr(gpEnv->tx_context);
   }
   return IRQ_HANDLED;
}


/**
  @brief wpalRxIsr is the interrupt service routine which handles
         the DXE RX Available interrupt
  
  wpalRxIsr is registered with the Operating System to handle the
  DXE RX Available interrupt during system initalization.  When a DXE
  RX Available interrupt occurs, it is dispatched to the handler which
  had previously been registered via wpalRegisterInterrupt.
  
  @param  irq:    Enumeration of the interrupt that occurred
  @param  dev_id: User-supplied data passed back via the ISR

  @see    wpalRegisterInterrupt

  @return IRQ_HANDLED since it is a dedicated interrupt
*/
static irqreturn_t wpalRxIsr
(
   int irq,
   void *dev_id
)
{
   if ((NULL != gpEnv) && (NULL != gpEnv->rx_isr)) {
      gpEnv->rx_isr(gpEnv->rx_context);
   }
   return IRQ_HANDLED;
}

/*----------------------------------------------------------------------------
 * Externalized Function Definitions
 * -------------------------------------------------------------------------*/


/**
  @brief wpalRegisterInterrupt provides a mechansim for client
         to register support for a given interrupt

  The DXE interface supports two interrupts, TX Complete and RX
  Available.  This interface provides the mechanism whereby a client
  can register to support one of these.  It is expected that the core
  DXE implementation will invoke this API twice, once for each interrupt.
  
  @param  intType:          Enumeration of the interrupt type (TX or RX)
  @param  callbackFunction: ISR function pointer
  @param  usrCtxt:          User context passed back whenever the
                            callbackFunction is invoked

  @return SUCCESS if the registration was successful
*/

wpt_status wpalRegisterInterrupt
(
   wpt_uint32      intType,
   wpalIsrType     callbackFunction,
   void            *usrCtxt
)
{
   if (NULL == gpEnv) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   if (NULL == callbackFunction) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: invoked with NULL callback",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   switch (intType) {

   case DXE_INTERRUPT_TX_COMPLE:
      if (NULL != gpEnv->tx_isr) {
         /* TX complete handler already registered */
         WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_WARN,
                 "%s: TX interrupt handler already registered",
                 __func__);
         /* fall though and accept the new values */
      }
      gpEnv->tx_isr = callbackFunction;
      gpEnv->tx_context = usrCtxt;
      break;

   case DXE_INTERRUPT_RX_READY:
      if (NULL != gpEnv->rx_isr) {
         /* RX complete handler already registered */
         WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_WARN,
                 "%s: RX interrupt handler already registered",
                 __func__);
         /* fall though and accept the new values */
      }
      gpEnv->rx_isr = callbackFunction;
      gpEnv->rx_context = usrCtxt;
      break;

   default:
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Unknown interrupt type [%u]",
                 __func__, intType);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalUnRegisterInterrupt provides a mechansim for client
         to un-register for a given interrupt

  When DXE stop, remove registered information from PAL
  
  @param  intType:          Enumeration of the interrupt type (TX or RX)

  @return NONE
*/

void wpalUnRegisterInterrupt
(
   wpt_uint32      intType
)
{
   if (NULL == gpEnv) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return;
   }

   switch (intType) {

   case DXE_INTERRUPT_TX_COMPLE:
      disable_irq_nosync(gpEnv->tx_irq);
      if (gpEnv->tx_registered)
      {
         free_irq(gpEnv->tx_irq, gpEnv);
         gpEnv->tx_registered = 0;
      }
      gpEnv->tx_isr = NULL;
      gpEnv->tx_context = NULL;
      break;

   case DXE_INTERRUPT_RX_READY:
      disable_irq_nosync(gpEnv->rx_irq);
      if (gpEnv->rx_registered)
      {
         free_irq(gpEnv->rx_irq, gpEnv);
         gpEnv->rx_registered = 0;
      }
      gpEnv->rx_isr = NULL;
      gpEnv->rx_context = NULL;
      break;

   default:
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Unknown interrupt type [%u]",
                 __func__, intType);
      return;
   }

   return;
}

/**
  @brief wpalEnableInterrupt provides a mechansim for a client
         to request that a given interrupt be enabled

  The DXE interface supports two interrupts, TX Complete and RX
  Available.  This interface provides the mechanism whereby a client
  can request that the platform-specific adaptation layer allows a
  given interrupt to occur.  The expectation is that if a given
  interrupt is not enabled, if the interrupt occurs then the APPS CPU
  will not be interrupted.
  
  @param  intType:          Enumeration of the interrupt type (TX or RX)

  @return SUCCESS if the interrupt was enabled
*/
wpt_status wpalEnableInterrupt
(
   wpt_uint32    intType
)
{
   int ret;
   wpt_status status = eWLAN_PAL_STATUS_SUCCESS;
   
   switch (intType) 
   {
   case DXE_INTERRUPT_RX_READY:
      gpEnv->rx_enable_return = VOS_RETURN_ADDRESS;
      if (!gpEnv->rx_registered) 
      {
         gpEnv->rx_registered = 1;
         ret = request_irq(gpEnv->rx_irq, wpalRxIsr, IRQF_TRIGGER_HIGH,
                     "wcnss_wlan", gpEnv);
         if (ret) {
            WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                       "%s: RX IRQ request failure",
                       __func__);
           gpEnv->rx_isr_enable_failure = 1;
           break;
         }
      
        
         ret = enable_irq_wake(gpEnv->rx_irq);
         if (ret) {
            WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                       "%s: enable_irq_wake failed for RX IRQ",
                       __func__);
           gpEnv->rx_isr_enable_partial_failure = 1;
            /* not fatal -- keep on going */
         }
      }
      else
      {
         enable_irq(gpEnv->rx_irq);
      }
      gpEnv->rx_isr_enabled = 1;
      break;
   case DXE_INTERRUPT_TX_COMPLE:
      if (!gpEnv->tx_registered) 
      {
         gpEnv->tx_registered = 1;
         ret = request_irq(gpEnv->tx_irq, wpalTxIsr, IRQF_TRIGGER_HIGH,
                           "wcnss_wlan", gpEnv);
         if (ret) {
            WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                       "%s: TX IRQ request failure",
                       __func__);
            break;
         }
   
   
         ret = enable_irq_wake(gpEnv->tx_irq);
         if (ret) {
            WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                       "%s: enable_irq_wake failed for TX IRQ",
                       __func__);
            /* not fatal -- keep on going */
         }
      }
      else
      {
         enable_irq(gpEnv->tx_irq);
      }
      gpEnv->tx_isr_enabled = 1;
      break;
   default:
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                    "%s: unknown interrupt: %d",
                    __func__, (int)intType);
      status = eWLAN_PAL_STATUS_E_INVAL;
      break;
   }
   /* on the integrated platform there is no platform-specific
      interrupt control */
   return status;
}

/**
  @brief wpalDisableInterrupt provides a mechansim for a client
         to request that a given interrupt be disabled

  The DXE interface supports two interrupts, TX Complete and RX
  Available.  This interface provides the mechanism whereby a client
  can request that the platform-specific adaptation layer not allow a
  given interrupt to occur.  The expectation is that if a given
  interrupt is not enabled, if the interrupt occurs then the APPS CPU
  will not be interrupted.
  
  @param  intType:          Enumeration of the interrupt type (TX or RX)

  @return SUCCESS if the interrupt was disabled
*/
wpt_status wpalDisableInterrupt
(
   wpt_uint32    intType
)
{
   wpt_status status = eWLAN_PAL_STATUS_SUCCESS;

   switch (intType)
   {
   case DXE_INTERRUPT_RX_READY:
      gpEnv->rx_disable_return = VOS_RETURN_ADDRESS;
      disable_irq_nosync(gpEnv->rx_irq);
      gpEnv->rx_isr_enabled = 0;
      break;
   case DXE_INTERRUPT_TX_COMPLE:
      disable_irq_nosync(gpEnv->tx_irq);
      gpEnv->tx_isr_enabled = 0;
      break;
   default:
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                    "%s: unknown interrupt: %d",
                    __func__, (int)intType);
      status = eWLAN_PAL_STATUS_E_INVAL;
      break;
   }

   /* on the integrated platform there is no platform-specific
      interrupt control */
   return status;
}

/**
  @brief wpalWriteRegister provides a mechansim for a client
         to write data into a hardware data register

  @param  address:  Physical memory address of the register
  @param  data:     Data value to be written

  @return SUCCESS if the data was successfully written
*/
wpt_status wpalWriteRegister
(
   wpt_uint32   address,
   wpt_uint32   data
)
{
   /* if SSR is in progress, and WCNSS is not out of reset (re-init
    * not invoked), then do not access WCNSS registers */
   if (NULL == gpEnv || wcnss_device_is_shutdown() ||
        (vos_is_logp_in_progress(VOS_MODULE_ID_WDI, NULL) &&
            !vos_is_reinit_in_progress(VOS_MODULE_ID_WDI, NULL))) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   address = (address | gpEnv->wcnss_memory->start);

   if ((address < gpEnv->wcnss_memory->start) ||
       (address > gpEnv->wcnss_memory->end)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Register address 0x%0x out of range 0x%0x - 0x%0x",
                 __func__, address,
                 (u32) gpEnv->wcnss_memory->start,
                 (u32) gpEnv->wcnss_memory->end);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   if (0 != (address & 0x3)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Register address 0x%0x is not word aligned",
                 __func__, address);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   wmb();
   writel_relaxed(data, gpEnv->mmio + (address - gpEnv->wcnss_memory->start));

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalReadRegister provides a mechansim for a client
         to read data from a hardware data register

  @param  address:  Physical memory address of the register
  @param  data:     Return location for value that is read

  @return SUCCESS if the data was successfully read
*/
wpt_status wpalReadRegister
(
   wpt_uint32   address,
   wpt_uint32  *data
)
{
   /* if SSR is in progress, and WCNSS is not out of reset (re-init
    * not invoked), then do not access WCNSS registers */
   if (NULL == gpEnv || wcnss_device_is_shutdown() ||
        (vos_is_logp_in_progress(VOS_MODULE_ID_WDI, NULL) &&
            !vos_is_reinit_in_progress(VOS_MODULE_ID_WDI, NULL))) {
       /* Ratelimit wpalReadRegister failure messages which
        * can flood serial console during improper system
        * initialization or wcnss_device in shutdown state.
        * wpalRegisterInterrupt() call to wpalReadRegister is
        * likely to cause flooding. */
       if (__ratelimit(&wpalReadRegister_rs)) {
           WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                   "%s: invoked before subsystem initialized",
                   __func__);
       }
       return eWLAN_PAL_STATUS_E_INVAL;
   }

   address = (address | gpEnv->wcnss_memory->start);

   if ((address < gpEnv->wcnss_memory->start) ||
       (address > gpEnv->wcnss_memory->end)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Register address 0x%0x out of range 0x%0x - 0x%0x",
                 __func__, address,
                 (u32) gpEnv->wcnss_memory->start,
                 (u32) gpEnv->wcnss_memory->end);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   if (0 != (address & 0x3)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Register address 0x%0x is not word aligned",
                 __func__, address);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   *data = readl_relaxed(gpEnv->mmio + (address - gpEnv->wcnss_memory->start));
   rmb();

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalWriteDeviceMemory provides a mechansim for a client
         to write data into the hardware address space

  @param  address:  Start address of physical memory to be written
  @param  s_buffer: Virtual source address from which the data will
                    be read
  @param  len:      Number of bytes of data to be written

  @return SUCCESS if the data was successfully written
*/
wpt_status wpalWriteDeviceMemory
(
  wpt_uint32 address,
  wpt_uint8* s_buffer,
  wpt_uint32 len
)
{
   /* if SSR is in progress, and WCNSS is not out of reset (re-init
    * not invoked), then do not access WCNSS registers */
   if (NULL == gpEnv || wcnss_device_is_shutdown() ||
        (vos_is_logp_in_progress(VOS_MODULE_ID_WDI, NULL) &&
            !vos_is_reinit_in_progress(VOS_MODULE_ID_WDI, NULL))) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   address = (address | gpEnv->wcnss_memory->start);

   if ((address < gpEnv->wcnss_memory->start) ||
       ((address + len) > gpEnv->wcnss_memory->end)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Memory address 0x%0x len %d out of range 0x%0x - 0x%0x",
                 __func__, address, len,
                 (u32) gpEnv->wcnss_memory->start,
                 (u32) gpEnv->wcnss_memory->end);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   vos_mem_copy(gpEnv->mmio + (address - gpEnv->wcnss_memory->start),
                                                            s_buffer, len);
   wmb();

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalReadDeviceMemory provides a mechansim for a client
         to read data from the hardware address space

  @param  address:  Start address of physical memory to be read
  @param  d_buffer: Virtual destination address to which the
                    data will be written
  @param  len:      Number of bytes of data to be read

  @return SUCCESS if the data was successfully read
*/
wpt_status wpalReadDeviceMemory
(
  wpt_uint32 address,
  wpt_uint8* d_buffer,
  wpt_uint32 len
)
{
   /* if SSR is in progress, and WCNSS is not out of reset (re-init
    * not invoked), then do not access WCNSS registers */
   if (NULL == gpEnv || wcnss_device_is_shutdown() ||
        (vos_is_logp_in_progress(VOS_MODULE_ID_WDI, NULL) &&
            !vos_is_reinit_in_progress(VOS_MODULE_ID_WDI, NULL))) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   address = (address | gpEnv->wcnss_memory->start);

   if ((address < gpEnv->wcnss_memory->start) ||
       ((address + len) > gpEnv->wcnss_memory->end)) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: Memory address 0x%0x len %d out of range 0x%0x - 0x%0x",
                 __func__, address, len,
                 (u32) gpEnv->wcnss_memory->start,
                 (u32) gpEnv->wcnss_memory->end);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   vos_mem_copy(d_buffer,
                   gpEnv->mmio + (address - gpEnv->wcnss_memory->start), len);
   rmb();

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalDeviceInit provides a mechanism to initialize the DXE
         platform adaptation

  @param  deviceCB:  Implementation-specific device control block

  @see    wpalDeviceClose

  @return SUCCESS if the DXE abstraction was opened
*/
wpt_status wpalDeviceInit
(
   void * devHandle
)
{
   struct device *wcnss_device = (struct device *)devHandle;
   struct resource *wcnss_memory;
   int tx_irq;
   int rx_irq;

   if (NULL != gpEnv) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: invoked  after subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   if (NULL == wcnss_device) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: invalid device",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

   wcnss_memory = wcnss_wlan_get_memory_map(wcnss_device);
   if (NULL == wcnss_memory) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: WCNSS memory map unavailable",
                 __func__);
      return eWLAN_PAL_STATUS_E_FAILURE;
   }

   tx_irq = wcnss_wlan_get_dxe_tx_irq(wcnss_device);
   if (0 > tx_irq) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: WCNSS TX IRQ unavailable",
                 __func__);
      return eWLAN_PAL_STATUS_E_FAILURE;
   }

   rx_irq = wcnss_wlan_get_dxe_rx_irq(wcnss_device);
   if (0 > rx_irq) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: WCNSS RX IRQ unavailable",
                 __func__);
      return eWLAN_PAL_STATUS_E_FAILURE;
   }

   gpEnv = &gEnv;
   if (NULL == gpEnv) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: memory allocation failure",
                 __func__);
      return eWLAN_PAL_STATUS_E_NOMEM;
   }

   memset(gpEnv, 0, sizeof(*gpEnv));

   gpEnv->wcnss_memory = wcnss_memory;
   gpEnv->tx_irq = tx_irq;
   gpEnv->rx_irq = rx_irq;

   /* note the we don't invoke request_mem_region().
      the memory described by wcnss_memory encompases the entire
      register space (including BT and FM) and we do not want
      exclusive access to that memory */

   gpEnv->mmio = ioremap(wcnss_memory->start, resource_size(wcnss_memory));

   if (NULL == gpEnv->mmio) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: memory remap failure",
                 __func__);
      goto err_ioremap;
   }

   gpEnv->tx_registered = 0;
   gpEnv->rx_registered = 0;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
   g_smem_state.tx_enable_state = qcom_smem_state_get(wcnss_device,
                                      "tx-enable",
                                      &g_smem_state.tx_enable_state_bit);
   if (IS_ERR(g_smem_state.tx_enable_state)) {
       WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: qcom_smem_state_get failed", __func__);

      goto err_ioremap;
   }
#endif

   /* successfully allocated environment, memory and IRQs */
   return eWLAN_PAL_STATUS_SUCCESS;

 err_ioremap:
   gpEnv = NULL;

   return eWLAN_PAL_STATUS_E_FAILURE;

}


/**
  @brief wpalDeviceClose provides a mechanism to deinitialize the DXE
         platform adaptation

  @param  deviceCB:  Implementation-specific device control block

  @see    wpalDeviceOpen

  @return SUCCESS if the DXE abstraction was closed
*/
wpt_status wpalDeviceClose
(
   void * deviceCB
 )
{
   if (NULL == gpEnv) {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_INFO,
                 "%s: invoked before subsystem initialized",
                 __func__);
      return eWLAN_PAL_STATUS_E_INVAL;
   }

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
   qcom_smem_state_put(g_smem_state.tx_enable_state);
   memset(&g_smem_state, 0, sizeof(g_smem_state));
#endif

   if (gpEnv->rx_registered)
   {
      free_irq(gpEnv->rx_irq, gpEnv);
   }
   if (gpEnv->tx_registered)
   {
      free_irq(gpEnv->tx_irq, gpEnv);
   }
   iounmap(gpEnv->mmio);
   gpEnv = NULL;

   return eWLAN_PAL_STATUS_SUCCESS;
}

/**
  @brief wpalNotifySmsm provides a mechansim for a client to 
         notify SMSM to start DXE engine and/or condition of Tx
         ring buffer

  @param  clrSt:   bit(s) to be cleared on the MASK 
  @param  setSt:   bit(s) to be set on the MASK

  @return SUCCESS if the operation is successful
*/
wpt_status wpalNotifySmsm
(
   wpt_uint32                            clrSt,
   wpt_uint32                            setSt
)
{
   int rc;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
   rc =  qcom_smem_state_update_bits(g_smem_state.tx_enable_state,
                                     clrSt, setSt);
#else
   rc = smsm_change_state(SMSM_APPS_STATE, clrSt, setSt);
#endif
   if(0 != rc) 
   {
      WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
                 "%s: smsm_change_state failed",
                 __func__);
      return eWLAN_PAL_STATUS_E_FAILURE;
   }
   return eWLAN_PAL_STATUS_SUCCESS;
}

/* Get Interrupt Status */
int wpal_get_int_state
(
   wpt_uint32    intType
)
{
   int ret;
   ret = 0;

   switch (intType)
   {
   case DXE_INTERRUPT_RX_READY:
      if (gpEnv->rx_isr_enabled == 1)
          ret = 1;
      break;
   case DXE_INTERRUPT_TX_COMPLE:
      if (gpEnv->tx_isr_enabled == 1)
          ret = 1;
      break;
   default:
      break;
   }

   return ret;
}
