/*
 * Copyright (c) 2013-2014,2016-2017,2019 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.
 */


#include <osdep.h>
#include "a_types.h"
#include <athdefs.h>
#include "osapi_linux.h"
#include "hif_msg_based.h"
#include "if_pci.h"
#include "copy_engine_api.h"
#include "copy_engine_internal.h"
#include "adf_os_lock.h"
#include "hif_pci.h"
#include "regtable.h"
#include <vos_getBin.h>
#include "epping_main.h"
#include "adf_trace.h"
#include "vos_api.h"

#define CE_POLL_TIMEOUT 10 /* ms */

static int war1_allow_sleep;
extern int hif_pci_war1;

/*
 * Support for Copy Engine hardware, which is mainly used for
 * communication between Host and Target over a PCIe interconnect.
 */

/**
 * enum hif_ce_event_type - HIF copy engine event type
 * @HIF_RX_DESC_POST: event recorded before updating write index of RX ring.
 * @HIF_RX_DESC_COMPLETION: event recorded before updating sw index of RX ring.
 * @HIF_TX_GATHER_DESC_POST: post gather desc. (no write index update)
 * @HIF_TX_DESC_POST: event recorded before updating write index of TX ring.
 * @HIF_TX_DESC_COMPLETION: event recorded before updating sw index of TX ring.
 */
enum hif_ce_event_type {
	HIF_RX_DESC_POST,
	HIF_RX_DESC_COMPLETION,
	HIF_TX_GATHER_DESC_POST,
	HIF_TX_DESC_POST,
	HIF_TX_DESC_COMPLETION,
};

static struct hif_pci_softc *hif_sc = NULL;
/**
 * target_reset_work_handler() - Work queue handler to reset target
 * @sc: work queue handle
 */
void target_reset_work_handler(struct work_struct *sc)
{
	if (hif_sc)
		vos_device_crashed(hif_sc->dev);
}

static DECLARE_WORK(target_reset_work, target_reset_work_handler);

/**
 * ce_target_reset() - Trigger SSR
 * @sc: hif layer handle
 *
 * Once hw error is hit, SSR would be triggered.
 */
void ce_target_reset(struct hif_pci_softc *sc)
{
	hif_sc = sc;
	adf_os_warn(1);
	adf_os_print("Trigger SSR.\n");
	schedule_work(&target_reset_work);
}

/**
 * ce_idx_invalid() - check whether a CE index is valid
 * @ring: ring handle
 * @idx: CE idx
 */
static bool ce_idx_invalid(struct CE_ring_state *ring, unsigned int idx)
{
	if (ring->nentries <= idx) {
		adf_os_print("hw index %d is not correct.\n", idx);
		return TRUE;
	} else {
		return FALSE;
	}
}

#ifdef CONFIG_SLUB_DEBUG_ON

/**
 * struct hif_ce_event - structure for detailing a ce event
 * @type: what the event was
 * @time: when it happened
 * @descriptor: descriptor enqueued or dequeued
 * @memory: virtual address that was used
 * @index: location of the descriptor in the ce ring;
 */
struct hif_ce_desc_event {
	uint16_t index;
	enum hif_ce_event_type type;
	uint64_t time;
	CE_desc descriptor;
	void* memory;
};

/* max history to record per copy engine */
#define HIF_CE_HISTORY_MAX 512
adf_os_atomic_t hif_ce_desc_history_index[CE_COUNT_MAX];
struct hif_ce_desc_event hif_ce_desc_history[CE_COUNT_MAX][HIF_CE_HISTORY_MAX];
static void CE_init_ce_desc_event_log(int ce_id, int size);

/**
 * get_next_record_index() - get the next record index
 * @table_index: atomic index variable to increment
 * @array_size: array size of the circular buffer
 *
 * Increment the atomic index and reserve the value.
 * Takes care of buffer wrap.
 * Guaranteed to be thread safe as long as fewer than array_size contexts
 * try to access the array.  If there are more than array_size contexts
 * trying to access the array, full locking of the recording process would
 * be needed to have sane logging.
 */
int get_next_record_index(adf_os_atomic_t *table_index, int array_size)
{
	int record_index = adf_os_atomic_inc_return(table_index);
	if (record_index == array_size)
		adf_os_atomic_sub(array_size, table_index);

	while (record_index >= array_size)
		record_index -= array_size;
	return record_index;
}

/**
 * hif_record_ce_desc_event() - record ce descriptor events
 * @ce_id: which ce is the event occuring on
 * @type: what happened
 * @descriptor: pointer to the descriptor posted/completed
 * @memory: virtual address of buffer related to the descriptor
 * @index: index that the descriptor was/will be at.
 */
static void hif_record_ce_desc_event(int ce_id, enum hif_ce_event_type type,
		CE_desc *descriptor, void *memory, int index)
{
	int record_index = get_next_record_index(
			&hif_ce_desc_history_index[ce_id], HIF_CE_HISTORY_MAX);

	struct hif_ce_desc_event *event =
		&hif_ce_desc_history[ce_id][record_index];
	event->type = type;
	event->time = adf_get_boottime();
	event->descriptor = *descriptor;
	event->memory = memory;
	event->index = index;
}

/**
 * CE_init_ce_desc_event_log() - initialize the ce event log
 * @ce_id: copy engine id for which we are initializing the log
 * @size: size of array to dedicate
 *
 * Currently the passed size is ignored in favor of a precompiled value.
 */
static void CE_init_ce_desc_event_log(int ce_id, int size)
{
	adf_os_atomic_init(&hif_ce_desc_history_index[ce_id]);
}
#else
static inline void hif_record_ce_desc_event(
		int ce_id, enum hif_ce_event_type type,
		CE_desc *descriptor, void* memory,
		int index)
{
}

static inline void CE_init_ce_desc_event_log(int ce_id, int size)
{
}
#endif

/*
 * A single CopyEngine (CE) comprises two "rings":
 *   a source ring
 *   a destination ring
 *
 * Each ring consists of a number of descriptors which specify
 * an address, length, and meta-data.
 *
 * Typically, one side of the PCIe interconnect (Host or Target)
 * controls one ring and the other side controls the other ring.
 * The source side chooses when to initiate a transfer and it
 * chooses what to send (buffer address, length). The destination
 * side keeps a supply of "anonymous receive buffers" available and
 * it handles incoming data as it arrives (when the destination
 * recieves an interrupt).
 *
 * The sender may send a simple buffer (address/length) or it may
 * send a small list of buffers.  When a small list is sent, hardware
 * "gathers" these and they end up in a single destination buffer
 * with a single interrupt.
 *
 * There are several "contexts" managed by this layer -- more, it
 * may seem -- than should be needed. These are provided mainly for
 * maximum flexibility and especially to facilitate a simpler HIF
 * implementation. There are per-CopyEngine recv, send, and watermark
 * contexts. These are supplied by the caller when a recv, send,
 * or watermark handler is established and they are echoed back to
 * the caller when the respective callbacks are invoked. There is
 * also a per-transfer context supplied by the caller when a buffer
 * (or sendlist) is sent and when a buffer is enqueued for recv.
 * These per-transfer contexts are echoed back to the caller when
 * the buffer is sent/received.
 */

/*
 * Guts of CE_send, used by both CE_send and CE_sendlist_send.
 * The caller takes responsibility for any needed locking.
 */
int
CE_completed_send_next_nolock(struct CE_state *CE_state,
                              void **per_CE_contextp,
                              void **per_transfer_contextp,
                              CE_addr_t *bufferp,
                              unsigned int *nbytesp,
                              unsigned int *transfer_idp,
                              unsigned int *sw_idx,
                              unsigned int *hw_idx);

void WAR_CE_SRC_RING_WRITE_IDX_SET(struct hif_pci_softc *sc,
                                   void __iomem *targid,
                                   u32 ctrl_addr,
                                   unsigned int write_index)
{
        if (hif_pci_war1) {
                void __iomem *indicator_addr;

                indicator_addr = targid + ctrl_addr + DST_WATERMARK_ADDRESS;

                if (!war1_allow_sleep && ctrl_addr == CE_BASE_ADDRESS(CDC_WAR_DATA_CE)) {
                        A_PCI_WRITE32(indicator_addr,
                                    (CDC_WAR_MAGIC_STR | write_index));
                } else {
                        unsigned long irq_flags;
                        local_irq_save(irq_flags);
                        A_PCI_WRITE32(indicator_addr, 1);

                        /*
                         * PCIE write waits for ACK in IPQ8K, there is no
                         * need to read back value.
                         */
                        (void)A_PCI_READ32(indicator_addr);
                        (void)A_PCI_READ32(indicator_addr); /* conservative */

                        CE_SRC_RING_WRITE_IDX_SET(targid,
                                                  ctrl_addr,
                                                  write_index);

                        A_PCI_WRITE32(indicator_addr, 0);
                        local_irq_restore(irq_flags);
                }
        } else
                CE_SRC_RING_WRITE_IDX_SET(targid, ctrl_addr, write_index);
}

int
CE_send_nolock(struct CE_handle *copyeng,
               void *per_transfer_context,
               CE_addr_t buffer,
               unsigned int nbytes,
               unsigned int transfer_id,
               unsigned int flags)
{
    int status;
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct CE_ring_state *src_ring = CE_state->src_ring;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);
    unsigned int nentries_mask = src_ring->nentries_mask;
    unsigned int sw_index = src_ring->sw_index;
    unsigned int write_index = src_ring->write_index;

    A_TARGET_ACCESS_BEGIN_RET(targid);
    if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index-1) <= 0)) {
        OL_ATH_CE_PKT_ERROR_COUNT_INCR(sc,CE_RING_DELTA_FAIL);
        status = A_ERROR;
        A_TARGET_ACCESS_END_RET(targid);
        return status;
    }
    {
        enum hif_ce_event_type event_type = HIF_TX_GATHER_DESC_POST;
        struct CE_src_desc *src_ring_base = (struct CE_src_desc *)src_ring->base_addr_owner_space;
        struct CE_src_desc *shadow_base = (struct CE_src_desc *)src_ring->shadow_base;
        struct CE_src_desc *src_desc = CE_SRC_RING_TO_DESC(src_ring_base, write_index);
        struct CE_src_desc *shadow_src_desc = CE_SRC_RING_TO_DESC(shadow_base, write_index);

        /* Update source descriptor */
        shadow_src_desc->src_ptr   = buffer;
        shadow_src_desc->meta_data = transfer_id;

        /*
         * Set the swap bit if:
         *   typical sends on this CE are swapped (host is big-endian) and
         *   this send doesn't disable the swapping (data is not bytestream)
         */
        shadow_src_desc->byte_swap =
            (((CE_state->attr_flags & CE_ATTR_BYTE_SWAP_DATA) != 0) &
            ((flags & CE_SEND_FLAG_SWAP_DISABLE) == 0));
        shadow_src_desc->gather    = ((flags & CE_SEND_FLAG_GATHER) != 0);
        shadow_src_desc->nbytes    = nbytes;

        *src_desc = *shadow_src_desc;

        src_ring->per_transfer_context[write_index] = per_transfer_context;

        /* Update Source Ring Write Index */
        write_index = CE_RING_IDX_INCR(nentries_mask, write_index);


        /* WORKAROUND */
        if (!shadow_src_desc->gather) {
            event_type = HIF_TX_DESC_POST;
            WAR_CE_SRC_RING_WRITE_IDX_SET(sc, targid, ctrl_addr, write_index);
        }

        /* src_ring->write index hasn't been updated event though the register
         * has allready been written to.
         */
        hif_record_ce_desc_event(CE_state->id, event_type,
                        (CE_desc *) shadow_src_desc, per_transfer_context,
                        src_ring->write_index);

        src_ring->write_index = write_index;
        status = A_OK;
    }
    A_TARGET_ACCESS_END_RET(targid);

    return status;
}

int
CE_send(struct CE_handle *copyeng,
        void *per_transfer_context,
        CE_addr_t buffer,
        unsigned int nbytes,
        unsigned int transfer_id,
        unsigned int flags)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;
    int status;


    adf_os_spin_lock_bh(&sc->target_lock);
    status = CE_send_nolock(copyeng, per_transfer_context, buffer, nbytes, transfer_id, flags);
    adf_os_spin_unlock_bh(&sc->target_lock);

    return status;
}

unsigned int
CE_sendlist_sizeof(void)
{
   return sizeof(struct CE_sendlist);
}

void
CE_sendlist_init(struct CE_sendlist *sendlist)
{
    struct CE_sendlist_s *sl = (struct CE_sendlist_s *)sendlist;
    sl->num_items=0;
}

int
CE_sendlist_buf_add(struct CE_sendlist *sendlist,
                    CE_addr_t buffer,
                    unsigned int nbytes,
                    u_int32_t flags)
{
    struct CE_sendlist_s *sl = (struct CE_sendlist_s *)sendlist;
    unsigned int num_items = sl->num_items;
    struct CE_sendlist_item *item;

	if (num_items >= CE_SENDLIST_ITEMS_MAX) {
		A_ASSERT(num_items < CE_SENDLIST_ITEMS_MAX);
		return A_NO_RESOURCE;
	}

    item = &sl->item[num_items];
    item->send_type = CE_SIMPLE_BUFFER_TYPE;
    item->data = buffer;
    item->u.nbytes = nbytes;
    item->flags = flags;
    sl->num_items = num_items+1;
	return A_OK;
}

int
CE_sendlist_send(struct CE_handle *copyeng,
                 void *per_transfer_context,
                 struct CE_sendlist *sendlist,
                 unsigned int transfer_id)
{
    int status = -ENOMEM;
    struct CE_sendlist_s *sl = (struct CE_sendlist_s *)sendlist;
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct CE_ring_state *src_ring = CE_state->src_ring;
    struct hif_pci_softc *sc = CE_state->sc;
    unsigned int nentries_mask = src_ring->nentries_mask;
    unsigned int num_items = sl->num_items;
    unsigned int sw_index ;
    unsigned int write_index ;

    A_ASSERT((num_items > 0) && (num_items < src_ring->nentries));

    adf_os_spin_lock_bh(&sc->target_lock);
    sw_index = src_ring->sw_index;
    write_index = src_ring->write_index;

    if (CE_RING_DELTA(nentries_mask, write_index, sw_index-1) >= num_items) {
        struct CE_sendlist_item *item;
        int i;

        /* handle all but the last item uniformly */
        for (i = 0; i < num_items-1; i++) {
            item = &sl->item[i];
            /* TBDXXX: Support extensible sendlist_types? */
            A_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE);
            status = CE_send_nolock(copyeng, CE_SENDLIST_ITEM_CTXT,
                                    (CE_addr_t)item->data, item->u.nbytes,
                                    transfer_id,
                                    item->flags | CE_SEND_FLAG_GATHER);
            A_ASSERT(status == A_OK);
        }
        /* provide valid context pointer for final item */
        item = &sl->item[i];
        /* TBDXXX: Support extensible sendlist_types? */
        A_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE);
        status = CE_send_nolock(copyeng, per_transfer_context,
                                (CE_addr_t)item->data, item->u.nbytes,
                                transfer_id, item->flags);
        A_ASSERT(status == A_OK);
        NBUF_UPDATE_TX_PKT_COUNT((adf_nbuf_t)per_transfer_context,
                              NBUF_TX_PKT_CE);
        DPTRACE(adf_dp_trace((adf_nbuf_t)per_transfer_context,
                ADF_DP_TRACE_CE_PACKET_PTR_RECORD,
               (uint8_t *)&(((adf_nbuf_t)per_transfer_context)->data),
               sizeof(((adf_nbuf_t)per_transfer_context)->data), ADF_TX));
    } else {
        /*
         * Probably not worth the additional complexity to support
         * partial sends with continuation or notification.  We expect
         * to use large rings and small sendlists. If we can't handle
         * the entire request at once, punt it back to the caller.
         */
    }
    adf_os_spin_unlock_bh(&sc->target_lock);

    return status;
}

int
CE_recv_buf_enqueue(struct CE_handle *copyeng,
                    void *per_recv_context,
                    CE_addr_t buffer)
{
    int status;
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct CE_ring_state *dest_ring = CE_state->dest_ring;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);
    unsigned int nentries_mask = dest_ring->nentries_mask;
    unsigned int write_index ;
    unsigned int sw_index;
    int val = 0;

    adf_os_spin_lock_bh(&sc->target_lock);
    write_index = dest_ring->write_index;
    sw_index = dest_ring->sw_index;

    A_TARGET_ACCESS_BEGIN_RET_EXT(targid, val);
    if (val == -1) {
        adf_os_spin_unlock_bh(&sc->target_lock);
        return val;
    }

    if (CE_RING_DELTA(nentries_mask, write_index, sw_index-1) > 0) {
        struct CE_dest_desc *dest_ring_base = (struct CE_dest_desc *)dest_ring->base_addr_owner_space;
        struct CE_dest_desc *dest_desc = CE_DEST_RING_TO_DESC(dest_ring_base, write_index);

        /* Update destination descriptor */
        dest_desc->dest_ptr = buffer;
        dest_desc->info.nbytes = 0; /* NB: Enable CE_completed_recv_next_nolock to
								protect against race between DRRI update and
								desc update */

        dest_ring->per_transfer_context[write_index] = per_recv_context;

        hif_record_ce_desc_event(CE_state->id, HIF_RX_DESC_POST,
                        (CE_desc *) dest_desc, per_recv_context,
                        write_index);

        /* Update Destination Ring Write Index */
        write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
        CE_DEST_RING_WRITE_IDX_SET(targid, ctrl_addr, write_index);
        dest_ring->write_index = write_index;
        status = A_OK;
    } else {
        status = A_ERROR;
    }
    A_TARGET_ACCESS_END_RET_EXT(targid, val);
    if (val == -1) {
        adf_os_spin_unlock_bh(&sc->target_lock);
        return val;
    }

    adf_os_spin_unlock_bh(&sc->target_lock);

    return status;
}

void
CE_send_watermarks_set(struct CE_handle *copyeng,
                       unsigned int low_alert_nentries,
                       unsigned int high_alert_nentries)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);

    adf_os_spin_lock(&sc->target_lock);
    CE_SRC_RING_LOWMARK_SET(targid, ctrl_addr, low_alert_nentries);
    CE_SRC_RING_HIGHMARK_SET(targid, ctrl_addr, high_alert_nentries);
    adf_os_spin_unlock(&sc->target_lock);
}

void
CE_recv_watermarks_set(struct CE_handle *copyeng,
                       unsigned int low_alert_nentries,
                       unsigned int high_alert_nentries)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);

    adf_os_spin_lock(&sc->target_lock);
    CE_DEST_RING_LOWMARK_SET(targid, ctrl_addr, low_alert_nentries);
    CE_DEST_RING_HIGHMARK_SET(targid, ctrl_addr, high_alert_nentries);
    adf_os_spin_unlock(&sc->target_lock);
}

unsigned int
CE_send_entries_avail(struct CE_handle *copyeng)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct CE_ring_state *src_ring = CE_state->src_ring;
    struct hif_pci_softc *sc = CE_state->sc;
    unsigned int nentries_mask = src_ring->nentries_mask;
    unsigned int sw_index;
    unsigned int write_index;

    adf_os_spin_lock(&sc->target_lock);
    sw_index = src_ring->sw_index;
    write_index = src_ring->write_index;
    adf_os_spin_unlock(&sc->target_lock);

    return CE_RING_DELTA(nentries_mask, write_index, sw_index-1);
}

unsigned int
CE_recv_entries_avail(struct CE_handle *copyeng)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct CE_ring_state *dest_ring = CE_state->dest_ring;
    struct hif_pci_softc *sc = CE_state->sc;
    unsigned int nentries_mask = dest_ring->nentries_mask;
    unsigned int sw_index;
    unsigned int write_index;

    adf_os_spin_lock(&sc->target_lock);
    sw_index = dest_ring->sw_index;
    write_index = dest_ring->write_index;
    adf_os_spin_unlock(&sc->target_lock);

    return CE_RING_DELTA(nentries_mask, write_index, sw_index-1);
}

/*
 * Guts of CE_send_entries_done.
 * The caller takes responsibility for any necessary locking.
 */
unsigned int
CE_send_entries_done_nolock(struct hif_pci_softc *sc, struct CE_state *CE_state)
{
    struct CE_ring_state *src_ring = CE_state->src_ring;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    A_target_id_t targid = TARGID(sc);
    unsigned int nentries_mask = src_ring->nentries_mask;
    unsigned int sw_index;
    unsigned int read_index;

    sw_index = src_ring->sw_index;
    read_index = CE_SRC_RING_READ_IDX_GET(targid, ctrl_addr);

    return CE_RING_DELTA(nentries_mask, sw_index, read_index);
}

unsigned int
CE_send_entries_done(struct CE_handle *copyeng)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;
    unsigned int nentries;

    adf_os_spin_lock(&sc->target_lock);
    nentries = CE_send_entries_done_nolock(sc, CE_state);
    adf_os_spin_unlock(&sc->target_lock);

    return nentries;
}

/*
 * Guts of CE_recv_entries_done.
 * The caller takes responsibility for any necessary locking.
 */
unsigned int
CE_recv_entries_done_nolock(struct hif_pci_softc *sc, struct CE_state *CE_state)
{
    struct CE_ring_state *dest_ring = CE_state->dest_ring;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    A_target_id_t targid = TARGID(sc);
    unsigned int nentries_mask = dest_ring->nentries_mask;
    unsigned int sw_index;
    unsigned int read_index;

    sw_index = dest_ring->sw_index;
    read_index = CE_DEST_RING_READ_IDX_GET(targid, ctrl_addr);

    return CE_RING_DELTA(nentries_mask, sw_index, read_index);
}

unsigned int
CE_recv_entries_done(struct CE_handle *copyeng)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;
    unsigned int nentries;

    adf_os_spin_lock(&sc->target_lock);
    nentries = CE_recv_entries_done_nolock(sc, CE_state);
    adf_os_spin_unlock(&sc->target_lock);

    return nentries;
}

/* Debug support */
void *ce_debug_cmplrn_context; /* completed recv next context */
void *ce_debug_cnclsn_context; /* cancel send next context */
void *ce_debug_rvkrn_context;  /* revoke receive next context */
void *ce_debug_cmplsn_context; /* completed send next context */


/*
 * Guts of CE_completed_recv_next.
 * The caller takes responsibility for any necessary locking.
 */
int
CE_completed_recv_next_nolock(struct CE_state *CE_state,
                              void **per_CE_contextp,
                              void **per_transfer_contextp,
                              CE_addr_t *bufferp,
                              unsigned int *nbytesp,
                              unsigned int *transfer_idp,
                              unsigned int *flagsp)
{
    int status;
    struct CE_ring_state *dest_ring = CE_state->dest_ring;
    unsigned int nentries_mask = dest_ring->nentries_mask;
    unsigned int sw_index = dest_ring->sw_index;

    struct CE_dest_desc *dest_ring_base = (struct CE_dest_desc *)dest_ring->base_addr_owner_space;
    struct CE_dest_desc *dest_desc = CE_DEST_RING_TO_DESC(dest_ring_base, sw_index);
    int nbytes;
    struct dest_desc_info dest_desc_info;

    /*
     * By copying the dest_desc_info element to local memory, we could
     * avoid extra memory read from non-cachable memory.
     */
    dest_desc_info = dest_desc->info;
    nbytes = dest_desc_info.nbytes;
    if (nbytes == 0) {
        /*
         * This closes a relatively unusual race where the Host
         * sees the updated DRRI before the update to the
         * corresponding descriptor has completed. We treat this
         * as a descriptor that is not yet done.
         */
        status = A_ERROR;
        goto done;
    }

    hif_record_ce_desc_event(CE_state->id, HIF_RX_DESC_COMPLETION,
                        (CE_desc *) dest_desc,
                        dest_ring->per_transfer_context[sw_index],
                        sw_index);

    dest_desc->info.nbytes = 0;

    /* Return data from completed destination descriptor */
    *bufferp      = (CE_addr_t)(dest_desc->dest_ptr);
    *nbytesp      = nbytes;
    *transfer_idp = dest_desc_info.meta_data;
    *flagsp       = (dest_desc_info.byte_swap) ?  CE_RECV_FLAG_SWAPPED : 0;

    if (per_CE_contextp) {
        *per_CE_contextp = CE_state->recv_context;
    }

    ce_debug_cmplrn_context = dest_ring->per_transfer_context[sw_index];
    if (per_transfer_contextp) {
        *per_transfer_contextp = ce_debug_cmplrn_context;
    }
    dest_ring->per_transfer_context[sw_index] = 0; /* sanity */

    /* Update sw_index */
    sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
    dest_ring->sw_index = sw_index;
    status = A_OK;

done:
    return status;
}

int
CE_completed_recv_next(struct CE_handle *copyeng,
                       void **per_CE_contextp,
                       void **per_transfer_contextp,
                       CE_addr_t *bufferp,
                       unsigned int *nbytesp,
                       unsigned int *transfer_idp,
                       unsigned int *flagsp)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;
    int status;


    adf_os_spin_lock_bh(&sc->target_lock);
    status = CE_completed_recv_next_nolock(CE_state, per_CE_contextp, per_transfer_contextp,
                                               bufferp, nbytesp, transfer_idp, flagsp);
    adf_os_spin_unlock_bh(&sc->target_lock);

    return status;
}

/* NB: Modeled after CE_completed_recv_next_nolock */
A_STATUS
CE_revoke_recv_next(struct CE_handle *copyeng,
                    void **per_CE_contextp,
                    void **per_transfer_contextp,
                    CE_addr_t *bufferp)
{
    struct CE_state *CE_state;
    struct CE_ring_state *dest_ring;
    unsigned int nentries_mask;
    unsigned int sw_index;
    unsigned int write_index;
    A_STATUS status;
    struct hif_pci_softc *sc;

    CE_state = (struct CE_state *)copyeng;
    dest_ring = CE_state->dest_ring;
    if (!dest_ring) {
        return A_ERROR;
    }

    sc = CE_state->sc;
    adf_os_spin_lock(&sc->target_lock);
    nentries_mask = dest_ring->nentries_mask;
    sw_index = dest_ring->sw_index;
    write_index = dest_ring->write_index;
    if (write_index != sw_index) {
        struct CE_dest_desc *dest_ring_base = (struct CE_dest_desc *)dest_ring->base_addr_owner_space;
        struct CE_dest_desc *dest_desc = CE_DEST_RING_TO_DESC(dest_ring_base, sw_index);

        /* Return data from completed destination descriptor */
        *bufferp     = (CE_addr_t)(dest_desc->dest_ptr);

        if (per_CE_contextp) {
            *per_CE_contextp = CE_state->recv_context;
        }

        ce_debug_rvkrn_context = dest_ring->per_transfer_context[sw_index];
        if (per_transfer_contextp) {
            *per_transfer_contextp = ce_debug_rvkrn_context;
        }
        dest_ring->per_transfer_context[sw_index] = 0; /* sanity */

        /* Update sw_index */
        sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
        dest_ring->sw_index = sw_index;
        status = A_OK;
    } else {
        status = A_ERROR;
    }
    adf_os_spin_unlock(&sc->target_lock);

    return status;
}

/*
 * Guts of CE_completed_send_next.
 * The caller takes responsibility for any necessary locking.
 */
int
CE_completed_send_next_nolock(struct CE_state *CE_state,
                              void **per_CE_contextp,
                              void **per_transfer_contextp,
                              CE_addr_t *bufferp,
                              unsigned int *nbytesp,
                              unsigned int *transfer_idp,
                              unsigned int *sw_idx,
                              unsigned int *hw_idx)
{
    int status = A_ERROR;
    struct CE_ring_state *src_ring = CE_state->src_ring;
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);
    unsigned int nentries_mask = src_ring->nentries_mask;
    unsigned int sw_index = src_ring->sw_index;
    unsigned int read_index;


    if (src_ring->hw_index == sw_index) {
        /*
         * The SW completion index has caught up with the cached
         * version of the HW completion index.
         * Update the cached HW completion index to see whether
         * the SW has really caught up to the HW, or if the cached
         * value of the HW index has become stale.
         */
        A_TARGET_ACCESS_BEGIN_RET(targid);
        src_ring->hw_index = CE_SRC_RING_READ_IDX_GET(targid, ctrl_addr);
        A_TARGET_ACCESS_END_RET(targid);

        if (ce_idx_invalid(src_ring, src_ring->hw_index))
            return status;
    }
    read_index = src_ring->hw_index;

    if (sw_idx)
        *sw_idx = sw_index;

    if (hw_idx)
        *hw_idx = read_index;

    if ((read_index != sw_index) && (read_index != 0xffffffff)) {
        struct CE_src_desc *shadow_base = (struct CE_src_desc *)src_ring->shadow_base;
        struct CE_src_desc *shadow_src_desc = CE_SRC_RING_TO_DESC(shadow_base, sw_index);

        hif_record_ce_desc_event(CE_state->id, HIF_TX_DESC_COMPLETION,
                        (CE_desc *) shadow_src_desc,
                        src_ring->per_transfer_context[sw_index],
                        sw_index);

        /* Return data from completed source descriptor */
        *bufferp      = (CE_addr_t)(shadow_src_desc->src_ptr);
        *nbytesp      = shadow_src_desc->nbytes;
        *transfer_idp = shadow_src_desc->meta_data;

        if (per_CE_contextp) {
            *per_CE_contextp = CE_state->send_context;
        }

        ce_debug_cmplsn_context = src_ring->per_transfer_context[sw_index];
        if (per_transfer_contextp) {
            *per_transfer_contextp = ce_debug_cmplsn_context;
        }
        src_ring->per_transfer_context[sw_index] = 0; /* sanity */

        /* Update sw_index */
        sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
        src_ring->sw_index = sw_index;
        status = A_OK;
    }

    return status;
}

/* NB: Modeled after CE_completed_send_next */
A_STATUS
CE_cancel_send_next(struct CE_handle *copyeng,
                    void **per_CE_contextp,
                    void **per_transfer_contextp,
                    CE_addr_t *bufferp,
                    unsigned int *nbytesp,
                    unsigned int *transfer_idp)
{
    struct CE_state *CE_state;
    struct CE_ring_state *src_ring;
    unsigned int nentries_mask;
    unsigned int sw_index;
    unsigned int write_index;
    A_STATUS status;
    struct hif_pci_softc *sc;

    CE_state = (struct CE_state *)copyeng;
    src_ring = CE_state->src_ring;
    if (!src_ring) {
        return A_ERROR;
    }

    sc = CE_state->sc;
    adf_os_spin_lock(&sc->target_lock);
    nentries_mask = src_ring->nentries_mask;
    sw_index = src_ring->sw_index;
    write_index = src_ring->write_index;

    if (write_index != sw_index) {
        struct CE_src_desc *src_ring_base = (struct CE_src_desc *)src_ring->base_addr_owner_space;
        struct CE_src_desc *src_desc = CE_SRC_RING_TO_DESC(src_ring_base, sw_index);

        /* Return data from completed source descriptor */
        *bufferp      = (CE_addr_t)(src_desc->src_ptr);
        *nbytesp      = src_desc->nbytes;
        *transfer_idp = src_desc->meta_data;

        if (per_CE_contextp) {
            *per_CE_contextp = CE_state->send_context;
        }

        ce_debug_cnclsn_context = src_ring->per_transfer_context[sw_index];
        if (per_transfer_contextp) {
            *per_transfer_contextp = ce_debug_cnclsn_context;
        }
        src_ring->per_transfer_context[sw_index] = 0; /* sanity */

        /* Update sw_index */
        sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
        src_ring->sw_index = sw_index;
        status = A_OK;
    } else {
        status = A_ERROR;
    }
    adf_os_spin_unlock(&sc->target_lock);

    return status;
}

/* Shift bits to convert IS_*_RING_*_WATERMARK_MASK to CE_WM_FLAG_*_* */
#define CE_WM_SHFT 1


int
CE_completed_send_next(struct CE_handle *copyeng,
                       void **per_CE_contextp,
                       void **per_transfer_contextp,
                       CE_addr_t *bufferp,
                       unsigned int *nbytesp,
                       unsigned int *transfer_idp,
                       unsigned int *sw_idx,
                       unsigned int *hw_idx)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;
    int status;


    adf_os_spin_lock_bh(&sc->target_lock);
    status = CE_completed_send_next_nolock(CE_state, per_CE_contextp, per_transfer_contextp,
                                               bufferp, nbytesp, transfer_idp,
                                               sw_idx, hw_idx);
    adf_os_spin_unlock_bh(&sc->target_lock);

    return status;
}


#ifdef ATH_11AC_TXCOMPACT
/* CE engine descriptor reap
   Similar to CE_per_engine_service , Only difference is CE_per_engine_service
   does recieve and reaping of completed descriptor ,
   This function only handles reaping of Tx complete descriptor.
   The Function is called from threshold reap  poll routine HIFSendCompleteCheck
   So should not countain recieve functionality within it .
 */

void
CE_per_engine_servicereap(struct hif_pci_softc *sc, unsigned int CE_id)
{
    struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
    A_target_id_t targid = TARGID(sc);
    void *CE_context;
    void *transfer_context;
    CE_addr_t buf;
    unsigned int nbytes;
    unsigned int id;
    unsigned int sw_idx, hw_idx;

    A_TARGET_ACCESS_BEGIN(targid);

    /* Since this function is called from both user context and
     * tasklet context the spinlock has to lock the bottom halves.
     * This fix assumes that ATH_11AC_TXCOMPACT flag is always
     * enabled in TX polling mode. If this is not the case, more
     * bottom halve spin lock changes are needed. Due to data path
     * performance concern, after internal discussion we've decided
     * to make minimum change, i.e., only address the issue occurred
     * in this function. The possible negative effect of this minimum
     * change is that, in the future, if some other function will also
     * be opened to let the user context to use, those cases need to be
     * addressed by change spin_lock to spin_lock_bh also. */

    adf_os_spin_lock_bh(&sc->target_lock);

    if (CE_state->send_cb) {
       {
            /* Pop completed send buffers and call the registered send callback for each */
            while (CE_completed_send_next_nolock(CE_state, &CE_context, &transfer_context,
                        &buf, &nbytes, &id, &sw_idx, &hw_idx) == A_OK)
            {
                if(CE_id != CE_HTT_H2T_MSG){
                    adf_os_spin_unlock_bh(&sc->target_lock);
                    CE_state->send_cb((struct CE_handle *)CE_state, CE_context, transfer_context, buf, nbytes, id,
                                      sw_idx, hw_idx);
                    adf_os_spin_lock_bh(&sc->target_lock);
                }else{
                     struct HIF_CE_pipe_info *pipe_info = (struct HIF_CE_pipe_info *)CE_context;

                     adf_os_spin_lock_bh(&pipe_info->completion_freeq_lock);
                     pipe_info->num_sends_allowed++;
                     adf_os_spin_unlock_bh(&pipe_info->completion_freeq_lock);
                }
            }
        }
    }

    adf_os_spin_unlock_bh(&sc->target_lock);
    A_TARGET_ACCESS_END(targid);
}

#endif /*ATH_11AC_TXCOMPACT*/

/**
 * ce_is_valid_entries() - check if ce ring delta is valid
 * @sc: pointer to hif_pci_softc
 * @ce_state: pointer to CE_state
 * @ring_delta: delta packets in CE ring
 *
 * check if difference between hw index and read index is
 * with in limit.
 *
 * Return: true if ring delta is valid.
 */
static inline bool ce_is_valid_entries(struct hif_pci_softc *sc,
		struct CE_state *ce_state, unsigned int ring_delta)
{
	bool status;
	A_target_id_t targid = TARGID(sc);
	unsigned int hw_index =
			CE_DEST_RING_READ_IDX_GET(targid, ce_state->ctrl_addr);

	/* check if difference between hw index and read index is with in
	* nentries_mask limit.
	*/
	if ((ring_delta < ce_state->dest_ring->nentries_mask) &&
		(hw_index != CE_HW_INDEX_LINK_DOWN)) {
		adf_os_print("%s: spent more time during rx proceesing for CE%d, allow other CE to process Rx packet.\n",
				__func__, ce_state->id);
		status = true;
	} else if (hw_index  == CE_HW_INDEX_LINK_DOWN) {
		status = false;
		adf_os_print("%s: hw index is invalid due to link down \n", __func__);
	} else {
		adf_os_print("%s:Potential infinite loop detected during rx processing for CE%d\n",
			__func__, ce_state->id);
		ce_target_reset(sc);
		status = false;
	}

	adf_os_print("nentries_mask:0x%x sw read_idx:0x%x hw read_idx:0x%x ring_delta:0x%x\n",
		ce_state->dest_ring->nentries_mask,
		ce_state->dest_ring->sw_index,
		CE_DEST_RING_READ_IDX_GET(targid, ce_state->ctrl_addr),
		ring_delta);

	return status;
}

/*
 * Number of times to check for any pending tx/rx completion on
 * a copy engine, this count should be big enough. Once we hit
 * this threashold we'll not check for any Tx/Rx comlpetion in same
 * interrupt handling. Note that this threashold is only used for
 * Rx interrupt processing, this can be used tor Tx as well if we
 * suspect any infinite loop in checking for pending Tx completion.
 */
#define CE_TXRX_COMP_CHECK_THRESHOLD 20

/*
 * Guts of interrupt handler for per-engine interrupts on a particular CE.
 *
 * Invokes registered callbacks for recv_complete,
 * send_complete, and watermarks.
 */
void
CE_per_engine_service(struct hif_pci_softc *sc, unsigned int CE_id)
{
    struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    A_target_id_t targid = TARGID(sc);
    void *CE_context;
    void *transfer_context;
    CE_addr_t buf;
    unsigned int nbytes;
    unsigned int id;
    unsigned int flags;
    u_int32_t CE_int_status;
    unsigned int more_comp_cnt = 0;
    unsigned int more_snd_comp_cnt = 0;
    unsigned int sw_idx, hw_idx;
    bool is_pkt_pending = 0;
    unsigned int ring_delta = 0;


    A_TARGET_ACCESS_BEGIN(targid);

    adf_os_spin_lock(&sc->target_lock);

    /* Clear force_break flag and re-initialize receive_count to 0 */
    sc->receive_count = 0;
    sc->force_break = 0;
more_completions:
    if (CE_state->recv_cb) {

        /* Pop completed recv buffers and call the registered recv callback for each */
        while (CE_completed_recv_next_nolock(CE_state, &CE_context, &transfer_context,
                    &buf, &nbytes, &id, &flags) == A_OK)
        {
                adf_os_spin_unlock(&sc->target_lock);
                CE_state->recv_cb((struct CE_handle *)CE_state, CE_context, transfer_context,
                                    buf, nbytes, id, flags);

                /*
                 * EV #112693 - [Peregrine][ES1][WB342][Win8x86][Performance] BSoD_0x133 occurred in VHT80 UDP_DL
                 * Break out DPC by force if number of loops in HIF_PCI_CE_recv_data reaches MAX_NUM_OF_RECEIVES to avoid spending too long time in DPC for each interrupt handling.
                 * Schedule another DPC to avoid data loss if we had taken force-break action before
                 * Apply to Windows OS only currently, Linux/MAC os can expand to their platform if necessary
                 */

                /* Break the receive processes by force if force_break set up */
                if (adf_os_unlikely(sc->force_break))
                {
                    adf_os_atomic_set(&CE_state->rx_pending, 1);
                    CE_ENGINE_INT_STATUS_CLEAR(targid, ctrl_addr, HOST_IS_COPY_COMPLETE_MASK);
                    A_TARGET_ACCESS_END(targid);
                    return;
                }
                adf_os_spin_lock(&sc->target_lock);
        }
    }

    /*
     * Attention: We may experience potential infinite loop for below While Loop during Sending Stress test
     * Resolve the same way as Receive Case (Refer to EV #112693)
     */

    if (CE_state->send_cb) {
        /* Pop completed send buffers and call the registered send callback for each */

#ifdef ATH_11AC_TXCOMPACT
        while (CE_completed_send_next_nolock(CE_state, &CE_context, &transfer_context,
                    &buf, &nbytes, &id, &sw_idx, &hw_idx) == A_OK){

            if(CE_id != CE_HTT_H2T_MSG ||
                    WLAN_IS_EPPING_ENABLED(vos_get_conparam())){
                adf_os_spin_unlock(&sc->target_lock);
                CE_state->send_cb((struct CE_handle *)CE_state, CE_context, transfer_context, buf, nbytes, id,
                                  sw_idx, hw_idx);
                adf_os_spin_lock(&sc->target_lock);
            }else{
                struct HIF_CE_pipe_info *pipe_info = (struct HIF_CE_pipe_info *)CE_context;

                adf_os_spin_lock(&pipe_info->completion_freeq_lock);
                pipe_info->num_sends_allowed++;
                adf_os_spin_unlock(&pipe_info->completion_freeq_lock);
            }
        }
#else  /*ATH_11AC_TXCOMPACT*/
        while (CE_completed_send_next_nolock(CE_state, &CE_context, &transfer_context,
                    &buf, &nbytes, &id, &sw_idx, &hw_idx) == A_OK){
            adf_os_spin_unlock(&sc->target_lock);
            CE_state->send_cb((struct CE_handle *)CE_state, CE_context, transfer_context, buf, nbytes, id,
                              sw_idx, hw_idx);
            adf_os_spin_lock(&sc->target_lock);
        }
#endif /*ATH_11AC_TXCOMPACT*/
    }

more_watermarks:
    if (CE_state->misc_cbs) {
        CE_int_status = CE_ENGINE_INT_STATUS_GET(targid, ctrl_addr);
        if (CE_int_status & CE_WATERMARK_MASK) {
            if (CE_state->watermark_cb) {

                adf_os_spin_unlock(&sc->target_lock);
                /* Convert HW IS bits to software flags */
                flags = (CE_int_status & CE_WATERMARK_MASK) >> CE_WM_SHFT;

                CE_state->watermark_cb((struct CE_handle *)CE_state, CE_state->wm_context, flags);
                adf_os_spin_lock(&sc->target_lock);
            }
        }
    }

    /*
     * Clear the misc interrupts (watermark) that were handled above,
     * and that will be checked again below.
     * Clear and check for copy-complete interrupts again, just in case
     * more copy completions happened while the misc interrupts were being
     * handled.
     */
    CE_ENGINE_INT_STATUS_CLEAR(targid, ctrl_addr, CE_WATERMARK_MASK | HOST_IS_COPY_COMPLETE_MASK);

    /*
     * Now that per-engine interrupts are cleared, verify that
     * no recv interrupts arrive while processing send interrupts,
     * and no recv or send interrupts happened while processing
     * misc interrupts.Go back and check again.Keep checking until
     * we find no more events to process.
     */
    if (CE_state->recv_cb) {
        ring_delta = CE_recv_entries_done_nolock(sc, CE_state);
        if(ring_delta) {
            if (WLAN_IS_EPPING_ENABLED(vos_get_conparam()) ||
                more_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) {
                goto more_completions;
            } else {
                if (ce_is_valid_entries(sc, CE_state, ring_delta))
                    is_pkt_pending = true;
            }
        }
    }

    if (CE_state->send_cb && CE_send_entries_done_nolock(sc, CE_state)) {
        if (WLAN_IS_EPPING_ENABLED(vos_get_conparam()) ||
            more_snd_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) {
            goto more_completions;
        } else {
            adf_os_print("%s:Potential infinite loop detected during send completion "
                         "nentries_mask:0x%x sw read_idx:0x%x hw read_idx:0x%x\n",
                         __func__, CE_state->src_ring->nentries_mask,
                         CE_state->src_ring->sw_index,
                         CE_SRC_RING_READ_IDX_GET(targid, CE_state->ctrl_addr));
        }
    }


    if (CE_state->misc_cbs) {
        CE_int_status = CE_ENGINE_INT_STATUS_GET(targid, ctrl_addr);
        if (CE_int_status & CE_WATERMARK_MASK) {
            if (CE_state->watermark_cb) {
                goto more_watermarks;
            }
        }
    }

    adf_os_spin_unlock(&sc->target_lock);
    if (is_pkt_pending)
        adf_os_atomic_set(&CE_state->rx_pending, 1);
    else
        adf_os_atomic_set(&CE_state->rx_pending, 0);
    A_TARGET_ACCESS_END(targid);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
static void
CE_poll_timeout(struct timer_list *t)
{
   struct CE_state *CE_state = from_timer(CE_state, t, poll_timer);
#else
static void
CE_poll_timeout(void *arg)
{
    struct CE_state *CE_state = (struct CE_state *) arg;
#endif
    if (CE_state->timer_inited) {
        CE_per_engine_service(CE_state->sc, CE_state->id);
        adf_os_timer_mod(&CE_state->poll_timer, CE_POLL_TIMEOUT);
    }
}


/*
 * Handler for per-engine interrupts on ALL active CEs.
 * This is used in cases where the system is sharing a
 * single interrput for all CEs
 */

void
CE_per_engine_service_any(int irq, void *arg)
{
    struct hif_pci_softc *sc = arg;
    A_target_id_t targid = TARGID(sc);
    int CE_id;
    A_UINT32 intr_summary;

    A_TARGET_ACCESS_BEGIN(targid);
    if (!adf_os_atomic_read(&sc->tasklet_from_intr)) {
        for (CE_id=0; CE_id < sc->ce_count; CE_id++) {
             struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
             if (adf_os_atomic_read(&CE_state->rx_pending)) {
                 adf_os_atomic_set(&CE_state->rx_pending, 0);
                 CE_per_engine_service(sc, CE_id);
             }
        }

        A_TARGET_ACCESS_END(targid);
        return;
    }

    intr_summary = CE_INTERRUPT_SUMMARY(targid);

    for (CE_id=0; intr_summary && (CE_id < sc->ce_count); CE_id++) {
        if (intr_summary & (1<<CE_id)) {
            intr_summary &= ~(1<<CE_id);
        } else {
            continue; /* no intr pending on this CE */
        }

        CE_per_engine_service(sc, CE_id);
    }

    A_TARGET_ACCESS_END(targid);
}

/*
 * Adjust interrupts for the copy complete handler.
 * If it's needed for either send or recv, then unmask
 * this interrupt; otherwise, mask it.
 *
 * Called with target_lock held.
 */
static void
CE_per_engine_handler_adjust(struct CE_state *CE_state,
                             int disable_copy_compl_intr)
{
    u_int32_t ctrl_addr = CE_state->ctrl_addr;
    struct hif_pci_softc *sc = CE_state->sc;
    A_target_id_t targid = TARGID(sc);
    CE_state->disable_copy_compl_intr = disable_copy_compl_intr;

    A_TARGET_ACCESS_BEGIN(targid);
    if ((!disable_copy_compl_intr) &&
        (CE_state->send_cb || CE_state->recv_cb))
    {
        CE_COPY_COMPLETE_INTR_ENABLE(targid, ctrl_addr);
    } else {
        CE_COPY_COMPLETE_INTR_DISABLE(targid, ctrl_addr);
    }

    if (CE_state->watermark_cb) {
        CE_WATERMARK_INTR_ENABLE(targid, ctrl_addr);
    } else {
        CE_WATERMARK_INTR_DISABLE(targid, ctrl_addr);
    }
    A_TARGET_ACCESS_END(targid);

}

/*Iterate the CE_state list and disable the compl interrupt if it has been registered already.*/
void CE_disable_any_copy_compl_intr_nolock(struct hif_pci_softc *sc)
{
    A_target_id_t targid = TARGID(sc);
    int CE_id;

    A_TARGET_ACCESS_BEGIN(targid);
    for (CE_id=0; CE_id < sc->ce_count; CE_id++) {
        struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
        u_int32_t ctrl_addr = CE_state->ctrl_addr;

        /* if the interrupt is currently enabled, disable it */
        if (!CE_state->disable_copy_compl_intr && (CE_state->send_cb || CE_state->recv_cb)) {
            CE_COPY_COMPLETE_INTR_DISABLE(targid, ctrl_addr);
        }

        if (CE_state->watermark_cb) {
            CE_WATERMARK_INTR_DISABLE(targid, ctrl_addr);
        }
    }
    A_TARGET_ACCESS_END(targid);
}

void CE_enable_any_copy_compl_intr_nolock(struct hif_pci_softc *sc)
{
    A_target_id_t targid = TARGID(sc);
    int CE_id;

    A_TARGET_ACCESS_BEGIN(targid);
    for (CE_id=0; CE_id < sc->ce_count; CE_id++) {
        struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
        u_int32_t ctrl_addr = CE_state->ctrl_addr;

        /*
        * If the CE is supposed to have copy complete interrupts enabled
        * (i.e. there a callback registered, and the "disable" flag is not set),
        * then re-enable the interrupt.
        */
        if (!CE_state->disable_copy_compl_intr && (CE_state->send_cb || CE_state->recv_cb)) {
            CE_COPY_COMPLETE_INTR_ENABLE(targid, ctrl_addr);
        }

        if (CE_state->watermark_cb) {
            CE_WATERMARK_INTR_ENABLE(targid, ctrl_addr);
        }
    }
    A_TARGET_ACCESS_END(targid);
}

void
CE_disable_any_copy_compl_intr(struct hif_pci_softc *sc)
{
	adf_os_spin_lock(&sc->target_lock);
	CE_disable_any_copy_compl_intr_nolock(sc);
	adf_os_spin_unlock(&sc->target_lock);
}

/*Re-enable the copy compl interrupt if it has not been disabled before.*/
void
CE_enable_any_copy_compl_intr(struct hif_pci_softc *sc)
{
	adf_os_spin_lock(&sc->target_lock);
	CE_enable_any_copy_compl_intr_nolock(sc);
	adf_os_spin_unlock(&sc->target_lock);
}

void
CE_send_cb_register(struct CE_handle *copyeng,
                    CE_send_cb fn_ptr,
                    void *CE_send_context,
                    int disable_interrupts)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;

    adf_os_spin_lock(&sc->target_lock);
    CE_state->send_cb = fn_ptr;
    CE_state->send_context = CE_send_context;
    CE_per_engine_handler_adjust(CE_state, disable_interrupts);
    adf_os_spin_unlock(&sc->target_lock);
}

void
CE_recv_cb_register(struct CE_handle *copyeng,
                    CE_recv_cb fn_ptr,
                    void *CE_recv_context,
                    int disable_interrupts)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;

    adf_os_spin_lock(&sc->target_lock);
    CE_state->recv_cb = fn_ptr;
    CE_state->recv_context = CE_recv_context;
    CE_per_engine_handler_adjust(CE_state, disable_interrupts);
    adf_os_spin_unlock(&sc->target_lock);
}

void
CE_watermark_cb_register(struct CE_handle *copyeng,
                         CE_watermark_cb fn_ptr,
                         void *CE_wm_context)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    struct hif_pci_softc *sc = CE_state->sc;

    adf_os_spin_lock(&sc->target_lock);
    CE_state->watermark_cb = fn_ptr;
    CE_state->wm_context = CE_wm_context;
    CE_per_engine_handler_adjust(CE_state, 0);
    if (fn_ptr) {
        CE_state->misc_cbs = 1;
    }
    adf_os_spin_unlock(&sc->target_lock);
}

static unsigned int
roundup_pwr2(unsigned int n)
{
    int i;
    unsigned int test_pwr2;

    if (!(n & (n-1))) {
        return n; /* already a power of 2 */
    }

    test_pwr2 = 4;
    for (i=0; i<29; i++) {
        if (test_pwr2 > n) {
            return test_pwr2;
        }
        test_pwr2 = test_pwr2 << 1;
    }

    A_ASSERT(0); /* n too large */
    return 0;
}

bool CE_get_rx_pending(struct hif_pci_softc *sc)
{
    int CE_id;

    for (CE_id=0; CE_id < sc->ce_count; CE_id++) {
         struct CE_state *CE_state = sc->CE_id_to_state[CE_id];
         if (adf_os_atomic_read(&CE_state->rx_pending))
             return true;
    }

    return false;
}
/*
 * Initialize a Copy Engine based on caller-supplied attributes.
 * This may be called once to initialize both source and destination
 * rings or it may be called twice for separate source and destination
 * initialization. It may be that only one side or the other is
 * initialized by software/firmware.
 */
struct CE_handle *
CE_init(struct hif_pci_softc *sc,
        unsigned int CE_id,
        struct CE_attr *attr)
{
    struct CE_state *CE_state;
    u_int32_t ctrl_addr;
    A_target_id_t targid;
    unsigned int nentries;
    adf_os_dma_addr_t base_addr;
    struct ol_softc *scn = sc->ol_sc;
    bool malloc_CE_state = false;
    bool malloc_src_ring = false;

    A_ASSERT(CE_id < sc->ce_count);
    ctrl_addr = CE_BASE_ADDRESS(CE_id);
    adf_os_spin_lock(&sc->target_lock);
    CE_state = sc->CE_id_to_state[CE_id];


    if (!CE_state) {
        adf_os_spin_unlock(&sc->target_lock);
        CE_state = (struct CE_state *)A_MALLOC(sizeof(*CE_state));
        if (!CE_state) {
            dev_err(&sc->pdev->dev, "ath ERROR: CE_state has no mem\n");
            return NULL;
        } else
            malloc_CE_state = true;
        A_MEMZERO(CE_state, sizeof(*CE_state));
        adf_os_spin_lock(&sc->target_lock);
        if (!sc->CE_id_to_state[CE_id]) { /* re-check under lock */
            sc->CE_id_to_state[CE_id] = CE_state;

            CE_state->sc = sc;
            CE_state->id = CE_id;
            CE_state->ctrl_addr = ctrl_addr;
            CE_state->state = CE_RUNNING;
            CE_state->attr_flags = attr->flags; /* Save attribute flags */
        } else {
            /*
             * We released target_lock in order to allocate CE state,
             * but someone else beat us to it.  Continue, using that
             * CE_state (and free the one we allocated).
             */
            A_FREE(CE_state);
            malloc_CE_state = false;
            CE_state = sc->CE_id_to_state[CE_id];
        }
    }
    adf_os_spin_unlock(&sc->target_lock);

    adf_os_atomic_init(&CE_state->rx_pending);
    if (attr == NULL) {
        /* Already initialized; caller wants the handle */
        return (struct CE_handle *)CE_state;
    }

    targid = TARGID(sc);

    if (CE_state->src_sz_max) {
        A_ASSERT(CE_state->src_sz_max == attr->src_sz_max);
    } else {
        CE_state->src_sz_max = attr->src_sz_max;
    }

    CE_init_ce_desc_event_log(CE_id,
               attr->src_nentries + attr->dest_nentries);

    /* source ring setup */
    nentries = attr->src_nentries;
    if (nentries) {
        struct CE_ring_state *src_ring;
        unsigned CE_nbytes;
        char *ptr;

        nentries = roundup_pwr2(nentries);
        if (CE_state->src_ring) {
            A_ASSERT(CE_state->src_ring->nentries == nentries);
        } else {
            CE_nbytes = sizeof(struct CE_ring_state)
                      + (nentries * sizeof(void *)); /* per-send context */
            ptr = A_MALLOC(CE_nbytes);
            if (!ptr) {
                /* cannot allocate src ring. If the CE_state is allocated
                 * locally free CE_State and return error. */
                dev_err(&sc->pdev->dev, "ath ERROR: src ring has no mem\n");
                if (malloc_CE_state) {
                    /* allocated CE_state locally */
                    adf_os_spin_lock(&sc->target_lock);
                    sc->CE_id_to_state[CE_id]= NULL;
                    adf_os_spin_unlock(&sc->target_lock);
                    A_FREE(CE_state);
                    malloc_CE_state = false;
                }
                return NULL;
            } else {
                /* we can allocate src ring.
                 * Mark that the src ring is allocated locally */
                malloc_src_ring = true;
            }
            A_MEMZERO(ptr, CE_nbytes);

            src_ring = CE_state->src_ring = (struct CE_ring_state *)ptr;
            ptr += sizeof(struct CE_ring_state);
            src_ring->nentries = nentries;
            src_ring->nentries_mask = nentries-1;
            A_TARGET_ACCESS_BEGIN_RET_PTR(targid);
            src_ring->hw_index =
                src_ring->sw_index = CE_SRC_RING_READ_IDX_GET(targid, ctrl_addr);
            src_ring->write_index = CE_SRC_RING_WRITE_IDX_GET(targid, ctrl_addr);
            A_TARGET_ACCESS_END_RET_PTR(targid);
            src_ring->low_water_mark_nentries = 0;
            src_ring->high_water_mark_nentries = nentries;
            src_ring->per_transfer_context = (void **)ptr;

            /* Legacy platforms that do not support cache coherent DMA are unsupported */
            src_ring->base_addr_owner_space_unaligned =
                pci_alloc_consistent(scn->sc_osdev->bdev,
                                    (nentries * sizeof(struct CE_src_desc) + CE_DESC_RING_ALIGN),
                                    &base_addr);
            if (src_ring->base_addr_owner_space_unaligned == NULL) {
                dev_err(&sc->pdev->dev, "ath ERROR: src ring has no DMA mem\n");
                goto error_no_dma_mem;
            }
            src_ring->base_addr_CE_space_unaligned = base_addr;


            if (src_ring->base_addr_CE_space_unaligned & (CE_DESC_RING_ALIGN-1)) {

                src_ring->base_addr_CE_space = (src_ring->base_addr_CE_space_unaligned +
                        CE_DESC_RING_ALIGN-1) & ~(CE_DESC_RING_ALIGN-1);

                src_ring->base_addr_owner_space = (void *)(((size_t)src_ring->base_addr_owner_space_unaligned +
                        CE_DESC_RING_ALIGN-1) & ~(CE_DESC_RING_ALIGN-1));
            } else {
                src_ring->base_addr_CE_space = src_ring->base_addr_CE_space_unaligned;
                src_ring->base_addr_owner_space = src_ring->base_addr_owner_space_unaligned;
            }
            /*
             * Also allocate a shadow src ring in regular mem to use for
             * faster access.
             */
            src_ring->shadow_base_unaligned = A_MALLOC(
                nentries * sizeof(struct CE_src_desc) + CE_DESC_RING_ALIGN);
            if (src_ring->shadow_base_unaligned == NULL) {
                dev_err(&sc->pdev->dev, "ath ERROR: src ring has no shadow_base mem\n");
                goto error_no_dma_mem;
            }
            src_ring->shadow_base = (struct CE_src_desc *)
                (((size_t) src_ring->shadow_base_unaligned +
                CE_DESC_RING_ALIGN-1) & ~(CE_DESC_RING_ALIGN-1));

            A_TARGET_ACCESS_BEGIN_RET_PTR(targid);
            CE_SRC_RING_BASE_ADDR_SET(targid, ctrl_addr, src_ring->base_addr_CE_space);
            CE_SRC_RING_SZ_SET(targid, ctrl_addr, nentries);
            CE_SRC_RING_DMAX_SET(targid, ctrl_addr, attr->src_sz_max);
#ifdef BIG_ENDIAN_HOST
            /* Enable source ring byte swap for big endian host */
            CE_SRC_RING_BYTE_SWAP_SET(targid, ctrl_addr, 1);
#endif
            CE_SRC_RING_LOWMARK_SET(targid, ctrl_addr, 0);
            CE_SRC_RING_HIGHMARK_SET(targid, ctrl_addr, nentries);
            A_TARGET_ACCESS_END_RET_PTR(targid);
        }
    }

    /* destination ring setup */
    nentries = attr->dest_nentries;
    if (nentries) {
        struct CE_ring_state *dest_ring;
        unsigned CE_nbytes;
        char *ptr;

        nentries = roundup_pwr2(nentries);
        if (CE_state->dest_ring) {
            A_ASSERT(CE_state->dest_ring->nentries == nentries);
        } else {
            CE_nbytes = sizeof(struct CE_ring_state)
                      + (nentries * sizeof(void *)); /* per-recv context */
            ptr = A_MALLOC(CE_nbytes);
            if (!ptr) {
                /* cannot allocate dst ring. If the CE_state or src ring is allocated
                 * locally free CE_State and src ring and return error. */
                dev_err(&sc->pdev->dev, "ath ERROR: dest ring has no mem\n");
                if (malloc_src_ring) {
                    A_FREE(CE_state->src_ring);
                    CE_state->src_ring= NULL;
                    malloc_src_ring = false;
                }
                if (malloc_CE_state) {
                    /* allocated CE_state locally */
                    adf_os_spin_lock(&sc->target_lock);
                    sc->CE_id_to_state[CE_id]= NULL;
                    adf_os_spin_unlock(&sc->target_lock);
                    A_FREE(CE_state);
                    malloc_CE_state = false;
                }
                return NULL;
            }
            A_MEMZERO(ptr, CE_nbytes);

            dest_ring = CE_state->dest_ring = (struct CE_ring_state *)ptr;
            ptr += sizeof(struct CE_ring_state);
            dest_ring->nentries = nentries;
            dest_ring->nentries_mask = nentries-1;
            A_TARGET_ACCESS_BEGIN_RET_PTR(targid);
            dest_ring->sw_index = CE_DEST_RING_READ_IDX_GET(targid, ctrl_addr);
            dest_ring->write_index = CE_DEST_RING_WRITE_IDX_GET(targid, ctrl_addr);
            A_TARGET_ACCESS_END_RET_PTR(targid);
            dest_ring->low_water_mark_nentries = 0;
            dest_ring->high_water_mark_nentries = nentries;
            dest_ring->per_transfer_context = (void **)ptr;

            /* Legacy platforms that do not support cache coherent DMA are unsupported */
            dest_ring->base_addr_owner_space_unaligned =
                pci_alloc_consistent(scn->sc_osdev->bdev,
                                    (nentries * sizeof(struct CE_dest_desc) + CE_DESC_RING_ALIGN),
                                    &base_addr);
            if (dest_ring->base_addr_owner_space_unaligned == NULL) {
                dev_err(&sc->pdev->dev, "ath ERROR: dest ring has no DMA mem\n");
                goto error_no_dma_mem;
            }
            dest_ring->base_addr_CE_space_unaligned = base_addr;

            /* Correctly initialize memory to 0 to prevent garbage data
             * crashing system when download firmware
             */
            A_MEMZERO(dest_ring->base_addr_owner_space_unaligned, nentries * sizeof(struct CE_dest_desc) + CE_DESC_RING_ALIGN);

            if (dest_ring->base_addr_CE_space_unaligned & (CE_DESC_RING_ALIGN-1)) {

                dest_ring->base_addr_CE_space = (dest_ring->base_addr_CE_space_unaligned +
                        CE_DESC_RING_ALIGN-1) & ~(CE_DESC_RING_ALIGN-1);

                dest_ring->base_addr_owner_space = (void *)(((size_t)dest_ring->base_addr_owner_space_unaligned +
                        CE_DESC_RING_ALIGN-1) & ~(CE_DESC_RING_ALIGN-1));
            } else {
                dest_ring->base_addr_CE_space = dest_ring->base_addr_CE_space_unaligned;
                dest_ring->base_addr_owner_space = dest_ring->base_addr_owner_space_unaligned;
            }

            A_TARGET_ACCESS_BEGIN_RET_PTR(targid);
            CE_DEST_RING_BASE_ADDR_SET(targid, ctrl_addr, dest_ring->base_addr_CE_space);
            CE_DEST_RING_SZ_SET(targid, ctrl_addr, nentries);
#ifdef BIG_ENDIAN_HOST
            /* Enable Destination ring byte swap for big endian host */
            CE_DEST_RING_BYTE_SWAP_SET(targid, ctrl_addr, 1);
#endif
            CE_DEST_RING_LOWMARK_SET(targid, ctrl_addr, 0);
            CE_DEST_RING_HIGHMARK_SET(targid, ctrl_addr, nentries);
            A_TARGET_ACCESS_END_RET_PTR(targid);

            /* epping */
            /* poll timer */
            if ((CE_state->attr_flags & CE_ATTR_ENABLE_POLL)) {
                adf_os_timer_init(scn->adf_dev, &CE_state->poll_timer,
                        CE_poll_timeout, CE_state, ADF_DEFERRABLE_TIMER);
                CE_state->timer_inited = true;
                adf_os_timer_mod(&CE_state->poll_timer, CE_POLL_TIMEOUT);
            }
        }
    }

    /* Enable CE error interrupts */
    A_TARGET_ACCESS_BEGIN_RET_PTR(targid);
    CE_ERROR_INTR_ENABLE(targid, ctrl_addr);
    A_TARGET_ACCESS_END_RET_PTR(targid);

    return (struct CE_handle *)CE_state;

error_no_dma_mem:
    CE_fini((struct CE_handle *)CE_state);
    return NULL;
}

void
CE_fini(struct CE_handle *copyeng)
{
    struct CE_state *CE_state = (struct CE_state *)copyeng;
    unsigned int CE_id = CE_state->id;
    struct hif_pci_softc *sc = CE_state->sc;
    struct ol_softc *scn = sc->ol_sc;

    CE_state->state = CE_UNUSED;
    CE_state->sc->CE_id_to_state[CE_id] = NULL;
    if (CE_state->src_ring) {
        if (CE_state->src_ring->shadow_base_unaligned)
            A_FREE(CE_state->src_ring->shadow_base_unaligned);
        if (CE_state->src_ring->base_addr_owner_space_unaligned)
            pci_free_consistent(scn->sc_osdev->bdev,
                   (CE_state->src_ring->nentries * sizeof(struct CE_src_desc) + CE_DESC_RING_ALIGN),
                   CE_state->src_ring->base_addr_owner_space_unaligned, CE_state->src_ring->base_addr_CE_space);
        A_FREE(CE_state->src_ring);
    }
    if (CE_state->dest_ring) {
        if (CE_state->dest_ring->base_addr_owner_space_unaligned)
            pci_free_consistent(scn->sc_osdev->bdev,
                   (CE_state->dest_ring->nentries * sizeof(struct CE_dest_desc) + CE_DESC_RING_ALIGN),
                   CE_state->dest_ring->base_addr_owner_space_unaligned, CE_state->dest_ring->base_addr_CE_space);
        A_FREE(CE_state->dest_ring);

        /* epping */
        if (CE_state->timer_inited) {
            CE_state->timer_inited = false;
            adf_os_timer_free(&CE_state->poll_timer);
        }
    }
    A_FREE(CE_state);
}

#ifdef IPA_UC_OFFLOAD
/*
 * Copy engine should release resource to micro controller
 * Micro controller needs
   - Copy engine source descriptor base address
   - Copy engine source descriptor size
   - PCI BAR address to access copy engine regiser
 */
void CE_ipaGetResource(struct CE_handle *ce,
            a_uint32_t *ce_sr_base_paddr,
            a_uint32_t *ce_sr_ring_size,
            a_uint32_t *ce_reg_paddr)
{
    struct CE_state *CE_state = (struct CE_state *)ce;
    a_uint32_t ring_loop;
    struct CE_src_desc *ce_desc;
    a_uint32_t bar_value;
    struct hif_pci_softc *sc = CE_state->sc;

    if (CE_RUNNING != CE_state->state)
    {
        *ce_sr_base_paddr = 0;
        *ce_sr_ring_size = 0;
        return;
    }

    /* Update default value for descriptor */
    for (ring_loop = 0; ring_loop < CE_state->src_ring->nentries; ring_loop++)
    {
        ce_desc = (struct CE_src_desc *)
                  ((char *)CE_state->src_ring->base_addr_owner_space +
                   ring_loop * (sizeof(struct CE_src_desc)));
        /* Source pointer and ID,
         * should be updated by uc dynamically
         * ce_desc->src_ptr   = buffer;
         * ce_desc->meta_data = transfer_id; */
        /* No Byte SWAP */
        ce_desc->byte_swap = 0;
        /* DL size
         * pdev->download_len =
         *   sizeof(struct htt_host_tx_desc_t) +
         *   HTT_TX_HDR_SIZE_OUTER_HDR_MAX +
         *   HTT_TX_HDR_SIZE_802_1Q +
         *   HTT_TX_HDR_SIZE_LLC_SNAP +
         *   ol_cfg_tx_download_size(pdev->ctrl_pdev); */
        ce_desc->nbytes = 60;
        /* Single fragment No gather */
        ce_desc->gather = 0;
    }

    /* Get BAR address */
    hif_read_bar(CE_state->sc, &bar_value);

    *ce_sr_base_paddr = (a_uint32_t)CE_state->src_ring->base_addr_CE_space;
    *ce_sr_ring_size = (a_uint32_t)CE_state->src_ring->nentries;
    *ce_reg_paddr = bar_value + CE_BASE_ADDRESS(CE_state->id) + SR_WR_INDEX_ADDRESS;
    return;
}
#endif /* IPA_UC_OFFLOAD */
